1mod args;
55mod arrays;
56mod backup;
57mod bits;
58mod blocking;
59mod bloom;
60mod cms;
61mod cpu;
62mod cuckoo;
63mod geo;
64mod graph;
65mod hashes;
66mod himport;
67mod hll;
68mod json;
69mod keyspace;
70mod lists;
71mod migrate;
72mod scan;
73mod scripting;
74mod server;
75mod sets;
76mod streams;
77mod strings;
78pub mod table;
79mod tdigest;
80mod topk;
81mod vectors;
82mod vfilter;
83mod zsets;
84
85pub use args::Args;
86pub use blocking::{Parked, Waiters};
87pub use server::parse_memory;
88pub use table::{COMMANDS, Spec, arity_ok, lookup};
89
90use crate::reply::Out;
91use std::path::{Path, PathBuf};
92use yo_common::{Code, Error};
93use yo_kv::cold::Blocks;
94use yo_kv::{Clock, Keyspace};
95
96pub const DATABASES: usize = 16;
103
104const ALL_DATABASES: u64 = if DATABASES == 64 {
111 u64::MAX
112} else {
113 (1u64 << DATABASES) - 1
114};
115const _: () = assert!(DATABASES <= 64);
116
117const EVICT_BUDGET: usize = 64;
129
130const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum Flow {
139 Continue,
141 Close,
143 Block,
150}
151
152#[derive(Debug, Clone, Copy, Default)]
158pub struct Stats {
159 pub clients: u64,
161 pub connections: u64,
163 pub commands: u64,
165}
166
167fn working_dir() -> PathBuf {
174 std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
175}
176
177#[derive(Debug, Clone, Copy, Default)]
186pub struct CommandStat {
187 pub calls: u64,
189 pub rejected: u64,
192 pub failed: u64,
194}
195
196impl CommandStat {
197 const fn seen(&self) -> bool {
203 self.calls != 0 || self.rejected != 0 || self.failed != 0
204 }
205}
206
207struct CommandStats(Box<[CommandStat]>);
214
215impl Default for CommandStats {
216 fn default() -> CommandStats {
217 CommandStats(vec![CommandStat::default(); table::count()].into_boxed_slice())
218 }
219}
220
221impl CommandStats {
222 fn at(&mut self, spec: &'static Spec) -> &mut CommandStat {
224 &mut self.0[table::index_of(spec)]
225 }
226}
227
228pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
234
235pub struct Server {
242 dbs: Vec<Keyspace>,
243 clock: Clock,
244 started_ms: u64,
245 next_db: usize,
248 dirty: u64,
258 conn_bytes: usize,
260 maxmemory: u64,
265 store: Option<Box<StoreSource>>,
276 maxstore: Option<u64>,
292 used: usize,
304 evict_db: usize,
310 expire_db: usize,
317 expire_ms: u64,
320 waiters: Waiters,
322 peers: migrate::Peers,
327 pub stats: Stats,
329 cmdstats: CommandStats,
331 dir: PathBuf,
339 backup: backup::State,
344 stopping: bool,
353}
354
355impl Server {
356 #[must_use]
358 pub fn new() -> Server {
359 let clock = Clock::system();
360 Server {
361 dbs: (0..DATABASES)
362 .map(|_| Keyspace::with_clock(clock))
363 .collect(),
364 clock,
365 started_ms: clock.now_ms(),
366 next_db: 0,
367 dirty: ALL_DATABASES,
368 conn_bytes: 0,
369 maxmemory: 0,
370 store: None,
371 maxstore: None,
372 used: 0,
373 evict_db: 0,
374 expire_db: 0,
375 expire_ms: 0,
376 waiters: Waiters::default(),
377 peers: migrate::Peers::default(),
378 stats: Stats::default(),
379 cmdstats: CommandStats::default(),
380 dir: working_dir(),
381 backup: backup::State::default(),
382 stopping: false,
383 }
384 }
385
386 #[must_use]
388 pub fn with_clock(clock: Clock) -> Server {
389 Server {
390 dbs: (0..DATABASES)
391 .map(|_| Keyspace::with_clock(clock))
392 .collect(),
393 clock,
394 started_ms: clock.now_ms(),
395 next_db: 0,
396 dirty: ALL_DATABASES,
397 conn_bytes: 0,
398 maxmemory: 0,
399 store: None,
400 maxstore: None,
401 used: 0,
402 evict_db: 0,
403 expire_db: 0,
404 expire_ms: 0,
405 waiters: Waiters::default(),
406 peers: migrate::Peers::default(),
407 stats: Stats::default(),
408 cmdstats: CommandStats::default(),
409 dir: working_dir(),
410 backup: backup::State::default(),
411 stopping: false,
412 }
413 }
414
415 pub fn db(&mut self, i: usize) -> &mut Keyspace {
423 self.dirty |= 1u64 << i;
426 &mut self.dbs[i]
427 }
428
429 #[must_use]
431 pub fn dir(&self) -> &Path {
432 &self.dir
433 }
434
435 pub fn set_dir(&mut self, dir: PathBuf) {
442 self.dir = dir;
443 }
444
445 pub fn backup_expire(&mut self) {
451 backup::expire(self);
452 }
453
454 pub fn stop(&mut self) {
460 self.stopping = true;
461 }
462
463 #[must_use]
469 pub fn stopping(&self) -> bool {
470 self.stopping
471 }
472
473 #[must_use]
484 pub fn db_ref(&self, i: usize) -> &Keyspace {
485 &self.dbs[i]
486 }
487
488 pub fn refresh_clock(&mut self) {
494 self.clock.refresh();
495 let now = self.clock.now_ms();
496 for db in &mut self.dbs {
497 db.clock_mut().set(now);
498 }
499 }
500
501 pub fn set_clock_ms(&mut self, ms: u64) {
509 self.clock.set(ms);
510 for db in &mut self.dbs {
511 db.clock_mut().set(ms);
512 }
513 }
514
515 #[must_use]
517 pub fn uptime_secs(&self) -> u64 {
518 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
519 }
520
521 #[must_use]
529 pub fn memory_bytes(&self) -> usize {
530 self.dbs.iter().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
531 }
532
533 #[must_use]
539 pub fn dataset_bytes(&self) -> usize {
540 self.dbs
541 .iter()
542 .map(|db| db.map().arena().live_bytes() as usize)
543 .sum()
544 }
545
546 #[must_use]
548 pub fn arena_bytes(&self) -> usize {
549 self.dbs
550 .iter()
551 .map(|db| db.map().arena().reserved_bytes() as usize)
552 .sum()
553 }
554
555 #[must_use]
557 pub fn index_bytes(&self) -> usize {
558 self.dbs
559 .iter()
560 .map(|db| db.map().index().memory_bytes())
561 .sum()
562 }
563
564 #[must_use]
571 pub fn compaction(&self) -> yo_kv::Compaction {
572 self.dbs.iter().map(|db| db.map().compaction()).fold(
573 yo_kv::Compaction::default(),
574 |a, b| yo_kv::Compaction {
575 walked: a.walked + b.walked,
576 moved: a.moved + b.moved,
577 bytes: a.bytes + b.bytes,
578 },
579 )
580 }
581
582 #[must_use]
584 pub fn segment_count(&self) -> usize {
585 self.dbs
586 .iter()
587 .map(|db| db.map().arena().resident_segments())
588 .sum()
589 }
590
591 #[must_use]
593 pub const fn conn_bytes(&self) -> usize {
594 self.conn_bytes
595 }
596
597 pub fn note_conn_bytes(&mut self, delta: isize) {
605 self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
606 }
607
608 #[must_use]
610 pub fn expired_keys(&self) -> u64 {
611 self.dbs.iter().map(Keyspace::expired_keys).sum()
612 }
613
614 #[must_use]
616 pub fn evicted_keys(&self) -> u64 {
617 self.dbs.iter().map(Keyspace::evicted_keys).sum()
618 }
619
620 pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
626 self.cmdstats
627 .0
628 .iter()
629 .enumerate()
630 .filter(|(_, row)| row.seen())
631 .map(|(at, row)| (table::name_at(at), *row))
632 }
633
634 #[must_use]
636 pub const fn maxmemory(&self) -> u64 {
637 self.maxmemory
638 }
639
640 pub fn set_maxmemory(&mut self, bytes: u64) {
654 self.maxmemory = bytes;
655 for db in &mut self.dbs {
656 db.track_memory(bytes != 0);
657 }
658 self.used = self.settled_memory();
659 }
660
661 pub fn set_store_source(
671 &mut self,
672 source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
673 ) {
674 self.store = Some(Box::new(source));
675 }
676
677 #[must_use]
679 pub const fn has_store_source(&self) -> bool {
680 self.store.is_some()
681 }
682
683 fn attach_store(&mut self, at: usize) {
690 if self.dbs[at].store_bytes().is_some() {
691 return;
692 }
693 let Some(source) = self.store.as_mut() else {
694 return;
695 };
696 if let Some(blocks) = source(at) {
697 self.dbs[at].attach(blocks);
698 }
699 }
700
701 #[must_use]
703 pub const fn maxstore(&self) -> Option<u64> {
704 self.maxstore
705 }
706
707 pub const fn set_maxstore(&mut self, bytes: Option<u64>) {
713 self.maxstore = bytes;
714 }
715
716 #[must_use]
722 pub fn store_bytes(&self) -> u64 {
723 self.dbs.iter().filter_map(Keyspace::store_bytes).sum()
724 }
725
726 #[must_use]
741 pub fn cold_stats(&self) -> yo_kv::tier::Stats {
742 let mut total = yo_kv::tier::Stats::default();
743 for db in &self.dbs {
744 let Some(tier) = db.tier() else { continue };
745 let s = tier.stats();
746 total.demoted += s.demoted;
747 total.promoted += s.promoted;
748 total.faults += s.faults;
749 total.served += s.served;
750 total.bytes_out += s.bytes_out;
751 total.bytes_in += s.bytes_in;
752 }
753 total
754 }
755
756 #[must_use]
763 pub fn regime(&self) -> &'static str {
764 if (0..self.dbs.len()).any(|at| self.migrates(at)) {
765 "migrate"
766 } else {
767 "evict"
768 }
769 }
770
771 fn migrates(&self, at: usize) -> bool {
783 if self.maxstore == Some(0) {
784 return false;
785 }
786 match self.dbs[at].store_bytes() {
787 Some(held) => self.maxstore.is_none_or(|cap| held < cap),
788 None => self.store.is_some(),
792 }
793 }
794
795 pub fn refresh_memory(&mut self) {
800 if self.maxmemory != 0 {
801 self.used = self.settled_memory();
802 }
803 }
804
805 fn settled_memory(&mut self) -> usize {
812 self.dbs
813 .iter_mut()
814 .map(Keyspace::settled_memory_bytes)
815 .sum::<usize>()
816 + self.conn_bytes
817 }
818
819 pub fn make_room(&mut self) -> bool {
853 if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
854 return true;
855 }
856 self.used = self.settled_memory();
861 let mut budget = EVICT_BUDGET;
862 while self.used as u64 > self.maxmemory {
863 let over = self.used - self.maxmemory as usize;
864 if !self.relieve_step(over) {
865 return false;
866 }
867 self.compact_hard_step();
868 self.used = self.settled_memory();
869 budget -= 1;
870 if budget == 0 {
871 break;
872 }
873 }
874 true
875 }
876
877 fn relieve_step(&mut self, over: usize) -> bool {
894 for turn in 0..self.dbs.len() {
895 let i = (self.evict_db + turn) % self.dbs.len();
896 let gave = if !self.dbs[i].is_empty() && self.migrates(i) {
899 self.attach_store(i);
900 self.dbs[i]
905 .relieve(over)
906 .is_ok_and(yo_kv::tier::Relief::made_room)
907 } else {
908 self.dbs[i].evict_one()
909 };
910 if gave {
911 self.evict_db = (i + 1) % self.dbs.len();
912 self.dirty |= 1u64 << i;
913 return true;
914 }
915 }
916 false
917 }
918
919 pub fn expire_slice(&mut self, budget: usize) -> usize {
934 let now = self.clock.now_ms();
935 if now == self.expire_ms {
936 return 0;
937 }
938 self.expire_ms = now;
939 self.expire_step(budget)
940 }
941
942 pub fn expire_step(&mut self, budget: usize) -> usize {
958 let mut spent = 0;
959 for turn in 0..self.dbs.len() {
960 if spent >= budget {
961 break;
962 }
963 let i = (self.expire_db + turn) % self.dbs.len();
964 let c = self.dbs[i].expire_cycle(budget - spent);
965 spent += c.examined;
966 if c.expired > 0 {
967 self.expire_db = (i + 1) % self.dbs.len();
968 self.dirty |= 1u64 << i;
969 }
970 }
971 spent
972 }
973
974 fn compact_hard_step(&mut self) -> Option<usize> {
980 for turn in 0..self.dbs.len() {
981 let i = (self.next_db + turn) % self.dbs.len();
982 if let Some(moved) = self.dbs[i].compact_hard() {
983 self.next_db = (i + 1) % self.dbs.len();
984 return Some(moved);
985 }
986 }
987 None
988 }
989
990 pub fn compact_step(&mut self) -> Option<usize> {
1003 for turn in 0..self.dbs.len() {
1004 let i = (self.next_db + turn) % self.dbs.len();
1005 if self.dirty & (1 << i) == 0 {
1009 continue;
1010 }
1011 if let Some(moved) = self.dbs[i].compact_step() {
1012 self.next_db = (i + 1) % self.dbs.len();
1013 return Some(moved);
1014 }
1015 self.dirty &= !(1u64 << i);
1016 }
1017 None
1018 }
1019}
1020
1021impl Default for Server {
1022 fn default() -> Server {
1023 Server::new()
1024 }
1025}
1026
1027pub struct Session {
1029 db: usize,
1030 id: u64,
1031 name: Vec<u8>,
1032 sets: himport::Fieldsets,
1038}
1039
1040impl Session {
1041 #[must_use]
1043 pub fn new(id: u64) -> Session {
1044 Session {
1045 db: 0,
1046 id,
1047 name: Vec::new(),
1048 sets: himport::Fieldsets::default(),
1049 }
1050 }
1051
1052 #[must_use]
1054 pub const fn id(&self) -> u64 {
1055 self.id
1056 }
1057
1058 #[must_use]
1060 pub const fn db(&self) -> usize {
1061 self.db
1062 }
1063
1064 #[must_use]
1066 pub fn name(&self) -> &[u8] {
1067 &self.name
1068 }
1069
1070 pub fn reset(&mut self) {
1075 self.db = 0;
1076 self.name.clear();
1077 self.sets.clear();
1081 }
1082
1083 fn set_name(&mut self, name: &[u8]) {
1085 yo_alloc::allow(|| {
1086 self.name.clear();
1087 self.name.extend_from_slice(name);
1088 });
1089 }
1090}
1091
1092pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1097 if args.is_empty() {
1100 return Flow::Continue;
1101 }
1102 resolved(server, session, lookup(args.name()), args, out)
1103}
1104
1105pub fn resolved(
1117 server: &mut Server,
1118 session: &mut Session,
1119 spec: Option<&'static Spec>,
1120 args: Args<'_>,
1121 out: &mut Out,
1122) -> Flow {
1123 if args.is_empty() {
1124 return Flow::Continue;
1125 }
1126 server.stats.commands += 1;
1127
1128 let Some(spec) = spec else {
1129 write_error(out, &args::unknown_command(args));
1130 return Flow::Continue;
1131 };
1132 if !arity_ok(spec, args.len()) {
1133 server.cmdstats.at(spec).rejected += 1;
1134 write_error(out, &args::wrong_arity(spec.name));
1135 return Flow::Continue;
1136 }
1137
1138 if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1148 server.cmdstats.at(spec).rejected += 1;
1149 out.error_line(b"OOM ", OOM);
1150 return Flow::Continue;
1151 }
1152
1153 server.dirty |= match spec.group {
1160 "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1161 | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" => {
1162 1u64 << session.db
1163 }
1164 _ => ALL_DATABASES,
1165 };
1166
1167 let mark = out.len();
1168 let done = if spec.flags.contains(&"blocking") {
1175 blocking::execute(server, session, spec, args, out)
1176 } else {
1177 match spec.group {
1178 "string" => {
1179 let db = session.db;
1180 strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1181 }
1182 "bitmap" => {
1186 let db = session.db;
1187 bits::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1188 }
1189 "hyperloglog" => {
1192 let db = session.db;
1193 hll::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1194 }
1195 "set" => {
1196 let db = session.db;
1197 sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1198 }
1199 "hash" if spec.name == "himport" => {
1204 let db = session.db;
1205 himport::execute(&mut server.dbs[db], &mut session.sets, args, out)
1206 .map(|()| Flow::Continue)
1207 }
1208 "hash" => {
1209 let db = session.db;
1210 hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1211 }
1212 "list" => {
1213 let db = session.db;
1214 lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1215 }
1216 "zset" => {
1217 let db = session.db;
1218 zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1219 }
1220 "geo" => {
1224 let db = session.db;
1225 geo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1226 }
1227 "array" => {
1228 let db = session.db;
1229 arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1230 }
1231 "graph" => {
1232 let db = session.db;
1233 graph::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1234 }
1235 "json" => {
1239 let db = session.db;
1240 json::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1241 }
1242 "vector" => {
1243 let db = session.db;
1244 vectors::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1245 }
1246 "bloom" => {
1247 let db = session.db;
1248 bloom::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1249 }
1250 "cuckoo" => {
1251 let db = session.db;
1252 cuckoo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1253 }
1254 "cms" => {
1255 let db = session.db;
1256 cms::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1257 }
1258 "topk" => {
1259 let db = session.db;
1260 topk::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1261 }
1262 "tdigest" => {
1263 let db = session.db;
1264 tdigest::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1265 }
1266 "stream" => {
1271 let db = session.db;
1272 let now = server.now_ms();
1273 streams::execute(&mut server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1274 }
1275 "keyspace" if spec.name == "migrate" => {
1279 migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1280 }
1281 "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
1284 .map(|()| Flow::Continue),
1285 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1286 _ => server::execute(server, session, spec, args, out),
1287 }
1288 };
1289 let flow = match done {
1290 Ok(flow) => flow,
1291 Err(e) => {
1292 out.truncate(mark);
1293 write_error(out, &e);
1294 Flow::Continue
1295 }
1296 };
1297
1298 let row = server.cmdstats.at(spec);
1309 row.calls += 1;
1310 if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1311 row.failed += 1;
1312 }
1313 flow
1314}
1315
1316fn write_error(out: &mut Out, e: &Error) {
1326 let prefix: &[u8] = match e.code() {
1327 Code::WrongType => b"WRONGTYPE ",
1328 Code::Corrupt => b"INVALIDOBJ ",
1332 _ => b"ERR ",
1333 };
1334 out.error_line(prefix, e.message().as_bytes());
1335}
1336
1337#[cfg(test)]
1338mod tests {
1339 use super::*;
1340 use crate::proto::{Limits, Proto};
1341 use crate::request::Argv;
1342
1343 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1348 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1349 for p in parts {
1350 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1351 wire.extend_from_slice(p);
1352 wire.extend_from_slice(b"\r\n");
1353 }
1354 wire
1355 }
1356
1357 struct Fixture {
1359 server: Server,
1360 session: Session,
1361 argv: Argv,
1362 out: Out,
1363 }
1364
1365 impl Fixture {
1366 fn new() -> Fixture {
1367 Fixture {
1368 server: Server::new(),
1369 session: Session::new(7),
1370 argv: Argv::new(),
1371 out: Out::new(Proto::Resp2),
1372 }
1373 }
1374
1375 fn run(&mut self, parts: &[&[u8]]) -> String {
1377 self.flow(parts).1
1378 }
1379
1380 fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1386 let wire = encode(parts);
1387 self.argv.decode(&wire, &Limits::default()).unwrap();
1388 self.out.clear();
1389 execute(
1390 &mut self.server,
1391 &mut self.session,
1392 Args::new(&self.argv, &wire),
1393 &mut self.out,
1394 );
1395 self.out.as_slice().to_vec()
1396 }
1397
1398 fn advance(&mut self, ms: u64) {
1400 for db in 0..DATABASES {
1401 self.server.db(db).clock_mut().advance(ms);
1402 }
1403 }
1404
1405 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1407 let wire = encode(parts);
1408 self.argv.decode(&wire, &Limits::default()).unwrap();
1409 self.out.clear();
1410 let flow = execute(
1411 &mut self.server,
1412 &mut self.session,
1413 Args::new(&self.argv, &wire),
1414 &mut self.out,
1415 );
1416 (
1417 flow,
1418 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1419 )
1420 }
1421 }
1422
1423 #[test]
1427 fn rewriting_the_same_keys_does_not_grow_the_server() {
1428 let mut f = Fixture::new();
1429 let val = vec![b'v'; 1024];
1430 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1431
1432 for k in &keys {
1433 f.run(&[b"SET", k, &val]);
1434 }
1435 f.server.compact_step();
1436 let after_first = f.server.memory_bytes();
1437
1438 for _ in 0..500 {
1443 for k in &keys {
1444 f.run(&[b"SET", k, &val]);
1445 }
1446 f.server.compact_step();
1447 }
1448
1449 assert!(
1450 f.server.memory_bytes() <= after_first * 2,
1451 "held {} after five hundred passes against {after_first} after one",
1452 f.server.memory_bytes()
1453 );
1454 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1455 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1456 }
1457
1458 #[test]
1472 fn a_database_nobody_started_on_is_still_collected() {
1473 let mut f = Fixture::new();
1474 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1475 let val = vec![b'v'; 1024];
1476 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1477
1478 for k in &keys {
1479 f.run(&[b"SET", k, &val]);
1480 }
1481 while f.server.compact_step().is_some() {}
1482 assert_eq!(
1483 f.server.dirty & (1 << 9),
1484 0,
1485 "database nine was drained and should not be asked again until it is written to"
1486 );
1487 let after_first = f.server.memory_bytes();
1488
1489 for _ in 0..500 {
1490 for k in &keys {
1491 f.run(&[b"SET", k, &val]);
1492 }
1493 f.server.compact_step();
1494 }
1495
1496 assert!(
1497 f.server.memory_bytes() <= after_first * 2,
1498 "held {} after five hundred passes against {after_first} after one",
1499 f.server.memory_bytes()
1500 );
1501 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1502 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1503 f.run(&[b"SELECT", b"0"]);
1505 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1506 }
1507
1508 #[test]
1509 fn a_command_goes_from_bytes_to_bytes() {
1510 let mut f = Fixture::new();
1511 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1512 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1513 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1514 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1515 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1517 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1518 }
1519
1520 #[test]
1521 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1522 let mut f = Fixture::new();
1523 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1524 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1527 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1528 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1529 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1531 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1532 }
1533
1534 #[test]
1535 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1536 let mut f = Fixture::new();
1537 f.run(&[b"SET", b"k", b"v"]);
1538 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1541 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1542 }
1543
1544 #[test]
1545 fn touch_counts_the_way_exists_counts() {
1546 let mut f = Fixture::new();
1547 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1548 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1549 assert_eq!(
1550 f.run(&[b"TOUCH", b"a", b"a"]),
1551 ":2\r\n",
1552 "twice counts twice"
1553 );
1554 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1555 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1556 }
1557
1558 #[test]
1559 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1560 let mut f = Fixture::new();
1561 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1562 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1563
1564 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1565 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1566 assert_eq!(
1567 f.run(&[b"TTL", b"b"]),
1568 ":100\r\n",
1569 "the source's and not b's"
1570 );
1571 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1572 }
1573
1574 #[test]
1575 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1576 let mut f = Fixture::new();
1577 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1578 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1581 }
1582
1583 #[test]
1584 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1585 let mut f = Fixture::new();
1586 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1587
1588 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1589 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1590 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1593 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1594 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1595 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1596 }
1597
1598 #[test]
1599 fn renaming_a_set_does_not_touch_a_member() {
1600 let mut f = Fixture::new();
1601 for i in 0..300 {
1602 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1603 }
1604 let before = f.server.memory_bytes();
1605
1606 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1607 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1608 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1609 assert!(
1610 f.server.memory_bytes().abs_diff(before) < 256,
1611 "the members were copied: {} against {before}",
1612 f.server.memory_bytes()
1613 );
1614 }
1615
1616 #[test]
1617 fn a_copy_is_a_second_value_and_not_a_second_name() {
1618 let mut f = Fixture::new();
1619 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1620
1621 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1622 f.run(&[b"SADD", b"t", b"m3"]);
1623 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1624 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1625 }
1626
1627 #[test]
1637 fn every_type_can_be_copied() {
1638 let mut f = Fixture::new();
1639 f.run(&[b"SET", b"str", b"v1"]);
1640 f.run(&[b"SADD", b"set", b"m1"]);
1641 f.run(&[b"HSET", b"hash", b"f", b"v"]);
1642 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1643 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1644
1645 for name in [
1646 &b"str"[..],
1647 &b"set"[..],
1648 &b"hash"[..],
1649 &b"list"[..],
1650 &b"zset"[..],
1651 ] {
1652 let dst = [name, b":copy"].concat();
1653 assert_eq!(
1654 f.run(&[b"COPY", name, &dst]),
1655 ":1\r\n",
1656 "copying {}",
1657 String::from_utf8_lossy(name)
1658 );
1659 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1660 }
1661
1662 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1663 let mut want = String::from("*2\r\n");
1664 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1665 want
1666 });
1667 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1668
1669 f.run(&[b"RPUSH", b"list:copy", b"c"]);
1671 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1672 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1673 }
1674
1675 #[test]
1676 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1677 let mut f = Fixture::new();
1678 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1679 f.run(&[b"SET", b"b", b"v2"]);
1680
1681 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1682 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1683 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1684 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1685 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1686 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1687 }
1688
1689 #[test]
1690 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1691 let mut f = Fixture::new();
1692 f.run(&[b"SET", b"a", b"v1"]);
1693
1694 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1697 f.run(&[b"SELECT", b"1"]);
1698 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1699 assert_eq!(
1700 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1701 ":0\r\n",
1702 "taken"
1703 );
1704 assert_eq!(
1705 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1706 ":1\r\n"
1707 );
1708 }
1709
1710 #[test]
1711 fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1712 let mut f = Fixture::new();
1713 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1714 assert_eq!(
1715 f.run(&[b"SORT", b"l"]),
1716 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1717 );
1718 assert_eq!(
1720 f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1721 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1722 );
1723 assert_eq!(
1724 f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1725 "*1\r\n$1\r\n2\r\n"
1726 );
1727 }
1728
1729 #[test]
1730 fn sort_reads_a_key_per_element_for_by_and_for_get() {
1731 let mut f = Fixture::new();
1732 f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1733 f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1734 assert_eq!(
1737 f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1738 "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1739 );
1740 }
1741
1742 #[test]
1743 fn sort_store_writes_a_list_and_answers_its_length() {
1744 let mut f = Fixture::new();
1745 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1746 assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1747 assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1748 assert_eq!(
1749 f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1750 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1751 );
1752 assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1755 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1756 }
1757
1758 #[test]
1759 fn sort_ro_does_not_know_the_word_store() {
1760 let mut f = Fixture::new();
1761 f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1762 assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1763 assert_eq!(
1764 f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1765 "-ERR syntax error\r\n"
1766 );
1767 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1768 }
1769
1770 #[test]
1771 fn sort_refuses_what_it_cannot_sort() {
1772 let mut f = Fixture::new();
1773 assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1774 f.run(&[b"SET", b"s", b"x"]);
1775 assert_eq!(
1776 f.run(&[b"SORT", b"s"]),
1777 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1778 );
1779 f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1780 assert_eq!(
1781 f.run(&[b"SORT", b"words"]),
1782 "-ERR One or more scores can't be converted into double\r\n"
1783 );
1784 assert_eq!(
1785 f.run(&[b"SORT", b"words", b"ALPHA"]),
1786 "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1787 );
1788 assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1789 }
1790
1791 #[test]
1792 fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1793 let mut f = Fixture::new();
1794 assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1795 assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1796 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1797 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1798 assert_eq!(
1799 f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1800 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1801 );
1802 assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1805 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1806 }
1807
1808 #[test]
1809 fn move_answers_zero_when_either_end_says_no() {
1810 let mut f = Fixture::new();
1811 assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1812 assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1813 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1814 assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1815 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1816 assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1819 assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1820 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1821 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1822 }
1823
1824 #[test]
1825 fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1826 let mut f = Fixture::new();
1827 assert_eq!(
1828 f.run(&[b"MOVE", b"a", b"0"]),
1829 "-ERR source and destination objects are the same\r\n"
1830 );
1831 assert_eq!(
1832 f.run(&[b"MOVE", b"a", b"99"]),
1833 "-ERR DB index is out of range\r\n"
1834 );
1835 assert_eq!(
1836 f.run(&[b"MOVE", b"a", b"-1"]),
1837 "-ERR DB index is out of range\r\n"
1838 );
1839 assert_eq!(
1840 f.run(&[b"MOVE", b"a", b"x"]),
1841 "-ERR value is not an integer or out of range\r\n"
1842 );
1843 }
1844
1845 #[test]
1846 fn swapdb_swaps_what_two_connections_would_see() {
1847 let mut f = Fixture::new();
1848 assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1849 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1850 assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1851 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1852
1853 assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1854 assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1856 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1857 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1858 assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1860 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1861 }
1862
1863 #[test]
1864 fn swapdb_says_which_index_it_could_not_read() {
1865 let mut f = Fixture::new();
1866 assert_eq!(
1867 f.run(&[b"SWAPDB", b"x", b"1"]),
1868 "-ERR invalid first DB index\r\n"
1869 );
1870 assert_eq!(
1871 f.run(&[b"SWAPDB", b"0", b"y"]),
1872 "-ERR invalid second DB index\r\n"
1873 );
1874 assert_eq!(
1878 f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
1879 "-ERR invalid first DB index\r\n"
1880 );
1881 assert_eq!(
1882 f.run(&[b"SWAPDB", b"0", b"99"]),
1883 "-ERR DB index is out of range\r\n"
1884 );
1885 assert_eq!(
1886 f.run(&[b"SWAPDB", b"-1", b"0"]),
1887 "-ERR DB index is out of range\r\n"
1888 );
1889 }
1890
1891 #[test]
1892 fn wait_answers_zero_replicas_without_waiting() {
1893 let mut f = Fixture::new();
1894 assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
1895 assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
1896 assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
1899 assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
1902 assert_eq!(
1903 f.run(&[b"WAIT", b"x", b"0"]),
1904 "-ERR value is not an integer or out of range\r\n"
1905 );
1906 assert_eq!(
1907 f.run(&[b"WAIT", b"0", b"-1"]),
1908 "-ERR timeout is negative\r\n"
1909 );
1910 assert_eq!(
1911 f.run(&[b"WAIT", b"0", b"1.5"]),
1912 "-ERR timeout is not an integer or out of range\r\n"
1913 );
1914 }
1915
1916 #[test]
1917 fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
1918 let mut f = Fixture::new();
1919 assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
1920 assert_eq!(
1921 f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
1922 "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
1923 );
1924 assert_eq!(
1925 f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
1926 "-ERR value is out of range, value must between 0 and 1\r\n"
1927 );
1928 assert_eq!(
1929 f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
1930 "-ERR value is out of range, must be positive\r\n"
1931 );
1932 assert_eq!(
1935 f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
1936 "-ERR timeout is negative\r\n"
1937 );
1938 }
1939
1940 fn payload(reply: &[u8]) -> Vec<u8> {
1944 let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
1945 reply[head + 2..reply.len() - 2].to_vec()
1946 }
1947
1948 #[test]
1949 fn a_value_survives_a_dump_and_a_restore() {
1950 let mut f = Fixture::new();
1951 f.run(&[b"SET", b"s", b"hello"]);
1952 f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
1953 f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
1954 f.run(&[b"SADD", b"u", b"x", b"y"]);
1955 f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
1956 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
1957
1958 for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
1959 let mut copy = key.to_vec();
1960 copy.push(b'2');
1961 let bytes = payload(&f.raw(&[b"DUMP", key]));
1962 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
1963 assert_eq!(f.run(&[b"TYPE", ©]), f.run(&[b"TYPE", key]));
1964 }
1965
1966 assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
1967 assert_eq!(
1968 f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
1969 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
1970 );
1971 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
1972 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
1973 assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
1974 assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
1975 assert_eq!(
1978 f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
1979 f.run(&[b"OBJECT", b"ENCODING", b"t"])
1980 );
1981 }
1982
1983 #[test]
1984 fn a_dumped_hash_keeps_its_field_deadlines() {
1985 let mut f = Fixture::new();
1986 f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
1987 assert_eq!(
1988 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
1989 "*1\r\n:1\r\n"
1990 );
1991 let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
1992 assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
1993 assert_eq!(
1994 f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
1995 "*2\r\n:-1\r\n:100\r\n"
1996 );
1997 }
1998
1999 #[test]
2000 fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2001 let mut f = Fixture::new();
2002 f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2003 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2004 assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2005 assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2006 assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2007 assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2008 assert_eq!(
2011 f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2012 "+OK\r\n"
2013 );
2014 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2015 }
2016
2017 #[test]
2018 fn dump_answers_nothing_for_a_key_that_is_not_there() {
2019 let mut f = Fixture::new();
2020 assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2021 f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2022 f.advance(50);
2023 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2024 }
2025
2026 #[test]
2027 fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2028 let mut f = Fixture::new();
2029 f.run(&[b"SET", b"a", b"first"]);
2030 f.run(&[b"SET", b"b", b"second"]);
2031 let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2032 assert_eq!(
2033 f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2034 "-BUSYKEY Target key name already exists.\r\n"
2035 );
2036 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2037 assert_eq!(
2038 f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2039 "+OK\r\n"
2040 );
2041 assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2042 }
2043
2044 #[test]
2048 fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2049 let mut f = Fixture::new();
2050 f.run(&[b"SET", b"a", b"v"]);
2051 assert_eq!(
2052 f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2053 "-BUSYKEY Target key name already exists.\r\n"
2054 );
2055 assert_eq!(
2058 f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2059 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2060 );
2061 }
2062
2063 #[test]
2064 fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2065 let mut f = Fixture::new();
2066 f.run(&[b"SET", b"a", b"hello"]);
2067 let good = payload(&f.raw(&[b"DUMP", b"a"]));
2068
2069 let mut flipped = good.clone();
2070 flipped[2] ^= 0x40;
2071 assert_eq!(
2072 f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2073 "-ERR DUMP payload version or checksum are wrong\r\n"
2074 );
2075 assert_eq!(
2076 f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2077 "-ERR DUMP payload version or checksum are wrong\r\n"
2078 );
2079 let mut truncated = good[..1].to_vec();
2083 truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2084 let crc = yo_common::crc::crc64(0, &truncated);
2085 truncated.extend_from_slice(&crc.to_le_bytes());
2086 assert_eq!(
2087 f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2088 "-ERR Bad data format\r\n"
2089 );
2090 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2091 }
2092
2093 #[test]
2094 fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2095 let mut f = Fixture::new();
2096 f.run(&[b"SET", b"a", b"v"]);
2097 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2098 assert_eq!(
2099 f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2100 "-ERR Invalid TTL value, must be >= 0\r\n"
2101 );
2102 assert_eq!(
2103 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2104 "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2105 );
2106 assert_eq!(
2107 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2108 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2109 );
2110 assert_eq!(
2112 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2113 "+OK\r\n"
2114 );
2115 assert_eq!(
2116 f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2117 "+OK\r\n"
2118 );
2119 }
2120
2121 #[test]
2125 fn restore_takes_idletime_or_freq_and_not_both() {
2126 let mut f = Fixture::new();
2127 f.run(&[b"SET", b"a", b"v"]);
2128 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2129 assert_eq!(
2130 f.run(&[
2131 b"RESTORE",
2132 b"b",
2133 b"0",
2134 &bytes,
2135 b"IDLETIME",
2136 b"1",
2137 b"FREQ",
2138 b"2"
2139 ]),
2140 "-ERR syntax error\r\n"
2141 );
2142 assert_eq!(
2143 f.run(&[
2144 b"RESTORE",
2145 b"b",
2146 b"0",
2147 &bytes,
2148 b"FREQ",
2149 b"2",
2150 b"IDLETIME",
2151 b"1"
2152 ]),
2153 "-ERR syntax error\r\n"
2154 );
2155 assert_eq!(
2156 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2157 "-ERR syntax error\r\n"
2158 );
2159 assert_eq!(
2160 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2161 "-ERR syntax error\r\n"
2162 );
2163 }
2164
2165 #[test]
2166 fn copy_checks_its_options_before_it_looks_for_anything() {
2167 let mut f = Fixture::new();
2168 assert_eq!(
2171 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2172 "-ERR DB index is out of range\r\n"
2173 );
2174 assert_eq!(
2175 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2176 "-ERR DB index is out of range\r\n"
2177 );
2178 assert_eq!(
2179 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2180 "-ERR value is not an integer or out of range\r\n"
2181 );
2182 assert_eq!(
2183 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2184 "-ERR syntax error\r\n"
2185 );
2186 assert_eq!(
2187 f.run(&[b"COPY", b"a", b"a"]),
2188 "-ERR source and destination objects are the same\r\n"
2189 );
2190 assert_eq!(
2192 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2193 ":0\r\n"
2194 );
2195 }
2196
2197 #[test]
2198 fn time_is_two_bulk_strings_and_moves() {
2199 let mut f = Fixture::new();
2200 let first = f.run(&[b"TIME"]);
2201 assert!(first.starts_with("*2\r\n$"), "got {first}");
2202 let parts: Vec<&str> = first.split("\r\n").collect();
2203 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2204 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2205 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2206 assert!((0..1_000_000).contains(µs), "got {micros}");
2207 assert_ne!(first, f.run(&[b"TIME"]));
2211 }
2212
2213 #[test]
2214 fn a_keyspace_scan_walks_every_key_once() {
2215 let mut f = Fixture::new();
2216 for i in 0..500 {
2217 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2218 }
2219
2220 let mut seen: Vec<String> = Vec::new();
2221 let mut cursor = "0".to_owned();
2222 let mut calls = 0;
2223 loop {
2224 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2225 seen.extend(keys);
2226 cursor = next;
2227 calls += 1;
2228 assert!(calls < 10_000, "the cursor is not advancing");
2229 if cursor == "0" {
2230 break;
2231 }
2232 }
2233
2234 seen.sort();
2235 seen.dedup();
2236 assert_eq!(seen.len(), 500, "every key once and only once");
2237 assert!(calls > 1, "500 keys came back in one batch");
2240 }
2241
2242 #[test]
2243 fn a_scan_narrows_by_pattern_and_by_type() {
2244 let mut f = Fixture::new();
2245 f.run(&[b"SET", b"str", b"v"]);
2246 f.run(&[b"SADD", b"members", b"a"]);
2247 f.run(&[b"HSET", b"fields", b"f", b"v"]);
2248
2249 let all = |f: &mut Fixture, args: &[&[u8]]| {
2250 let mut out: Vec<String> = Vec::new();
2251 let mut cursor = "0".to_owned();
2252 loop {
2253 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2254 line.extend_from_slice(args);
2255 let (next, keys) = scan_reply(&f.run(&line));
2256 out.extend(keys);
2257 cursor = next;
2258 if cursor == "0" {
2259 break;
2260 }
2261 }
2262 out.sort();
2263 out
2264 };
2265
2266 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2267 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2268 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2269 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2271 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2273 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2274 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2276 }
2277
2278 #[test]
2279 fn a_scan_says_what_is_wrong_with_it() {
2280 let mut f = Fixture::new();
2281 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2282 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2283 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2284 assert_eq!(
2285 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2286 "-ERR syntax error\r\n"
2287 );
2288 assert_eq!(
2289 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2290 "-ERR value is not an integer or out of range\r\n"
2291 );
2292 assert_eq!(
2293 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2294 "-ERR syntax error\r\n"
2295 );
2296 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2301 }
2302
2303 #[test]
2304 fn keys_and_randomkey_look_at_the_whole_database() {
2305 let mut f = Fixture::new();
2306 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2307 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2308
2309 for name in ["one", "two", "three"] {
2310 f.run(&[b"SET", name.as_bytes(), b"v"]);
2311 }
2312 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2313 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2314 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2315
2316 for _ in 0..50 {
2317 let got = f.run(&[b"RANDOMKEY"]);
2318 assert!(
2319 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2320 "got {got}"
2321 );
2322 }
2323 }
2324
2325 #[test]
2326 fn a_walk_does_not_answer_keys_that_have_expired() {
2327 let mut f = Fixture::new();
2328 f.run(&[b"SET", b"alive", b"v"]);
2329 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2330 f.server.db(0).clock_mut().advance(2);
2331 assert_eq!(
2332 f.run(&[b"DBSIZE"]),
2333 ":2\r\n",
2334 "nothing has collected it yet"
2335 );
2336
2337 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2338 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2339 assert_eq!(keys, ["alive"]);
2340 for _ in 0..20 {
2341 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2342 }
2343 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2346 }
2347
2348 #[test]
2349 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2350 let mut f = Fixture::new();
2351 f.run(&[b"SET", b"k", b"v"]);
2352 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2353 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2354
2355 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2356 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2357 let ms = int(&f.run(&[b"PTTL", b"k"]));
2358 assert!((99_000..=100_000).contains(&ms), "got {ms}");
2359
2360 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2362 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2363 assert_eq!(at, (at_ms + 500) / 1000);
2364 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2365
2366 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2367 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2368 assert_eq!(
2369 f.run(&[b"PERSIST", b"k"]),
2370 ":0\r\n",
2371 "nothing to take off the second time"
2372 );
2373 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2374 assert_eq!(
2375 f.run(&[b"GET", b"k"]),
2376 "$1\r\nv\r\n",
2377 "and the value went through all of that untouched"
2378 );
2379 }
2380
2381 #[test]
2382 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2383 let mut f = Fixture::new();
2384 f.run(&[b"SET", b"str", b"v"]);
2385 f.run(&[b"SADD", b"set", b"a", b"b"]);
2386 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2387
2388 for key in [b"str".as_slice(), b"set", b"hash"] {
2389 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2390 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2391 }
2392 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2395 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2396 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2397 }
2398
2399 #[test]
2400 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2401 let mut f = Fixture::new();
2402 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2403 f.run(&[b"SET", key, b"v"]);
2404 }
2405 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2409 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2410 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2411 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2412 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2413 assert_eq!(
2414 f.run(&[b"EXPIRE", b"a", b"100"]),
2415 ":0\r\n",
2416 "and the key really went, so there is nothing to put a deadline on"
2417 );
2418 }
2419
2420 #[test]
2421 fn the_four_conditions_decide_whether_the_deadline_moves() {
2422 let mut f = Fixture::new();
2423 f.run(&[b"SET", b"k", b"v"]);
2424
2425 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2426 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2427 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2428 assert_eq!(
2429 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2430 ":1\r\n",
2431 "no deadline reads as infinitely far away, so LT passes where GT fails"
2432 );
2433
2434 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2435 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2436 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2437 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2438 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2439 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2440
2441 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2444 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2445 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2446 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2447 }
2448
2449 #[test]
2450 fn the_conditions_are_a_set_and_not_a_keyword() {
2451 let mut f = Fixture::new();
2452 f.run(&[b"SET", b"k", b"v"]);
2453
2454 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2455 assert_eq!(
2456 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2457 ":0\r\n",
2458 "the same keyword twice means it once, and NX now has a deadline to fail on"
2459 );
2460
2461 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2464 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2465 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2466 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2467 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2468 f.run(&[b"PERSIST", b"k"]);
2469 assert_eq!(
2470 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2471 ":0\r\n",
2472 "where LT on its own would have taken it"
2473 );
2474 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2475 }
2476
2477 #[test]
2478 fn a_key_is_gone_once_its_moment_passes() {
2479 let mut f = Fixture::new();
2480 f.run(&[b"SET", b"k", b"v"]);
2481 f.run(&[b"EXPIRE", b"k", b"100"]);
2482
2483 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2484 f.server.set_clock_ms(at as u64 + 1);
2485 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2486 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2487 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2488 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2489 }
2490
2491 #[test]
2492 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2493 let mut f = Fixture::new();
2494 f.run(&[b"SET", b"k", b"v"]);
2495 for (bad, want) in [
2496 (
2497 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2498 "-ERR value is not an integer or out of range\r\n",
2499 ),
2500 (
2501 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2502 "-ERR Unsupported option MAYBE\r\n",
2503 ),
2504 (
2505 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2506 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2507 ),
2508 (
2509 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2510 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2511 ),
2512 (
2513 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2514 "-ERR GT and LT options at the same time are not compatible\r\n",
2515 ),
2516 (
2519 &[b"EXPIRE", b"k", b"9223372036854775807"],
2520 "-ERR invalid expire time in 'expire' command\r\n",
2521 ),
2522 (
2523 &[b"EXPIREAT", b"k", b"9223372036854775807"],
2524 "-ERR invalid expire time in 'expireat' command\r\n",
2525 ),
2526 (
2527 &[b"PEXPIRE", b"k", b"9223372036854775807"],
2528 "-ERR invalid expire time in 'pexpire' command\r\n",
2529 ),
2530 ] {
2531 assert_eq!(f.run(bad), want, "for {bad:?}");
2532 }
2533 assert_eq!(
2534 f.run(&[b"TTL", b"k"]),
2535 ":-1\r\n",
2536 "and none of those put a deadline on anything"
2537 );
2538
2539 assert_eq!(
2543 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2544 ":1\r\n"
2545 );
2546 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2547 }
2548
2549 #[test]
2550 fn flushing_empties_this_database_or_every_one_of_them() {
2551 let mut f = Fixture::new();
2552 f.run(&[b"SELECT", b"0"]);
2553 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2554 f.run(&[b"SELECT", b"1"]);
2555 f.run(&[b"SET", b"c", b"3"]);
2556 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2557 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2560 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2561 f.run(&[b"SELECT", b"0"]);
2563 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2564 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2565 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2566 f.run(&[b"SELECT", b"1"]);
2567 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2568 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2571 assert_eq!(
2572 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2573 "-ERR syntax error\r\n"
2574 );
2575 }
2576
2577 #[test]
2578 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2579 let mut f = Fixture::new();
2580 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2581 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2582 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2583 assert_eq!(
2586 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2587 "*2\r\n:0\r\n:0\r\n"
2588 );
2589 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2590 assert_eq!(
2591 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2592 "*0\r\n"
2593 );
2594 assert_eq!(
2595 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2596 "-ERR Library not found\r\n"
2597 );
2598
2599 assert_eq!(
2602 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2603 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2604 );
2605 assert_eq!(
2606 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2607 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2608 );
2609 assert_eq!(
2612 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2613 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2614 );
2615 assert_eq!(
2616 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2617 "-ERR Unknown argument bogus\r\n"
2618 );
2619 assert_eq!(
2620 f.run(&[b"SCRIPT", b"EXISTS"]),
2621 "-ERR wrong number of arguments for 'script|exists' command\r\n"
2622 );
2623
2624 assert_eq!(
2627 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2628 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2629 );
2630 assert_eq!(
2631 f.run(&[b"FUNCTION", b"STATS"]),
2632 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2633 );
2634 }
2635
2636 #[test]
2637 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2638 let mut f = Fixture::new();
2639 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2640 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2641 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2642 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2645 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2646 f.run(&[b"SET", b"k", b"hello"]);
2649 assert_eq!(
2650 f.run(&[b"INCR", b"k"]),
2651 "-ERR value is not an integer or out of range\r\n"
2652 );
2653 assert_eq!(
2654 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2655 "-ERR increment would produce NaN or Infinity\r\n"
2656 );
2657 }
2658
2659 #[test]
2664 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2665 let mut f = Fixture::new();
2666 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2667 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2670 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2671 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2672 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2673 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2674 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2675 assert_eq!(
2676 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2677 "*2\r\n:1\r\n:0\r\n",
2678 "a refused increment reports the value it left alone and applied nothing"
2679 );
2680 assert_eq!(
2681 f.run(&[
2682 b"INCREX",
2683 b"n",
2684 b"BYINT",
2685 b"5",
2686 b"UBOUND",
2687 b"3",
2688 b"SATURATE"
2689 ]),
2690 "*2\r\n:3\r\n:2\r\n"
2691 );
2692 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2693 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2694 }
2695
2696 #[test]
2697 fn the_same_answers_come_out_in_resp3_spelling() {
2698 let mut f = Fixture::new();
2699 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2700 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2701 assert_eq!(
2704 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2705 "*2\r\n,1.5\r\n,1.5\r\n"
2706 );
2707 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2708 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2711 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2712 }
2713
2714 #[test]
2715 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2716 let mut f = Fixture::new();
2717 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2718 assert_eq!(flow, Flow::Continue);
2719 assert_eq!(
2720 reply,
2721 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2722 );
2723 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2726 assert_eq!(reply.matches("\r\n").count(), 1);
2727 }
2728
2729 #[test]
2730 fn arity_is_checked_before_the_command_is() {
2731 let mut f = Fixture::new();
2732 assert_eq!(
2733 f.run(&[b"GET"]),
2734 "-ERR wrong number of arguments for 'get' command\r\n"
2735 );
2736 assert_eq!(
2737 f.run(&[b"MSET", b"k"]),
2738 "-ERR wrong number of arguments for 'mset' command\r\n"
2739 );
2740 assert_eq!(
2744 f.run(&[b"PING", b"a", b"b"]),
2745 "-ERR wrong number of arguments for 'ping' command\r\n"
2746 );
2747 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2748 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2749 assert_eq!(
2751 f.run(&[b"DELEX", b"k", b"IFEQ"]),
2752 "-ERR wrong number of arguments for 'delex' command\r\n"
2753 );
2754 }
2755
2756 #[test]
2760 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2761 let mut f = Fixture::new();
2762 let syntax = "-ERR syntax error\r\n";
2763 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2764 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2765 assert_eq!(
2766 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2767 syntax
2768 );
2769 assert_eq!(
2770 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2771 syntax
2772 );
2773 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2774 assert_eq!(
2776 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2777 "+OK\r\n"
2778 );
2779 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2780 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2781 assert_eq!(
2783 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2784 syntax
2785 );
2786 assert_eq!(
2787 f.run(&[b"INCREX", b"n", b"ENX"]),
2788 "-ERR ENX flag requires an expiration\r\n"
2789 );
2790 assert_eq!(
2791 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2792 "-ERR UBOUND is not an integer or out of range\r\n"
2793 );
2794 assert_eq!(
2795 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2796 "-ERR LBOUND can't be greater than UBOUND\r\n"
2797 );
2798 assert_eq!(
2799 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2800 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2801 );
2802 }
2803
2804 #[test]
2808 fn the_expiry_rules_are_redis_own() {
2809 let mut f = Fixture::new();
2810 let bad = "-ERR invalid expire time in 'set' command\r\n";
2811 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2812 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2813 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2814 assert_eq!(
2815 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2816 bad
2817 );
2818 assert_eq!(
2819 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2820 "-ERR value is not an integer or out of range\r\n"
2821 );
2822 assert_eq!(
2823 f.run(&[b"SETEX", b"k", b"0", b"v"]),
2824 "-ERR invalid expire time in 'setex' command\r\n"
2825 );
2826 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2827 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2828 assert_eq!(
2829 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2830 "-ERR syntax error\r\n",
2831 "the option list is still checked before the key is looked up"
2832 );
2833 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2835 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2836 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2837 }
2838
2839 #[test]
2840 fn mset_takes_its_pairs_from_the_read_buffer() {
2841 let mut f = Fixture::new();
2842 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2843 assert_eq!(
2844 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2845 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2846 );
2847 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2848 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2849 assert_eq!(
2850 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2851 "-ERR wrong number of key-value pairs\r\n"
2852 );
2853 assert_eq!(
2854 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2855 "-ERR invalid numkeys value\r\n"
2856 );
2857 assert_eq!(
2858 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2859 "-ERR invalid numkeys value\r\n"
2860 );
2861 }
2862
2863 #[test]
2864 fn lcs_answers_the_length_the_string_and_the_runs() {
2865 let mut f = Fixture::new();
2866 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2867 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
2868 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
2869 assert_eq!(
2870 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
2871 "*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"
2872 );
2873 assert_eq!(
2876 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
2877 "$6\r\nmytext\r\n"
2878 );
2879 }
2880
2881 #[test]
2882 fn select_moves_the_connection_and_the_databases_stay_apart() {
2883 let mut f = Fixture::new();
2884 f.run(&[b"SET", b"k", b"zero"]);
2885 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
2886 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2887 f.run(&[b"SET", b"k", b"four"]);
2888 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2889 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2890 assert_eq!(
2891 f.run(&[b"SELECT", b"99"]),
2892 "-ERR DB index is out of range\r\n"
2893 );
2894 assert_eq!(
2895 f.run(&[b"SELECT", b"-1"]),
2896 "-ERR DB index is out of range\r\n"
2897 );
2898 assert_eq!(
2899 f.run(&[b"SELECT", b"abc"]),
2900 "-ERR value is not an integer or out of range\r\n"
2901 );
2902 f.run(&[b"SELECT", b"4"]);
2904 f.run(&[b"RESET"]);
2905 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2906 }
2907
2908 #[test]
2909 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
2910 let mut f = Fixture::new();
2911 let reply = f.run(&[b"HELLO"]);
2912 assert!(reply.starts_with("*14\r\n"), "{reply}");
2913 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
2914 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
2915 assert!(
2916 reply.contains(":7\r\n"),
2917 "the connection id is in there: {reply}"
2918 );
2919 assert_eq!(
2920 f.run(&[b"HELLO", b"4"]),
2921 "-NOPROTO unsupported protocol version\r\n"
2922 );
2923 assert_eq!(
2924 f.run(&[b"HELLO", b"abc"]),
2925 "-ERR Protocol version is not an integer or out of range\r\n"
2926 );
2927 assert_eq!(
2928 f.run(&[b"HELLO", b"3", b"SETNAME"]),
2929 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
2930 );
2931 assert!(
2932 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
2933 .starts_with("%7\r\n")
2934 );
2935 assert_eq!(f.session.name(), b"bob");
2936 f.run(&[b"RESET"]);
2937 assert_eq!(f.session.name(), b"");
2938 }
2939
2940 #[test]
2941 fn command_describes_this_server_in_the_shape_a_driver_reads() {
2942 let mut f = Fixture::new();
2943 let count = format!(":{}\r\n", COMMANDS.len());
2944 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
2945 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
2946 assert_eq!(
2947 info,
2948 "*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\
2949 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
2950 );
2951 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
2953 assert_eq!(
2954 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
2955 "*1\r\n$8\r\ngetrange\r\n"
2956 );
2957 assert_eq!(
2958 f.run(&[b"COMMAND", b"NOPE"]),
2959 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
2960 );
2961 }
2962
2963 #[test]
2967 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
2968 let mut f = Fixture::new();
2969 assert_eq!(
2970 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
2971 "*1\r\n$1\r\nk\r\n"
2972 );
2973 assert_eq!(
2974 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
2975 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2976 );
2977 assert_eq!(
2978 f.run(&[
2979 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
2980 ]),
2981 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2982 );
2983 assert_eq!(
2984 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
2985 "-ERR The command has no key arguments\r\n"
2986 );
2987 assert_eq!(
2988 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
2989 "-ERR Invalid number of arguments specified for command\r\n"
2990 );
2991 }
2992
2993 #[test]
2994 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
2995 let mut f = Fixture::new();
2996 assert_eq!(
2997 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2998 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
2999 );
3000 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3003 assert!(both.starts_with("*6\r\n"), "{both}");
3004 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3005 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3006 assert_eq!(
3007 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3008 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3009 );
3010 assert_eq!(
3011 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3012 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3013 );
3014 assert_eq!(
3015 f.run(&[b"CONFIG", b"GET"]),
3016 "-ERR wrong number of arguments for 'config|get' command\r\n"
3017 );
3018 assert_eq!(
3022 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3023 "-ERR wrong number of arguments for 'config|set' command\r\n"
3024 );
3025 assert_eq!(
3026 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3027 "-ERR syntax error\r\n"
3028 );
3029 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3030 assert_eq!(
3031 f.run(&[b"CONFIG", b"REWRITE"]),
3032 "-ERR The server is running without a config file\r\n"
3033 );
3034 }
3035
3036 #[test]
3037 fn the_eviction_policy_reads_back_what_was_written_to_it() {
3038 let mut f = Fixture::new();
3039 assert_eq!(
3040 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3041 "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3042 );
3043 assert_eq!(
3044 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3045 "+OK\r\n",
3046 "the name is matched without regard to case, like every other one"
3047 );
3048 assert_eq!(
3049 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3050 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3051 );
3052 assert!(
3054 f.run(&[b"INFO", b"memory"])
3055 .contains("maxmemory_policy:allkeys-lfu"),
3056 "INFO and CONFIG disagree about the policy"
3057 );
3058 assert_eq!(
3062 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3063 "-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"
3064 );
3065 assert_eq!(
3068 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3069 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3070 );
3071 f.run(&[
3072 b"CONFIG",
3073 b"SET",
3074 b"hash-max-listpack-entries",
3075 b"7",
3076 b"maxmemory-policy",
3077 b"nonsense",
3078 ]);
3079 assert_eq!(
3080 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3081 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3082 );
3083 }
3084
3085 #[test]
3086 fn the_three_eviction_numbers_read_back_too() {
3087 let mut f = Fixture::new();
3088 for (name, default, set) in [
3089 ("maxmemory-samples", "5", "12"),
3090 ("lfu-log-factor", "10", "3"),
3091 ("lfu-decay-time", "1", "60"),
3092 ] {
3093 let get = || {
3094 format!(
3095 "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3096 name.len(),
3097 default.len()
3098 )
3099 };
3100 assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3101 assert_eq!(
3102 f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3103 "+OK\r\n"
3104 );
3105 assert_eq!(
3106 f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3107 format!(
3108 "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3109 name.len(),
3110 set.len()
3111 )
3112 );
3113 assert_eq!(
3116 f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3117 format!(
3118 "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3119 )
3120 );
3121 }
3122 }
3123
3124 #[test]
3125 fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3126 let mut f = Fixture::new();
3127 assert_eq!(
3128 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3129 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3130 "no limit is the default"
3131 );
3132 for (typed, bytes) in [
3135 (&b"1024"[..], "1024"),
3136 (b"1k", "1000"),
3137 (b"1kb", "1024"),
3138 (b"1M", "1000000"),
3139 (b"1Mb", "1048576"),
3140 (b"1gb", "1073741824"),
3141 (b"100mb", "104857600"),
3142 ] {
3143 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3144 assert_eq!(
3145 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3146 format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3147 "set {}",
3148 String::from_utf8_lossy(typed)
3149 );
3150 }
3151 assert!(
3152 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3153 "the report agrees with the setting"
3154 );
3155
3156 for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3159 assert_eq!(
3160 f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3161 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3162 "refused {}",
3163 String::from_utf8_lossy(bad)
3164 );
3165 }
3166 assert!(
3167 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3168 "and the refusal left the old one alone"
3169 );
3170 }
3171
3172 #[test]
3173 fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3174 let mut f = Fixture::new();
3175 f.run(&[b"SET", b"here", b"already"]);
3176 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3180 assert_eq!(
3181 f.run(&[b"SET", b"k", b"v"]),
3182 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3183 );
3184 assert_eq!(
3185 f.run(&[b"LPUSH", b"l", b"v"]),
3186 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3187 );
3188 assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3190 assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3191 assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3192
3193 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3195 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3196 }
3197
3198 #[test]
3199 fn an_allkeys_policy_makes_room_instead_of_refusing() {
3200 let mut f = Fixture::new();
3201 let val = vec![b'v'; 256];
3202 for i in 0..24000u32 {
3203 let k = format!("key:{i:08}");
3204 f.run(&[b"SET", k.as_bytes(), &val]);
3205 }
3206 let full = f.server.memory_bytes();
3207 assert!(
3208 full > 3 * 1024 * 1024,
3209 "the arena is several segments: {full}"
3210 );
3211
3212 let limit = full - 2 * 1024 * 1024;
3216 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3217 f.run(&[
3218 b"CONFIG",
3219 b"SET",
3220 b"maxmemory",
3221 limit.to_string().as_bytes(),
3222 ]);
3223
3224 for i in 0..2000u32 {
3228 let k = format!("new:{i:08}");
3229 assert_eq!(
3230 f.run(&[b"SET", k.as_bytes(), &val]),
3231 "+OK\r\n",
3232 "write {i} was refused"
3233 );
3234 f.server.refresh_memory();
3235 if f.server.memory_bytes() <= limit {
3236 break;
3237 }
3238 }
3239 assert!(
3240 f.server.memory_bytes() <= limit,
3241 "it never got under: {} against {limit}",
3242 f.server.memory_bytes()
3243 );
3244 let info = f.run(&[b"INFO", b"stats"]);
3245 assert!(!info.contains("evicted_keys:0"), "{info}");
3246 assert!(
3247 f.run(&[b"DBSIZE"]) != ":0\r\n",
3248 "and it did not empty the database to get there"
3249 );
3250 }
3251
3252 #[test]
3253 fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3254 let mut f = Fixture::new();
3261 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3262 let big = vec![b'v'; 200];
3263
3264 for i in 0..400u32 {
3265 let n = i.to_string();
3266 let n = n.as_bytes();
3267 f.run(&[b"SADD", b"s", n]);
3268 f.run(&[b"SADD", b"s2", &big]);
3269 f.run(&[b"HSET", b"h", n, &big]);
3270 f.run(&[b"RPUSH", b"l", &big]);
3271 f.run(&[b"ZADD", b"z", n, n]);
3272 f.run(&[b"ARSET", b"a", n, &big]);
3273 if i % 7 == 0 {
3274 f.run(&[b"SREM", b"s", n]);
3275 f.run(&[b"HDEL", b"h", n]);
3276 f.run(&[b"LPOP", b"l"]);
3277 f.run(&[b"ZREM", b"z", n]);
3278 f.run(&[b"ARDEL", b"a", n]);
3279 }
3280 if i % 53 == 0 {
3281 f.run(&[b"DEL", b"s2"]);
3284 }
3285 assert_eq!(
3286 f.server.settled_memory(),
3287 f.server.memory_bytes(),
3288 "after round {i}"
3289 );
3290 }
3291
3292 assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3295 assert!(
3296 f.server.memory_bytes() > 512 * 1024,
3297 "{}",
3298 f.server.memory_bytes()
3299 );
3300
3301 f.run(&[b"FLUSHALL"]);
3303 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3304 }
3305
3306 #[test]
3307 fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3308 let mut f = Fixture::new();
3313 for i in 0..200u32 {
3314 let n = i.to_string();
3315 f.run(&[b"SADD", b"s", n.as_bytes()]);
3316 f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3317 }
3318 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3319 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3320
3321 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3322 for i in 200..400u32 {
3323 let n = i.to_string();
3324 f.run(&[b"SADD", b"s", n.as_bytes()]);
3325 }
3326 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3327 assert_eq!(
3328 f.server.settled_memory(),
3329 f.server.memory_bytes(),
3330 "the writes it was not watching are in the number it started from"
3331 );
3332 }
3333
3334 #[test]
3335 fn evicted_keys_and_expired_keys_are_different_numbers() {
3336 let mut f = Fixture::new();
3337 f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3340 f.server.db(0).clock_mut().advance(20);
3341 f.run(&[b"GET", b"gone"]);
3342 let info = f.run(&[b"INFO", b"stats"]);
3343 assert!(info.contains("expired_keys:1"), "{info}");
3344 assert!(info.contains("evicted_keys:0"), "{info}");
3345 }
3346
3347 #[test]
3348 fn the_object_subcommands_follow_the_policy() {
3349 let mut f = Fixture::new();
3350 f.run(&[b"SET", b"s", b"v"]);
3351 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3355 assert!(
3356 f.run(&[b"OBJECT", b"FREQ", b"s"])
3357 .starts_with("-ERR An LFU maxmemory policy is not selected"),
3358 );
3359
3360 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3361 assert!(
3362 f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3363 .starts_with("-ERR An LFU maxmemory policy is selected"),
3364 );
3365 assert!(
3370 f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3371 "FREQ should answer under an LFU policy"
3372 );
3373 }
3374
3375 #[test]
3376 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3377 let mut f = Fixture::new();
3378 f.run(&[b"SET", b"s", b"hello"]);
3379 f.run(&[b"SET", b"n", b"123"]);
3380 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3381 f.run(&[b"SADD", b"ss", b"a", b"b"]);
3382 f.run(&[b"HSET", b"h", b"f", b"v"]);
3383 for (key, want) in [
3384 (b"s".as_slice(), "embstr"),
3385 (b"n", "int"),
3386 (b"si", "intset"),
3387 (b"ss", "listpack"),
3388 (b"h", "listpack"),
3389 ] {
3390 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3391 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3392 }
3393
3394 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3397 assert_eq!(
3398 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3399 "$10\r\nlistpackex\r\n"
3400 );
3401
3402 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3403 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3404 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3405 }
3406
3407 #[test]
3408 fn object_answers_nil_for_a_key_that_is_not_there() {
3409 let mut f = Fixture::new();
3410 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3411 assert_eq!(
3412 f.run(&[b"OBJECT", sub, b"nokey"]),
3413 "$-1\r\n",
3414 "a nil and not an error, which is what 8.10.1 does"
3415 );
3416 }
3417 f.run(&[b"SET", b"s", b"v"]);
3420 assert!(
3421 f.run(&[b"OBJECT", b"FREQ", b"s"])
3422 .starts_with("-ERR An LFU maxmemory policy is not"),
3423 );
3424 assert_eq!(
3425 f.run(&[b"OBJECT", b"NOPE", b"s"]),
3426 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3427 );
3428 assert_eq!(
3429 f.run(&[b"OBJECT", b"ENCODING"]),
3430 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3431 );
3432 assert_eq!(
3433 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3434 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3435 );
3436 assert_eq!(
3437 f.run(&[b"OBJECT"]),
3438 "-ERR wrong number of arguments for 'object' command\r\n"
3439 );
3440 }
3441
3442 #[test]
3443 fn config_moves_the_ladder_and_object_encoding_agrees() {
3444 let mut f = Fixture::new();
3445 assert_eq!(
3446 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3447 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3448 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3449 );
3450 assert_eq!(
3453 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3454 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3455 );
3456 assert!(
3457 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3458 .starts_with("*8\r\n")
3459 );
3460 assert!(
3461 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3462 .starts_with("*6\r\n")
3463 );
3464
3465 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3466 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3467
3468 assert_eq!(
3469 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3470 "+OK\r\n",
3471 "written under the old name and read back under the new one"
3472 );
3473 assert_eq!(
3474 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3475 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3476 );
3477 assert_eq!(
3478 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3479 "$8\r\nlistpack\r\n",
3480 "the hash that already exists is left exactly where it was"
3481 );
3482 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3483 assert_eq!(
3484 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3485 "$9\r\nhashtable\r\n",
3486 "and the next one built goes straight to a table"
3487 );
3488
3489 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3491 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3492 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3493 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3494 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3495 assert_eq!(
3496 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3497 "$9\r\nhashtable\r\n"
3498 );
3499 }
3500
3501 #[test]
3502 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3503 let mut f = Fixture::new();
3504 assert_eq!(
3505 f.run(&[
3506 b"CONFIG",
3507 b"SET",
3508 b"hash-max-listpack-entries",
3509 b"7",
3510 b"set-max-listpack-entries",
3511 b"abc"
3512 ]),
3513 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3514 );
3515 assert_eq!(
3516 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3517 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3518 "the pair in front of the bad one did not go in"
3519 );
3520 assert_eq!(
3523 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3524 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3525 );
3526 assert_eq!(
3527 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3528 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3529 );
3530 assert_eq!(
3533 f.run(&[
3534 b"CONFIG",
3535 b"SET",
3536 b"set-max-intset-entries",
3537 b"99999999999999999999"
3538 ]),
3539 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3540 );
3541 assert_eq!(
3542 f.run(&[
3543 b"CONFIG",
3544 b"SET",
3545 b"set-max-intset-entries",
3546 b"9223372036854775807"
3547 ]),
3548 "+OK\r\n"
3549 );
3550 }
3551
3552 #[test]
3553 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3554 let mut f = Fixture::new();
3555 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3556 f.run(&[b"SELECT", b"3"]);
3557 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3558 assert_eq!(
3559 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3560 "$9\r\nhashtable\r\n",
3561 "these are one server wide number in Redis, whatever a Keyspace carries"
3562 );
3563 }
3564
3565 #[test]
3566 fn info_reports_the_numbers_it_can_stand_behind() {
3567 let mut f = Fixture::new();
3568 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3569 let all = f.run(&[b"INFO"]);
3570 assert!(all.contains("redis_version:8.8.0"), "{all}");
3571 assert!(
3572 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3573 "{all}"
3574 );
3575 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3576 assert!(all.contains("role:master"), "{all}");
3577 let clients = f.run(&[b"INFO", b"clients"]);
3579 assert!(clients.contains("connected_clients:0"), "{clients}");
3580 assert!(!clients.contains("redis_version"), "{clients}");
3581 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3582 }
3583
3584 #[test]
3591 fn commandstats_is_asked_for_and_replication_is_not() {
3592 let mut f = Fixture::new();
3593 for arg in ["", "all", "default", "everything"] {
3594 let info = if arg.is_empty() {
3595 f.run(&[b"INFO"])
3596 } else {
3597 f.run(&[b"INFO", arg.as_bytes()])
3598 };
3599 assert!(info.contains("redis_version"), "{arg}: {info}");
3600 assert!(info.contains("used_cpu_user"), "{arg}: {info}");
3601 assert!(info.contains("used_memory"), "{arg}: {info}");
3602 assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
3603 let asked = arg == "all" || arg == "everything";
3604 assert_eq!(
3605 info.contains("rejected_calls"),
3606 asked,
3607 "{arg} should{} carry the command counters: {info}",
3608 if asked { "" } else { " not" }
3609 );
3610 }
3611
3612 let cpu = f.run(&[b"INFO", b"cpu"]);
3613 assert!(cpu.contains("used_cpu_user"), "{cpu}");
3614 assert!(!cpu.contains("used_memory"), "{cpu}");
3615
3616 let stats = f.run(&[b"INFO", b"commandSTATS"]);
3619 assert!(!stats.contains("used_memory"), "{stats}");
3620 assert!(stats.contains("rejected_calls"), "{stats}");
3621
3622 let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
3624 assert!(pair.contains("used_cpu_user"), "{pair}");
3625 assert!(!pair.contains("master_repl_offset"), "{pair}");
3626
3627 let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
3628 assert!(with_all.contains("used_memory"), "{with_all}");
3629 assert!(with_all.contains("master_repl_offset"), "{with_all}");
3630 assert!(with_all.contains("rejected_calls"), "{with_all}");
3631 assert_eq!(
3633 with_all.matches("used_cpu_user_children").count(),
3634 1,
3635 "{with_all}"
3636 );
3637
3638 let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
3639 assert!(with_default.contains("used_memory"), "{with_default}");
3640 assert!(
3641 with_default.contains("master_repl_offset"),
3642 "{with_default}"
3643 );
3644 assert!(!with_default.contains("rejected_calls"), "{with_default}");
3645 assert_eq!(
3646 with_default.matches("used_cpu_user_children").count(),
3647 1,
3648 "{with_default}"
3649 );
3650 }
3651
3652 #[test]
3661 fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
3662 let mut f = Fixture::new();
3663 let info = f.run(&[b"INFO", b"memory"]);
3664 for field in [
3665 "total_system_memory:",
3666 "mem_cgroup_limit:",
3667 "mem_limit:",
3668 "mem_budget:",
3669 ] {
3670 assert!(info.contains(field), "no {field} in {info}");
3671 }
3672
3673 let field = |name: &str| -> u64 {
3674 info.lines()
3675 .find_map(|l| l.strip_prefix(name))
3676 .unwrap_or_else(|| panic!("no {name} in {info}"))
3677 .trim()
3678 .parse()
3679 .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
3680 };
3681 let limit = field("mem_limit:");
3682 assert_eq!(field("mem_budget:"), limit / 4, "{info}");
3683 if limit != 0 {
3686 let host = field("total_system_memory:");
3687 let cgroup = field("mem_cgroup_limit:");
3688 assert!(
3689 limit == host || limit == cgroup,
3690 "the limit came from neither number: {info}"
3691 );
3692 }
3693 }
3694
3695 #[test]
3704 fn a_command_counts_what_it_did_separately_from_what_it_refused() {
3705 let mut f = Fixture::new();
3706 f.run(&[b"SET", b"k", b"v"]);
3707 f.run(&[b"SET", b"k", b"w"]);
3708 f.run(&[b"LPUSH", b"k", b"x"]);
3710 f.run(&[b"LPUSH", b"k"]);
3712
3713 let stats = f.run(&[b"INFO", b"commandstats"]);
3714 assert!(
3715 stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
3716 "{stats}"
3717 );
3718 assert!(
3719 stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
3720 "{stats}"
3721 );
3722 assert!(
3723 !stats.contains("cmdstat_zadd"),
3724 "a command nobody has sent has no row: {stats}"
3725 );
3726 }
3727
3728 #[test]
3732 fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3733 let mut f = Fixture::new();
3734 for i in 0..3_000u32 {
3735 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3736 }
3737 for i in 0..1_000u32 {
3738 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3739 }
3740 assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3741 f.advance(100);
3742 assert_eq!(
3743 f.run(&[b"DBSIZE"]),
3744 ":4000\r\n",
3745 "DBSIZE counts records and nothing has read past the dead ones yet"
3746 );
3747
3748 let mut spent = 0;
3750 for _ in 0..2_000 {
3751 spent += f.server.expire_step(4096);
3752 if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3753 break;
3754 }
3755 }
3756 assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3757 assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3758 for i in 0..1_000u32 {
3759 assert_eq!(
3760 f.run(&[b"GET", format!("k{i}").as_bytes()]),
3761 "$1\r\nv\r\n",
3762 "it took a key that had no deadline"
3763 );
3764 }
3765 }
3766
3767 #[test]
3768 fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3769 let mut f = Fixture::new();
3770 for i in 0..2_000u32 {
3771 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3772 }
3773 assert_eq!(f.server.expire_step(4096), 0);
3774 f.run(&[b"SELECT", b"3"]);
3776 f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3777 f.advance(100);
3778 for _ in 0..64 {
3779 f.server.expire_step(4096);
3780 }
3781 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3782 f.run(&[b"SELECT", b"0"]);
3783 assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3784 assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3785 }
3786
3787 #[test]
3790 fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3791 let mut f = Fixture::new();
3792 for i in 0..500u32 {
3793 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3794 }
3795 f.advance(100);
3796 let at = f.server.db(0).clock().now_ms();
3797 f.server.set_clock_ms(at);
3798 assert!(f.server.expire_slice(8) > 0, "the first one works");
3802 for _ in 0..1_000 {
3803 assert_eq!(
3804 f.server.expire_slice(8),
3805 0,
3806 "the millisecond has not moved and neither should this"
3807 );
3808 }
3809 assert!(
3810 f.server.db(0).expires() > 400,
3811 "there is plenty left to take"
3812 );
3813 f.server.set_clock_ms(at + 1);
3814 assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3815 }
3816
3817 #[test]
3820 fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3821 let mut f = Fixture::new();
3822 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3823 assert!(
3824 f.run(&[b"INFO", b"keyspace"])
3825 .contains("db0:keys=3,expires=0"),
3826 "none of them has one yet"
3827 );
3828 f.run(&[b"EXPIRE", b"a", b"1000"]);
3829 f.run(&[b"EXPIRE", b"b", b"1000"]);
3830 let two = f.run(&[b"INFO", b"keyspace"]);
3831 assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3832 f.run(&[b"PERSIST", b"a"]);
3833 f.run(&[b"DEL", b"b"]);
3834 let none = f.run(&[b"INFO", b"keyspace"]);
3835 assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3836
3837 f.run(&[b"SELECT", b"1"]);
3839 f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3840 let both = f.run(&[b"INFO", b"keyspace"]);
3841 assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3842 assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3843 }
3844
3845 #[cfg(unix)]
3846 #[test]
3847 fn info_cpu_reports_processor_time_that_was_really_measured() {
3848 let mut f = Fixture::new();
3849 let cpu = f.run(&[b"INFO", b"cpu"]);
3850 assert!(cpu.contains("# CPU"), "{cpu}");
3851 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3853 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3854 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3855 assert!(!cpu.contains("redis_version"), "{cpu}");
3856
3857 let before = used_cpu_user(&cpu);
3861 let mut n = 0u64;
3862 let mut rounds = 0;
3863 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3864 for i in 0..1_000_000u64 {
3865 n = n.wrapping_add(i.wrapping_mul(i));
3866 }
3867 rounds += 1;
3868 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
3872 }
3873 }
3874
3875 #[cfg(unix)]
3877 fn used_cpu_user(info: &str) -> f64 {
3878 info.lines()
3879 .find_map(|l| l.strip_prefix("used_cpu_user:"))
3880 .expect("no used_cpu_user in the reply")
3881 .trim()
3882 .parse()
3883 .expect("used_cpu_user is not a number")
3884 }
3885
3886 #[test]
3892 fn a_command_that_fails_leaves_nothing_half_written() {
3893 let mut f = Fixture::new();
3894 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
3895 assert_eq!(reply, "-ERR offset is out of range\r\n");
3896 assert!(!reply.contains(':'), "no integer went out in front of it");
3897 }
3898
3899 #[test]
3900 fn quit_answers_first_and_closes_after() {
3901 let mut f = Fixture::new();
3902 let (flow, reply) = f.flow(&[b"QUIT"]);
3903 assert_eq!(reply, "+OK\r\n");
3904 assert_eq!(flow, Flow::Close);
3905 }
3906
3907 #[test]
3914 fn shutdown_writes_nothing_and_sets_the_flag() {
3915 let mut f = Fixture::new();
3916 assert!(!f.server.stopping(), "nobody has asked yet");
3917
3918 let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
3919 assert_eq!(reply, "");
3920 assert_eq!(flow, Flow::Close);
3921 assert!(f.server.stopping());
3922 }
3923
3924 #[test]
3932 fn shutdown_takes_the_flags_redis_takes() {
3933 for flags in [
3934 &[b"NOSAVE".as_slice()][..],
3935 &[b"SAVE"],
3936 &[b"NOW"],
3937 &[b"FORCE"],
3938 &[b"nosave"],
3939 &[b"NOW", b"NOW"],
3940 &[b"SAVE", b"SAVE"],
3941 &[b"NOSAVE", b"NOW", b"FORCE"],
3942 ] {
3943 let mut f = Fixture::new();
3944 let mut parts = vec![b"SHUTDOWN".as_slice()];
3945 parts.extend_from_slice(flags);
3946 let (flow, reply) = f.flow(&parts);
3947 assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
3948 assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
3949 assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
3950 }
3951
3952 for flags in [
3953 &[b"BOGUS".as_slice()][..],
3954 &[b"SAVE", b"NOSAVE"],
3955 &[b"NOSAVE", b"SAVE"],
3956 &[b"ABORT", b"NOW"],
3957 &[b"NOSAVE", b"ABORT"],
3958 &[b"NOW", b"FORCE", b"ABORT"],
3959 ] {
3960 let mut f = Fixture::new();
3961 let mut parts = vec![b"SHUTDOWN".as_slice()];
3962 parts.extend_from_slice(flags);
3963 assert_eq!(
3964 f.run(&parts),
3965 "-ERR syntax error\r\n",
3966 "SHUTDOWN {flags:?} was accepted"
3967 );
3968 assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
3969 }
3970 }
3971
3972 #[test]
3980 fn shutdown_abort_never_has_anything_to_abort() {
3981 let mut f = Fixture::new();
3982 for parts in [
3983 &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
3984 &[b"SHUTDOWN", b"ABORT", b"ABORT"],
3985 ] {
3986 assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
3987 assert!(!f.server.stopping(), "an abort stopped the server");
3988 }
3989 }
3990
3991 struct Backups {
3999 f: Fixture,
4000 dir: PathBuf,
4001 }
4002
4003 impl Backups {
4004 fn new(name: &str) -> Backups {
4005 let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4006 let _ = std::fs::remove_dir_all(&dir);
4007 std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4008 let mut f = Fixture::new();
4009 f.server.set_dir(dir.clone());
4010 Backups { f, dir }
4011 }
4012
4013 fn run(&mut self, parts: &[&[u8]]) -> String {
4014 self.f.run(parts)
4015 }
4016
4017 fn files(&self) -> Vec<String> {
4019 let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4020 Ok(entries) => entries
4021 .filter_map(|e| e.ok())
4022 .map(|e| e.file_name().to_string_lossy().into_owned())
4023 .collect(),
4024 Err(_) => Vec::new(),
4025 };
4026 names.sort();
4027 names
4028 }
4029
4030 fn read(&self, name: &str) -> Vec<u8> {
4031 std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4032 }
4033 }
4034
4035 impl Drop for Backups {
4036 fn drop(&mut self) {
4037 let _ = std::fs::remove_dir_all(&self.dir);
4038 }
4039 }
4040
4041 #[test]
4044 fn backup_walks_the_states_the_reference_walks() {
4045 let mut b = Backups::new("states");
4046 let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4047
4048 assert!(status(&mut b).contains("idle"));
4049 assert!(b.files().is_empty(), "an idle server has written a backup");
4050
4051 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4052 assert!(status(&mut b).contains("incrementing"));
4053 assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4054
4055 assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4056 assert!(status(&mut b).contains("sealed"));
4057 assert_eq!(
4058 b.files(),
4059 [
4060 "appendonly.aof.1.base.rdb",
4061 "appendonly.aof.1.incr.aof",
4062 "appendonly.aof.manifest",
4063 ]
4064 );
4065
4066 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4067 assert!(status(&mut b).contains("idle"));
4068 assert!(b.files().is_empty(), "cleanup left something behind");
4069 }
4070
4071 #[test]
4073 fn backup_refuses_the_moves_the_reference_refuses() {
4074 let mut b = Backups::new("refusals");
4075
4076 assert_eq!(
4077 b.run(&[b"BACKUP", b"SEAL"]),
4078 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4079 );
4080 assert_eq!(
4081 b.run(&[b"BACKUP", b"ABORT"]),
4082 "-ERR No backup in progress\r\n"
4083 );
4084 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4087
4088 b.run(&[b"BACKUP", b"START"]);
4089 assert_eq!(
4090 b.run(&[b"BACKUP", b"START"]),
4091 "-ERR A backup is already in progress, ABORT it first\r\n"
4092 );
4093 assert_eq!(
4094 b.run(&[b"BACKUP", b"CLEANUP"]),
4095 "-ERR Backup is in progress\r\n"
4096 );
4097
4098 b.run(&[b"BACKUP", b"SEAL"]);
4099 assert_eq!(
4100 b.run(&[b"BACKUP", b"START"]),
4101 "-ERR A sealed backup exists, CLEANUP it first\r\n"
4102 );
4103 assert_eq!(
4104 b.run(&[b"BACKUP", b"SEAL"]),
4105 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4106 );
4107 assert_eq!(
4108 b.run(&[b"BACKUP", b"ABORT"]),
4109 "-ERR No backup in progress\r\n"
4110 );
4111 }
4112
4113 #[test]
4120 fn backup_abort_removes_the_file_and_says_who_did_it() {
4121 let mut b = Backups::new("abort");
4122 b.run(&[b"BACKUP", b"START"]);
4123 assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4124
4125 let status = b.run(&[b"BACKUP", b"STATUS"]);
4126 assert!(status.contains("failed"), "{status}");
4127 assert!(status.contains("aborted by user"), "{status}");
4128 assert!(b.files().is_empty(), "abort left the base file behind");
4129 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4130
4131 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4133 assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4134 let status = b.run(&[b"BACKUP", b"STATUS"]);
4135 assert!(status.contains("incrementing"), "{status}");
4136 assert!(!status.contains("aborted"), "the old error was kept");
4137 }
4138
4139 #[test]
4141 fn backup_list_names_the_files_that_are_pinned_so_far() {
4142 let mut b = Backups::new("list");
4143 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4144
4145 b.run(&[b"BACKUP", b"START"]);
4146 let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4147 let base = base.to_string_lossy().into_owned();
4148 assert_eq!(
4149 b.run(&[b"BACKUP", b"LIST"]),
4150 format!("*1\r\n${}\r\n{base}\r\n", base.len())
4151 );
4152
4153 b.run(&[b"BACKUP", b"SEAL"]);
4154 let listed = b.run(&[b"BACKUP", b"LIST"]);
4155 assert!(listed.starts_with("*3\r\n"), "{listed}");
4156 let names: Vec<&str> = listed
4159 .lines()
4160 .filter(|l| l.starts_with('/') || l.contains(":\\"))
4161 .collect();
4162 assert_eq!(names.len(), 3, "{listed}");
4163 assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4164 assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4165 assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4166 }
4167
4168 #[test]
4176 fn a_backup_holds_the_dataset_as_it_was_at_start() {
4177 let mut b = Backups::new("contents");
4178 b.run(&[b"SET", b"bk", b"v1"]);
4179 b.run(&[b"BACKUP", b"START"]);
4180 b.run(&[b"SET", b"bk", b"v2"]);
4181 b.run(&[b"BACKUP", b"SEAL"]);
4182
4183 let base = b.read("appendonly.aof.1.base.rdb");
4184 assert!(base.starts_with(b"REDIS"), "not an RDB file");
4185 assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4186 assert!(
4187 !base.windows(2).any(|w| w == b"v2"),
4188 "the base file moved on after START"
4189 );
4190 let at = base
4195 .windows(8)
4196 .position(|w| w == b"aof-base")
4197 .expect("no aof-base aux field");
4198 assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4199
4200 assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4201 assert_eq!(
4202 String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4203 "file appendonly.aof.1.base.rdb seq 1 type b\n\
4204 file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4205 );
4206 }
4207
4208 #[test]
4211 fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4212 let mut b = Backups::new("status");
4213 b.f.server.set_clock_ms(1_700_000_000_000);
4214
4215 assert_eq!(
4216 b.run(&[b"BACKUP", b"STATUS"]),
4217 "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4218 $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4219 );
4220
4221 b.f.out = Out::new(Proto::Resp3);
4222 b.run(&[b"BACKUP", b"START"]);
4223 assert_eq!(
4224 b.run(&[b"BACKUP", b"STATUS"]),
4225 "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4226 $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4227 );
4228
4229 b.run(&[b"BACKUP", b"SEAL"]);
4230 let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4231 assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4232 }
4233
4234 #[test]
4237 fn a_sealed_backup_is_swept_away_after_the_timeout() {
4238 let mut b = Backups::new("ttl");
4239 b.f.server.set_clock_ms(1_000_000);
4240 assert_eq!(
4241 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4242 "+OK\r\n"
4243 );
4244 b.run(&[b"BACKUP", b"START"]);
4245 b.run(&[b"BACKUP", b"SEAL"]);
4246
4247 b.f.server.set_clock_ms(1_000_000 + 59_000);
4249 b.f.server.backup_expire();
4250 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4251 assert_eq!(b.files().len(), 3);
4252
4253 b.f.server.set_clock_ms(1_000_000 + 60_000);
4254 b.f.server.backup_expire();
4255 let status = b.run(&[b"BACKUP", b"STATUS"]);
4256 assert!(status.contains("idle"), "{status}");
4257 assert!(b.files().is_empty(), "the timeout left the files behind");
4258
4259 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4261 b.run(&[b"BACKUP", b"START"]);
4262 b.run(&[b"BACKUP", b"SEAL"]);
4263 b.f.server.set_clock_ms(9_000_000_000);
4264 b.f.server.backup_expire();
4265 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4266 }
4267
4268 #[test]
4271 fn the_backup_settings_behave_the_way_the_reference_does() {
4272 let mut b = Backups::new("config");
4273 let dir = b.dir.to_string_lossy().into_owned();
4274
4275 assert_eq!(
4276 b.run(&[b"CONFIG", b"GET", b"dir"]),
4277 format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4278 );
4279 assert_eq!(
4280 b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4281 "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4282 );
4283 assert_eq!(
4284 b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4285 "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4286 );
4287
4288 assert_eq!(
4291 b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4292 "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4293 );
4294 assert_eq!(
4295 b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4296 "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4297 );
4298 assert!(
4299 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4300 .contains("argument couldn't be parsed into an integer")
4301 );
4302 assert!(
4303 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4304 .contains("argument must be between 0 and 9223372036854775807 inclusive")
4305 );
4306 }
4307
4308 #[test]
4310 fn backup_help_is_the_text_the_reference_sends() {
4311 let mut f = Fixture::new();
4312 let help = f.run(&[b"BACKUP", b"HELP"]);
4313 assert!(help.starts_with("*17\r\n"), "{help}");
4314 assert!(
4315 help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4316 );
4317 assert!(help.contains("+ Start a new backup into the configured 'backupdirname'.\r\n"));
4318 assert!(help.contains("+ Freeze the current backup (BASE + INCR + manifest).\r\n"));
4319 assert!(help.contains("+ Return this help.\r\n+HELP\r\n+ Print this help.\r\n"));
4320 }
4321
4322 #[test]
4330 fn backup_refuses_what_it_cannot_read() {
4331 let mut f = Fixture::new();
4332 assert_eq!(
4333 f.run(&[b"BACKUP"]),
4334 "-ERR wrong number of arguments for 'backup' command\r\n"
4335 );
4336 assert_eq!(
4337 f.run(&[b"BACKUP", b"START", b"x"]),
4338 "-ERR wrong number of arguments for 'backup' command\r\n"
4339 );
4340 assert_eq!(
4341 f.run(&[b"BACKUP", b"NOPE"]),
4342 "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4343 );
4344 }
4345
4346 #[test]
4347 fn the_command_counter_counts_every_command_including_the_bad_ones() {
4348 let mut f = Fixture::new();
4349 f.run(&[b"PING"]);
4350 f.run(&[b"NOPE"]);
4351 f.run(&[b"GET"]);
4352 assert_eq!(f.server.stats.commands, 3);
4353 }
4354
4355 #[test]
4356 fn a_set_goes_from_bytes_to_bytes() {
4357 let mut f = Fixture::new();
4358 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4359 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4360 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4361 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4362 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4363 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4364 assert_eq!(
4365 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4366 "*3\r\n:1\r\n:0\r\n:1\r\n"
4367 );
4368 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4369 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4370 }
4371
4372 #[test]
4373 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4374 let mut f = Fixture::new();
4375 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4376 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4377 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4378 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4379 assert_eq!(
4380 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4381 "*2\r\n:0\r\n:0\r\n"
4382 );
4383 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4384 }
4385
4386 #[test]
4387 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
4388 let mut f = Fixture::new();
4392 f.run(&[b"SADD", b"s", b"one"]);
4393 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
4394
4395 f.run(&[b"HELLO", b"3"]);
4396 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
4397 }
4398
4399 #[test]
4400 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
4401 let mut f = Fixture::new();
4404 f.run(&[b"SADD", b"s", b"42"]);
4405 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
4406 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
4407 assert_eq!(
4408 f.run(&[b"SISMEMBER", b"s", b"042"]),
4409 ":0\r\n",
4410 "the member is the bytes and not the number they parse to"
4411 );
4412 }
4413
4414 #[test]
4415 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
4416 let mut f = Fixture::new();
4417 f.run(&[b"SET", b"str", b"v"]);
4418 f.run(&[b"SADD", b"set", b"a"]);
4419
4420 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4421 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
4422 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
4423 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
4424 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
4425 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
4426 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
4427 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
4428 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
4429
4430 assert_eq!(
4433 f.run(&[b"MGET", b"str", b"set", b"nope"]),
4434 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
4435 );
4436 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
4438 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
4439 }
4440
4441 #[test]
4442 fn a_wrongtype_leaves_nothing_half_written() {
4443 let mut f = Fixture::new();
4447 f.run(&[b"SET", b"k", b"v"]);
4448 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
4449 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
4450 assert!(!reply.contains('*'), "an array header went out in front");
4451 }
4452
4453 #[test]
4454 fn emptying_a_set_takes_the_key_with_it() {
4455 let mut f = Fixture::new();
4456 f.run(&[b"SADD", b"s", b"a", b"b"]);
4457 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4458 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
4459 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4460 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
4461 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4462 }
4463
4464 fn split_scan(reply: &str) -> (String, Vec<String>) {
4470 let mut lines = reply.split("\r\n");
4471 assert_eq!(lines.next(), Some("*2"), "got {reply}");
4472 lines.next().expect("the cursor header");
4473 let cursor = lines.next().expect("the cursor").to_owned();
4474 let header = lines.next().expect("the member header");
4475 let n: usize = header[1..].parse().expect("a member count");
4476 let mut members = Vec::with_capacity(n);
4477 for _ in 0..n {
4478 lines.next().expect("a member header");
4479 members.push(lines.next().expect("a member").to_owned());
4480 }
4481 (cursor, members)
4482 }
4483
4484 #[test]
4485 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
4486 let mut f = Fixture::new();
4487 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
4488
4489 let one = f.run(&[b"SPOP", b"s"]);
4490 assert!(
4491 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
4492 "got {one}"
4493 );
4494 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4495
4496 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
4498 assert!(rest.starts_with("*3\r\n"), "got {rest}");
4499 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4500 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
4502 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
4503 }
4504
4505 #[test]
4506 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
4507 let mut f = Fixture::new();
4512 f.run(&[b"HELLO", b"3"]);
4513 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
4514
4515 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
4516 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
4518
4519 f.run(&[b"SADD", b"one", b"z"]);
4523 assert_eq!(
4524 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
4525 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
4526 );
4527 }
4528
4529 #[test]
4530 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
4531 let mut f = Fixture::new();
4532 f.run(&[b"SADD", b"s", b"only"]);
4533 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4534 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4535 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
4536
4537 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
4538 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
4541 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
4542 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
4544 }
4545
4546 #[test]
4547 fn a_pop_count_that_is_not_a_positive_number_says_so() {
4548 let mut f = Fixture::new();
4549 f.run(&[b"SADD", b"s", b"a"]);
4550 let bad = "-ERR value is out of range, must be positive\r\n";
4551 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
4552 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
4553 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
4554 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
4556 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
4557 }
4558
4559 #[test]
4560 fn a_scan_walks_a_set_of_any_size_exactly_once() {
4561 let mut f = Fixture::new();
4562 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
4563 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
4564 .into_iter()
4565 .chain(members.iter().map(Vec::as_slice))
4566 .collect();
4567 f.run(&args);
4568
4569 let mut seen = Vec::new();
4570 let mut cursor = "0".to_owned();
4571 loop {
4572 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
4573 let (next, got) = split_scan(&reply);
4574 seen.extend(got);
4575 cursor = next;
4576 if cursor == "0" {
4577 break;
4578 }
4579 }
4580 seen.sort();
4581 seen.dedup();
4582 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
4583
4584 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
4587 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
4588 assert_eq!(cursor, "0");
4589 assert_eq!(got.len(), 3);
4590 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
4592 }
4593
4594 #[test]
4595 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
4596 let mut f = Fixture::new();
4597 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
4598
4599 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
4600 let mut got = got;
4601 got.sort();
4602 assert_eq!(got, ["aa", "ab"]);
4603
4604 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
4607 let mut got = got;
4608 got.sort();
4609 assert_eq!(got, ["12", "13"]);
4610
4611 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
4612 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
4613 assert_eq!(
4614 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
4615 "-ERR syntax error\r\n"
4616 );
4617 assert_eq!(
4620 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
4621 "-ERR syntax error\r\n"
4622 );
4623 }
4624
4625 #[test]
4626 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
4627 let mut f = Fixture::new();
4628 f.run(&[b"SADD", b"src", b"a", b"b"]);
4629 f.run(&[b"SADD", b"dst", b"c"]);
4630
4631 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
4632 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
4633 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
4634 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
4636 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
4637
4638 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
4641 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
4642 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
4643 }
4644
4645 #[test]
4646 fn moving_checks_the_types_in_the_order_redis_checks_them() {
4647 let mut f = Fixture::new();
4651 f.run(&[b"SET", b"str", b"v"]);
4652 f.run(&[b"SADD", b"set", b"a"]);
4653
4654 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4655 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
4656 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
4657 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
4658 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
4659 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
4660 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
4661 assert_eq!(
4662 f.run(&[b"SISMEMBER", b"set", b"a"]),
4663 ":1\r\n",
4664 "and none of that moved anything"
4665 );
4666 }
4667
4668 #[test]
4669 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4670 let mut f = Fixture::new();
4673 f.run(&[b"SADD", b"s", b"a"]);
4674 for bad in [
4675 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
4676 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
4677 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
4678 ] {
4679 let reply = f.run(bad);
4680 assert!(reply.starts_with("-ERR"), "got {reply}");
4681 assert!(!reply.contains('*'), "an array header went out in front");
4682 }
4683 }
4684
4685 #[test]
4686 fn a_hash_writes_reads_and_deletes_its_fields() {
4687 let mut f = Fixture::new();
4688 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
4689 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
4690 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4691 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
4692 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
4693 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
4694 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
4695 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
4696 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
4697 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
4698
4699 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
4702
4703 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
4704 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
4705 assert_eq!(
4706 f.run(&[b"EXISTS", b"h"]),
4707 ":0\r\n",
4708 "and losing the last field lost the key"
4709 );
4710 }
4711
4712 #[test]
4713 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
4714 let mut f = Fixture::new();
4715 f.run(&[b"HSET", b"h", b"a", b"1"]);
4716 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
4717 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
4718 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
4719 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
4720 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
4721
4722 f.run(&[b"HELLO", b"3"]);
4723 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
4724 assert_eq!(
4725 f.run(&[b"HGETALL", b"nokey"]),
4726 "%0\r\n",
4727 "a missing key is the empty hash and never a nil"
4728 );
4729 assert_eq!(
4730 f.run(&[b"HKEYS", b"h"]),
4731 "*1\r\n$1\r\na\r\n",
4732 "and the two that answer one side stay arrays"
4733 );
4734 }
4735
4736 #[test]
4737 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
4738 let mut f = Fixture::new();
4739 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
4740 assert_eq!(
4741 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
4742 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
4743 "the reply is positional, so b is a nil and not a gap"
4744 );
4745 assert_eq!(
4746 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
4747 "*2\r\n$-1\r\n$-1\r\n",
4748 "and a missing key is all nils rather than an empty array"
4749 );
4750
4751 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
4752 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
4753 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4754 }
4755
4756 #[test]
4757 fn a_hash_counts_up_and_says_so_when_it_cannot() {
4758 let mut f = Fixture::new();
4759 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
4760 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
4761 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
4762 assert_eq!(
4763 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
4764 "$4\r\n10.5\r\n",
4765 "a bulk string and not a double, on both protocols"
4766 );
4767
4768 f.run(&[b"HSET", b"h", b"s", b"words"]);
4769 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
4770 assert!(
4771 bad.starts_with("-ERR hash value is not an integer"),
4772 "{bad}"
4773 );
4774 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
4775 assert!(
4776 bad.starts_with("-ERR value is not an integer"),
4777 "a bad argument is not yet a hash value, {bad}"
4778 );
4779 assert_eq!(
4780 f.run(&[b"HGET", b"h", b"s"]),
4781 "$5\r\nwords\r\n",
4782 "and neither of them wrote anything"
4783 );
4784 }
4785
4786 #[test]
4787 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
4788 let mut f = Fixture::new();
4789 for i in 0..500 {
4790 let field = format!("field-{i}");
4791 let value = format!("value-{i}");
4792 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
4793 }
4794
4795 let mut seen: Vec<String> = Vec::new();
4796 let mut cursor = "0".to_owned();
4797 loop {
4798 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
4799 let (next, items) = scan_reply(&reply);
4800 assert_eq!(items.len() % 2, 0, "a pair went out half written");
4801 for pair in items.chunks(2) {
4802 assert_eq!(
4803 pair[0].strip_prefix("field-"),
4804 pair[1].strip_prefix("value-"),
4805 "a field came back with someone else's value"
4806 );
4807 seen.push(pair[0].clone());
4808 }
4809 cursor = next;
4810 if cursor == "0" {
4811 break;
4812 }
4813 }
4814 seen.sort();
4815 seen.dedup();
4816 assert_eq!(seen.len(), 500, "every field once and only once");
4817
4818 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
4819 assert!(
4820 items.iter().all(|s| s.starts_with("field-")),
4821 "NOVALUES still sent the values"
4822 );
4823
4824 let (_, one) = scan_reply(&f.run(&[
4825 b"HSCAN",
4826 b"h",
4827 b"0",
4828 b"MATCH",
4829 b"field-499",
4830 b"COUNT",
4831 b"1000",
4832 ]));
4833 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
4834 }
4835
4836 #[test]
4837 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
4838 let mut f = Fixture::new();
4839 f.run(&[b"HSET", b"h", b"a", b"1"]);
4840 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
4841 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
4842 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
4843 assert_eq!(
4844 f.run(&[b"HRANDFIELD", b"h", b"3"]),
4845 "*1\r\n$1\r\na\r\n",
4846 "a positive count is capped at the size of the hash"
4847 );
4848 assert_eq!(
4849 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
4850 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
4851 "and a negative one repeats itself"
4852 );
4853 assert_eq!(
4854 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4855 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4856 "flat on RESP2"
4857 );
4858
4859 f.run(&[b"HELLO", b"3"]);
4860 assert_eq!(
4861 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4862 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4863 "and nested on RESP3, but still an array and never a map"
4864 );
4865 }
4866
4867 #[test]
4868 fn every_hash_command_says_wrongtype_and_writes_nothing() {
4869 let mut f = Fixture::new();
4870 f.run(&[b"SET", b"str", b"v"]);
4871 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4872
4873 for cmd in [
4874 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
4875 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
4876 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
4877 &[b"HGET".as_slice(), b"str", b"f"][..],
4878 &[b"HMGET".as_slice(), b"str", b"f"][..],
4879 &[b"HDEL".as_slice(), b"str", b"f"][..],
4880 &[b"HLEN".as_slice(), b"str"][..],
4881 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
4882 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
4883 &[b"HGETALL".as_slice(), b"str"][..],
4884 &[b"HKEYS".as_slice(), b"str"][..],
4885 &[b"HVALS".as_slice(), b"str"][..],
4886 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
4887 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
4888 &[b"HRANDFIELD".as_slice(), b"str"][..],
4889 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
4890 &[b"HSCAN".as_slice(), b"str", b"0"][..],
4891 ] {
4892 let reply = f.run(cmd);
4893 assert_eq!(reply, wrong, "{:?}", cmd[0]);
4894 }
4895 assert_eq!(
4896 f.run(&[b"GET", b"str"]),
4897 "$1\r\nv\r\n",
4898 "and none of them touched the value"
4899 );
4900 }
4901
4902 #[test]
4903 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4904 let mut f = Fixture::new();
4905 f.run(&[b"HSET", b"h", b"f", b"v"]);
4906 for bad in [
4907 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
4908 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
4909 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
4910 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
4911 ] {
4912 let reply = f.run(bad);
4913 assert!(reply.starts_with("-ERR"), "got {reply}");
4914 assert!(!reply.contains('*'), "an array header went out in front");
4915 }
4916 }
4917
4918 #[test]
4919 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
4920 let mut f = Fixture::new();
4921 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4922 assert_eq!(
4923 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
4924 "*1\r\n:1\r\n"
4925 );
4926 assert_eq!(
4927 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
4928 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
4929 "one answer per field, and the two sentinels are TTL's own"
4930 );
4931
4932 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
4935 assert!((99_000..=100_000).contains(&ms), "got {ms}");
4936 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
4937 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
4938 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
4939 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
4940
4941 assert_eq!(
4942 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
4943 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
4944 "one for the deadline taken off, and it does not say what it was"
4945 );
4946 assert_eq!(
4947 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4948 "*1\r\n:-1\r\n"
4949 );
4950 assert_eq!(
4951 f.run(&[b"HGET", b"h", b"a"]),
4952 "$1\r\n1\r\n",
4953 "and the field is still there with the value it had"
4954 );
4955 }
4956
4957 #[test]
4958 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
4959 let mut f = Fixture::new();
4960 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4961 assert_eq!(
4962 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
4963 "*1\r\n:2\r\n",
4964 "two, and not one, because nothing was stored"
4965 );
4966 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
4967 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4968
4969 assert_eq!(
4970 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
4971 "*1\r\n:2\r\n"
4972 );
4973 assert_eq!(
4974 f.run(&[b"EXISTS", b"h"]),
4975 ":0\r\n",
4976 "and the last field going took the key with it"
4977 );
4978
4979 f.run(&[b"HSET", b"h", b"a", b"1"]);
4982 assert_eq!(
4983 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
4984 "*1\r\n:2\r\n"
4985 );
4986 }
4987
4988 #[test]
4989 fn a_field_is_gone_once_its_moment_passes() {
4990 let mut f = Fixture::new();
4991 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4992 assert_eq!(
4993 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
4994 "*1\r\n:1\r\n"
4995 );
4996 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
4997
4998 f.server.db(0).clock_mut().advance(60);
5002 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5003 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5004 assert_eq!(
5005 f.run(&[b"HGETALL", b"h"]),
5006 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5007 "and the walks do not hand back a field that has expired"
5008 );
5009 }
5010
5011 #[test]
5012 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5013 let mut f = Fixture::new();
5014 for cmd in [
5015 &[
5016 b"HEXPIRE".as_slice(),
5017 b"nokey",
5018 b"100",
5019 b"FIELDS",
5020 b"2",
5021 b"a",
5022 b"b",
5023 ][..],
5024 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5025 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5026 &[
5027 b"HEXPIRETIME".as_slice(),
5028 b"nokey",
5029 b"FIELDS",
5030 b"2",
5031 b"a",
5032 b"b",
5033 ][..],
5034 &[
5035 b"HPERSIST".as_slice(),
5036 b"nokey",
5037 b"FIELDS",
5038 b"2",
5039 b"a",
5040 b"b",
5041 ][..],
5042 ] {
5043 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5044 }
5045 }
5046
5047 #[test]
5048 fn writing_a_field_clears_the_deadline_that_was_on_it() {
5049 let mut f = Fixture::new();
5050 f.run(&[b"HSET", b"h", b"a", b"1"]);
5051 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5052 f.run(&[b"HSET", b"h", b"a", b"2"]);
5053 assert_eq!(
5054 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5055 "*1\r\n:-1\r\n",
5056 "Redis has done this since 7.4, and it is why HGETEX exists"
5057 );
5058 }
5059
5060 #[test]
5061 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5062 let mut f = Fixture::new();
5063 f.run(&[b"HSET", b"h", b"a", b"1"]);
5064 assert_eq!(
5065 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5066 "*1\r\n:0\r\n",
5067 "XX on a field with no deadline changes nothing"
5068 );
5069 assert_eq!(
5070 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5071 "*1\r\n:1\r\n"
5072 );
5073 assert_eq!(
5074 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5075 "*1\r\n:0\r\n",
5076 "and NX will not move one that is already there"
5077 );
5078 assert_eq!(
5079 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5080 "*1\r\n:0\r\n"
5081 );
5082 assert_eq!(
5083 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5084 "*1\r\n:1\r\n"
5085 );
5086 assert_eq!(
5087 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5088 "*1\r\n:1\r\n"
5089 );
5090 assert_eq!(
5091 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5092 "*1\r\n:50\r\n"
5093 );
5094 }
5095
5096 #[test]
5097 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5098 let mut f = Fixture::new();
5099 f.run(&[b"HSET", b"h", b"a", b"1"]);
5100 for (bad, want) in [
5101 (
5102 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5103 "-ERR invalid expire time, must be >= 0",
5104 ),
5105 (
5106 &[
5107 b"HEXPIRE".as_slice(),
5108 b"h",
5109 b"9999999999999999",
5110 b"FIELDS",
5111 b"1",
5112 b"a",
5113 ][..],
5114 "-ERR invalid expire time in 'hexpire' command",
5115 ),
5116 (
5117 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5118 "-ERR wrong number of arguments for 'hexpire' command",
5119 ),
5120 (
5121 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5122 "-ERR Parameter `numFields` should be greater than 0",
5123 ),
5124 (
5125 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5126 "-ERR wrong number of arguments",
5127 ),
5128 (
5129 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5130 "-ERR wrong number of arguments",
5131 ),
5132 ] {
5133 let reply = f.run(bad);
5134 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5135 assert!(!reply.contains('*'), "an array header went out in front");
5136 }
5137 assert_eq!(
5138 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5139 "*1\r\n:-1\r\n",
5140 "and not one of them put a deadline on anything"
5141 );
5142 }
5143
5144 #[test]
5145 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5146 let mut f = Fixture::new();
5147 f.run(&[b"SET", b"str", b"v"]);
5148 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5149
5150 for cmd in [
5151 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5152 &[
5153 b"HPEXPIRE".as_slice(),
5154 b"str",
5155 b"100",
5156 b"FIELDS",
5157 b"1",
5158 b"f",
5159 ][..],
5160 &[
5161 b"HEXPIREAT".as_slice(),
5162 b"str",
5163 b"9999999999",
5164 b"FIELDS",
5165 b"1",
5166 b"f",
5167 ][..],
5168 &[
5169 b"HPEXPIREAT".as_slice(),
5170 b"str",
5171 b"9999999999999",
5172 b"FIELDS",
5173 b"1",
5174 b"f",
5175 ][..],
5176 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5177 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5178 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5179 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5180 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5181 ] {
5182 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5183 }
5184 assert_eq!(
5185 f.run(&[b"GET", b"str"]),
5186 "$1\r\nv\r\n",
5187 "and none of them touched the value"
5188 );
5189 }
5190
5191 #[test]
5192 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5193 let mut f = Fixture::new();
5194 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5195 assert_eq!(
5196 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5197 "*2\r\n$1\r\n1\r\n$-1\r\n",
5198 "positional, so the field that was not there is a nil in its place"
5199 );
5200 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5201 assert_eq!(
5202 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5203 "*1\r\n$-1\r\n"
5204 );
5205 assert_eq!(
5206 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5207 "*1\r\n$1\r\n2\r\n"
5208 );
5209 assert_eq!(
5210 f.run(&[b"EXISTS", b"h"]),
5211 ":0\r\n",
5212 "and the last field took the key"
5213 );
5214 }
5215
5216 #[test]
5217 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5218 let mut f = Fixture::new();
5219 f.run(&[b"HSET", b"h", b"a", b"1"]);
5220 assert_eq!(
5221 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5222 "*1\r\n$1\r\n1\r\n"
5223 );
5224 assert_eq!(
5225 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5226 "*1\r\n:-1\r\n",
5227 "no option means leave it alone, which is the one place this is not GETEX"
5228 );
5229
5230 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5231 assert_eq!(
5232 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5233 "*1\r\n:100\r\n"
5234 );
5235 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5236 assert_eq!(
5237 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5238 "*1\r\n:100\r\n",
5239 "and a plain read really does leave it alone"
5240 );
5241 assert_eq!(
5242 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5243 "*1\r\n$1\r\n1\r\n"
5244 );
5245 assert_eq!(
5246 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5247 "*1\r\n:-1\r\n"
5248 );
5249
5250 assert_eq!(
5251 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5252 "*1\r\n$1\r\n1\r\n",
5253 "the value goes out before the deadline that has already gone is applied"
5254 );
5255 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5256 assert_eq!(
5257 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5258 "*1\r\n$-1\r\n"
5259 );
5260 }
5261
5262 #[test]
5263 fn hsetex_writes_all_of_it_or_none_of_it() {
5264 let mut f = Fixture::new();
5265 assert_eq!(
5266 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5267 ":1\r\n"
5268 );
5269 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5270 assert_eq!(
5271 f.run(&[
5272 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5273 ]),
5274 ":0\r\n",
5275 "FNX wants every field named to be missing"
5276 );
5277 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5278 assert_eq!(
5279 f.run(&[b"HEXISTS", b"h", b"new"]),
5280 ":0\r\n",
5281 "and none of the list was written"
5282 );
5283 assert_eq!(
5284 f.run(&[
5285 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5286 ]),
5287 ":0\r\n",
5288 "and FXX wants every one of them to be there"
5289 );
5290 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5291 assert_eq!(
5292 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5293 ":1\r\n"
5294 );
5295 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5296
5297 assert_eq!(
5298 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5299 ":0\r\n"
5300 );
5301 assert_eq!(
5302 f.run(&[b"EXISTS", b"gone"]),
5303 ":0\r\n",
5304 "a key with no fields cannot meet FXX and is not created trying"
5305 );
5306 }
5307
5308 #[test]
5309 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5310 let mut f = Fixture::new();
5311 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5312 assert_eq!(
5313 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5314 "*1\r\n:100\r\n"
5315 );
5316
5317 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5318 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5319 assert_eq!(
5320 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5321 "*1\r\n:100\r\n",
5322 "KEEPTTL put back what the write cleared"
5323 );
5324
5325 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5326 assert_eq!(
5327 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5328 "*1\r\n:-1\r\n",
5329 "and without it a write clears the deadline the way HSET does"
5330 );
5331
5332 assert_eq!(
5335 f.run(&[
5336 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5337 ]),
5338 ":1\r\n"
5339 );
5340 assert_eq!(
5341 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5342 "*1\r\n:100\r\n"
5343 );
5344
5345 assert_eq!(
5346 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5347 ":1\r\n",
5348 "written, and not the separate code the HEXPIRE family has for this"
5349 );
5350 assert_eq!(
5351 f.run(&[b"EXISTS", b"h"]),
5352 ":0\r\n",
5353 "and storing it and then removing it emptied the hash"
5354 );
5355 }
5356
5357 #[test]
5358 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5359 let mut f = Fixture::new();
5360 f.run(&[b"HSET", b"h", b"a", b"1"]);
5361 for (bad, want) in [
5362 (
5364 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5365 "-ERR Number of fields must be a positive integer",
5366 ),
5367 (
5368 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5369 "-ERR The `numfields` parameter must match the number of arguments",
5370 ),
5371 (
5372 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5373 "-ERR Mandatory argument FIELDS is missing or not at the right position",
5374 ),
5375 (
5377 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5378 "-ERR invalid number of fields",
5379 ),
5380 (
5381 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5382 "-ERR wrong number of arguments",
5383 ),
5384 (
5385 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5386 "-ERR unknown argument: FIELD",
5387 ),
5388 (
5389 &[
5390 b"HGETEX".as_slice(),
5391 b"h",
5392 b"KEEPTTL",
5393 b"FIELDS",
5394 b"1",
5395 b"a",
5396 ][..],
5397 "-ERR unknown argument: KEEPTTL",
5398 ),
5399 (
5400 &[
5401 b"HGETEX".as_slice(),
5402 b"h",
5403 b"EX",
5404 b"100",
5405 b"PERSIST",
5406 b"FIELDS",
5407 b"1",
5408 b"a",
5409 ][..],
5410 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
5411 ),
5412 (
5413 &[
5414 b"HSETEX".as_slice(),
5415 b"h",
5416 b"EX",
5417 b"1",
5418 b"KEEPTTL",
5419 b"FIELDS",
5420 b"1",
5421 b"a",
5422 b"1",
5423 ][..],
5424 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
5425 ),
5426 (
5427 &[
5428 b"HSETEX".as_slice(),
5429 b"h",
5430 b"FNX",
5431 b"FXX",
5432 b"FIELDS",
5433 b"1",
5434 b"a",
5435 b"1",
5436 ][..],
5437 "-ERR Only one of FXX or FNX arguments can be specified",
5438 ),
5439 (
5440 &[
5441 b"HSETEX".as_slice(),
5442 b"h",
5443 b"FIELDS",
5444 b"2",
5445 b"a",
5446 b"1",
5447 b"b",
5448 ][..],
5449 "-ERR wrong number of arguments",
5450 ),
5451 (
5452 &[
5453 b"HGETEX".as_slice(),
5454 b"h",
5455 b"EX",
5456 b"-1",
5457 b"FIELDS",
5458 b"1",
5459 b"a",
5460 ][..],
5461 "-ERR invalid expire time, must be >= 0",
5462 ),
5463 (
5464 &[
5465 b"HGETEX".as_slice(),
5466 b"h",
5467 b"PXAT",
5468 b"99999999999999",
5469 b"FIELDS",
5470 b"1",
5471 b"a",
5472 ][..],
5473 "-ERR invalid expire time in 'hgetex' command",
5474 ),
5475 (
5476 &[
5477 b"HSETEX".as_slice(),
5478 b"h",
5479 b"EX",
5480 b"abc",
5481 b"FIELDS",
5482 b"1",
5483 b"a",
5484 b"1",
5485 ][..],
5486 "-ERR value is not an integer or out of range",
5487 ),
5488 ] {
5489 let reply = f.run(bad);
5490 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5491 assert!(!reply.contains('*'), "an array header went out in front");
5492 }
5493 assert_eq!(
5494 f.run(&[b"HGET", b"h", b"a"]),
5495 "$1\r\n1\r\n",
5496 "and not one of them wrote anything"
5497 );
5498 assert_eq!(
5499 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5500 "*1\r\n:-1\r\n"
5501 );
5502 }
5503
5504 #[test]
5505 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
5506 let mut f = Fixture::new();
5507 f.run(&[b"SET", b"str", b"v"]);
5508 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5509 for cmd in [
5510 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5511 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5512 &[
5513 b"HGETEX".as_slice(),
5514 b"str",
5515 b"EX",
5516 b"100",
5517 b"FIELDS",
5518 b"1",
5519 b"f",
5520 ][..],
5521 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
5522 ] {
5523 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5524 }
5525 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5526 }
5527
5528 #[test]
5536 fn himport_writes_declared_values_into_sorted_fields() {
5537 let mut f = Fixture::new();
5538 assert_eq!(
5539 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
5540 "+OK\r\n"
5541 );
5542 assert_eq!(
5543 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
5544 "+OK\r\n"
5545 );
5546 assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
5547 assert_eq!(
5548 f.run(&[b"HGETALL", b"k"]),
5549 bulks(&["a", "3", "b", "1", "aa", "2"])
5550 );
5551 }
5552
5553 #[test]
5556 fn himport_set_replaces_the_whole_key() {
5557 let mut f = Fixture::new();
5558 f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
5559 f.run(&[b"EXPIRE", b"k", b"100"]);
5560 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5561 assert_eq!(
5562 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5563 "+OK\r\n"
5564 );
5565 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5566 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
5567 }
5568
5569 #[test]
5572 fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
5573 let mut f = Fixture::new();
5574 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
5575 f.run(&[b"SELECT", b"1"]);
5576 assert_eq!(
5577 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5578 "+OK\r\n"
5579 );
5580 f.run(&[b"SELECT", b"0"]);
5581 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
5582 assert_eq!(
5583 f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
5584 "-ERR no such fieldset\r\n"
5585 );
5586 }
5587
5588 #[test]
5595 fn himport_complains_in_the_order_a_real_server_does() {
5596 let mut f = Fixture::new();
5597 f.run(&[b"SET", b"str", b"v"]);
5598 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5599 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5600 assert_eq!(
5601 f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
5602 wrong,
5603 "the type beats a missing fieldset"
5604 );
5605 assert_eq!(
5606 f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
5607 wrong,
5608 "and it beats a value count that does not fit"
5609 );
5610 assert_eq!(
5611 f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
5612 "-ERR no such fieldset\r\n"
5613 );
5614 for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
5616 let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
5617 line.extend_from_slice(values);
5618 assert_eq!(
5619 f.run(&line),
5620 "-ERR value count does not match fieldset field count\r\n",
5621 "{} values into two fields",
5622 values.len()
5623 );
5624 }
5625 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5626 }
5627
5628 #[test]
5630 fn himport_checks_each_subcommand_count_under_its_own_name() {
5631 let mut f = Fixture::new();
5632 assert_eq!(
5633 f.run(&[b"HIMPORT"]),
5634 "-ERR wrong number of arguments for 'himport' command\r\n"
5635 );
5636 for (rest, name) in [
5637 (&["PREPARE"][..], "prepare"),
5638 (&["PREPARE", "fs"][..], "prepare"),
5639 (&["SET"][..], "set"),
5640 (&["SET", "k"][..], "set"),
5641 (&["SET", "k", "fs"][..], "set"),
5642 (&["DISCARD"][..], "discard"),
5643 (&["DISCARD", "a", "b"][..], "discard"),
5644 (&["DISCARDALL", "x"][..], "discardall"),
5645 ] {
5646 let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
5647 line.extend(rest.iter().map(|a| a.as_bytes()));
5648 assert_eq!(
5649 f.run(&line),
5650 format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
5651 "HIMPORT {}",
5652 rest.join(" ")
5653 );
5654 }
5655 assert_eq!(
5656 f.run(&[b"HIMPORT", b"NOPE", b"x"]),
5657 "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
5658 );
5659 }
5660
5661 #[test]
5664 fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
5665 let mut f = Fixture::new();
5666 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5667 assert_eq!(
5668 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
5669 "-ERR duplicate field name in fieldset\r\n"
5670 );
5671 assert_eq!(
5672 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5673 "+OK\r\n"
5674 );
5675 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5676 }
5677
5678 #[test]
5681 fn himport_prepare_replaces_and_the_discards_count() {
5682 let mut f = Fixture::new();
5683 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5684 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
5685 assert_eq!(
5686 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5687 "+OK\r\n"
5688 );
5689 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
5690
5691 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
5692 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
5693 f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
5694 f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
5695 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
5696 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
5697 }
5698
5699 fn int(reply: &str) -> i64 {
5705 let body = reply
5706 .strip_prefix(':')
5707 .and_then(|s| s.strip_suffix("\r\n"))
5708 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
5709 body.parse().expect("an integer")
5710 }
5711
5712 fn int_reply(reply: &str) -> i64 {
5713 let body = reply
5714 .strip_prefix("*1\r\n:")
5715 .and_then(|s| s.strip_suffix("\r\n"))
5716 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
5717 body.parse().expect("an integer")
5718 }
5719
5720 fn scan_reply(reply: &str) -> (String, Vec<String>) {
5722 let mut lines = reply.split("\r\n");
5723 assert_eq!(lines.next(), Some("*2"), "got {reply}");
5724 lines.next().expect("the cursor header");
5725 let cursor = lines.next().expect("a cursor").to_owned();
5726 let header = lines.next().expect("an item count");
5727 let n: usize = header[1..].parse().expect("a count");
5728 let mut items = Vec::with_capacity(n);
5729 for _ in 0..n {
5730 lines.next().expect("an item header");
5731 items.push(lines.next().expect("an item").to_owned());
5732 }
5733 (cursor, items)
5734 }
5735
5736 fn sorted(reply: &str) -> Vec<String> {
5739 let mut lines = reply.split("\r\n");
5740 let header = lines.next().expect("a header");
5741 assert!(
5742 header.starts_with('*') || header.starts_with('~'),
5743 "got {reply}"
5744 );
5745 let n: usize = header[1..].parse().expect("a member count");
5746 let mut got = Vec::with_capacity(n);
5747 for _ in 0..n {
5748 lines.next().expect("a member header");
5749 got.push(lines.next().expect("a member").to_owned());
5750 }
5751 got.sort();
5752 got
5753 }
5754
5755 #[test]
5756 fn the_algebra_answers_what_the_sets_share_and_do_not() {
5757 let mut f = Fixture::new();
5758 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5759 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5760 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
5761
5762 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
5763 assert_eq!(
5764 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
5765 ["1", "2", "3", "4", "5"]
5766 );
5767 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
5768 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
5769
5770 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
5773 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
5774 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
5775 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
5776 }
5777
5778 #[test]
5779 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
5780 let mut f = Fixture::new();
5781 f.run(&[b"SADD", b"a", b"x"]);
5782 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
5783 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
5784 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
5785
5786 f.run(&[b"HELLO", b"3"]);
5787 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
5788 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
5789 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
5790 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
5791 }
5792
5793 #[test]
5794 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
5795 let mut f = Fixture::new();
5796 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5797 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5798
5799 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
5800 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
5801 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
5802 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
5803 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
5804 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
5805
5806 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
5809 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5810 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
5811 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
5812
5813 f.run(&[b"SET", b"str", b"v"]);
5816 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
5817 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
5818 }
5819
5820 #[test]
5821 fn sintercard_counts_without_building_and_stops_at_a_limit() {
5822 let mut f = Fixture::new();
5823 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5824 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
5825
5826 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
5827 assert_eq!(
5828 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5829 ":2\r\n"
5830 );
5831 assert_eq!(
5832 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5833 ":3\r\n",
5834 "a limit of zero is no limit"
5835 );
5836 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
5837 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
5838
5839 assert_eq!(
5841 f.run(&[b"SINTERCARD", b"0", b"a"]),
5842 "-ERR numkeys should be greater than 0\r\n"
5843 );
5844 assert_eq!(
5845 f.run(&[b"SINTERCARD", b"abc", b"a"]),
5846 "-ERR numkeys should be greater than 0\r\n"
5847 );
5848 assert_eq!(
5849 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
5850 "-ERR Number of keys can't be greater than number of args\r\n"
5851 );
5852 assert_eq!(
5853 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
5854 "-ERR LIMIT can't be negative\r\n"
5855 );
5856 assert_eq!(
5857 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
5858 "-ERR syntax error\r\n"
5859 );
5860 f.run(&[b"SADD", b"LIMIT", b"2"]);
5862 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
5863 }
5864
5865 #[test]
5868 fn sunioncard_and_sdiffcard_count_without_building() {
5869 let mut f = Fixture::new();
5870 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5871 f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
5872
5873 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
5874 assert_eq!(
5875 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5876 ":2\r\n"
5877 );
5878 assert_eq!(
5879 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5880 ":6\r\n",
5881 "a limit of zero is no limit"
5882 );
5883 assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
5884 assert_eq!(
5885 f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
5886 ":4\r\n",
5887 "a missing key adds nothing to a union"
5888 );
5889
5890 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
5891 assert_eq!(
5892 f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
5893 ":1\r\n"
5894 );
5895 assert_eq!(
5896 f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
5897 ":2\r\n",
5898 "a difference is not symmetric"
5899 );
5900 assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
5901 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
5902 assert_eq!(
5903 f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
5904 ":0\r\n",
5905 "nothing taken away from nothing"
5906 );
5907
5908 for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
5911 assert_eq!(
5912 f.run(&[name, b"0", b"a"]),
5913 "-ERR numkeys should be greater than 0\r\n"
5914 );
5915 assert_eq!(
5916 f.run(&[name, b"abc", b"a"]),
5917 "-ERR numkeys should be greater than 0\r\n"
5918 );
5919 assert_eq!(
5920 f.run(&[name, b"-1", b"a"]),
5921 "-ERR numkeys should be greater than 0\r\n"
5922 );
5923 assert_eq!(
5924 f.run(&[name, b"3", b"a", b"b"]),
5925 "-ERR Number of keys can't be greater than number of args\r\n"
5926 );
5927 assert_eq!(
5928 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
5929 "-ERR LIMIT can't be negative\r\n"
5930 );
5931 assert_eq!(
5932 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
5933 "-ERR LIMIT can't be negative\r\n",
5934 "a LIMIT that is not a number gets the negative message too"
5935 );
5936 assert_eq!(
5937 f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
5938 "-ERR syntax error\r\n"
5939 );
5940 assert_eq!(
5941 f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
5942 "-ERR syntax error\r\n"
5943 );
5944 assert_eq!(
5945 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
5946 "-ERR syntax error\r\n"
5947 );
5948 }
5949
5950 f.run(&[b"SADD", b"LIMIT", b"2"]);
5952 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
5953 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
5954 }
5955
5956 #[test]
5957 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
5958 let mut f = Fixture::new();
5959 f.run(&[b"SADD", b"a", b"1"]);
5960 f.run(&[b"SADD", b"d", b"old"]);
5961 f.run(&[b"SET", b"str", b"v"]);
5962
5963 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5964 for bad in [
5965 &[b"SINTER".as_slice(), b"a", b"str"][..],
5966 &[b"SUNION".as_slice(), b"str"][..],
5967 &[b"SDIFF".as_slice(), b"a", b"str"][..],
5968 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
5969 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
5970 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
5971 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
5972 ] {
5973 let reply = f.run(bad);
5974 assert_eq!(reply, wrong, "for {:?}", bad[0]);
5975 }
5976 assert_eq!(
5977 f.run(&[b"SMEMBERS", b"d"]),
5978 "*1\r\n$3\r\nold\r\n",
5979 "and the destination was left alone every time"
5980 );
5981 }
5982
5983 #[test]
5986 fn churning_sets_does_not_grow_the_server() {
5987 let mut f = Fixture::new();
5988 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
5989 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
5990 .chain(std::iter::once(&b"s"[..]))
5991 .chain(members.iter().map(Vec::as_slice))
5992 .collect();
5993
5994 f.run(&args);
5995 f.run(&[b"DEL", b"s"]);
5996 f.server.compact_step();
5997 let after_first = f.server.memory_bytes();
5998
5999 for _ in 0..200 {
6000 f.run(&args);
6001 f.run(&[b"DEL", b"s"]);
6002 f.server.compact_step();
6003 }
6004 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6005 assert!(
6006 f.server.memory_bytes() <= after_first * 2,
6007 "held {} after two hundred passes against {after_first} after one",
6008 f.server.memory_bytes()
6009 );
6010 }
6011
6012 #[test]
6020 fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6021 let mut f = Fixture::new();
6022 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6023 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6024 assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6025 assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6026 assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6027 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6028
6029 assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6031 assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6032 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6033
6034 f.run(&[b"SET", b"num", b"12345"]);
6035 assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6036 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6037 assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6038 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6039 assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6040 }
6041
6042 #[test]
6047 fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6048 let mut f = Fixture::new();
6049 f.run(&[b"SET", b"mykey", b"foobar"]);
6050 assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6051 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6052 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6053 assert_eq!(
6054 f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6055 ":6\r\n"
6056 );
6057 assert_eq!(
6058 f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6059 ":25\r\n"
6060 );
6061 assert_eq!(
6062 f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6063 ":17\r\n"
6064 );
6065 assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6066
6067 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6070
6071 assert_eq!(
6073 f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6074 "-ERR syntax error\r\n"
6075 );
6076 assert_eq!(
6077 f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6078 "-ERR syntax error\r\n"
6079 );
6080 }
6081
6082 #[test]
6088 fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6089 let mut f = Fixture::new();
6090 f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6091 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6092 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6093 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6094 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6095 assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6096
6097 f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6098 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6099 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6100 assert_eq!(
6101 f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6102 ":8\r\n"
6103 );
6104
6105 assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6108 assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6109 }
6110
6111 #[test]
6113 fn the_eight_combinations_write_what_a_real_server_writes() {
6114 let mut f = Fixture::new();
6115 f.run(&[b"SET", b"a", b"abc"]);
6116 f.run(&[b"SET", b"b", b"abd"]);
6117 let cases: &[(&[u8], &str)] = &[
6118 (b"AND", "ab`"),
6119 (b"OR", "abg"),
6120 (b"XOR", "\u{0}\u{0}\u{7}"),
6121 (b"DIFF", "\u{0}\u{0}\u{3}"),
6122 (b"DIFF1", "\u{0}\u{0}\u{4}"),
6123 (b"ANDOR", "ab`"),
6124 (b"ONE", "\u{0}\u{0}\u{7}"),
6125 ];
6126 for (op, want) in cases {
6127 assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6128 assert_eq!(
6129 f.run(&[b"GET", b"d"]),
6130 format!("$3\r\n{want}\r\n"),
6131 "{op:?}"
6132 );
6133 }
6134 assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6136 assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6137
6138 assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6141 assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6142
6143 f.run(&[b"SET", b"dest", b"x"]);
6146 assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6147 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6148 }
6149
6150 #[test]
6152 fn bitop_names_the_operation_in_its_own_complaints() {
6153 let mut f = Fixture::new();
6154 f.run(&[b"SET", b"a", b"abc"]);
6155 assert_eq!(
6156 f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6157 "-ERR syntax error\r\n"
6158 );
6159 assert_eq!(
6160 f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6161 "-ERR BITOP NOT must be called with a single source key.\r\n"
6162 );
6163 for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6164 assert_eq!(
6165 f.run(&[b"BITOP", op, b"d", b"a"]),
6166 format!(
6167 "-ERR BITOP {} must be called with at least two source keys.\r\n",
6168 String::from_utf8_lossy(op)
6169 )
6170 );
6171 }
6172 f.run(&[b"LPUSH", b"l", b"x"]);
6173 assert_eq!(
6174 f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6175 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6176 );
6177 }
6178
6179 #[test]
6181 fn bitfield_reads_and_writes_packed_fields() {
6182 let mut f = Fixture::new();
6183 assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6184 assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6185
6186 assert_eq!(
6187 f.run(&[
6188 b"BITFIELD",
6189 b"bf",
6190 b"INCRBY",
6191 b"u2",
6192 b"100",
6193 b"1",
6194 b"GET",
6195 b"u4",
6196 b"0"
6197 ]),
6198 "*2\r\n:1\r\n:0\r\n"
6199 );
6200 assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6203
6204 assert_eq!(
6206 f.run(&[
6207 b"BITFIELD",
6208 b"bf",
6209 b"SET",
6210 b"u8",
6211 b"#0",
6212 b"255",
6213 b"GET",
6214 b"u8",
6215 b"#0"
6216 ]),
6217 "*2\r\n:0\r\n:255\r\n"
6218 );
6219
6220 assert_eq!(
6221 f.run(&[
6222 b"BITFIELD",
6223 b"bf",
6224 b"OVERFLOW",
6225 b"SAT",
6226 b"INCRBY",
6227 b"i8",
6228 b"0",
6229 b"120",
6230 b"INCRBY",
6231 b"i8",
6232 b"0",
6233 b"120"
6234 ]),
6235 "*2\r\n:119\r\n:127\r\n"
6236 );
6237 assert_eq!(
6238 f.run(&[
6239 b"BITFIELD",
6240 b"bf2",
6241 b"OVERFLOW",
6242 b"FAIL",
6243 b"INCRBY",
6244 b"u2",
6245 b"0",
6246 b"5"
6247 ]),
6248 "*1\r\n$-1\r\n"
6249 );
6250 assert_eq!(
6251 f.run(&[
6252 b"BITFIELD",
6253 b"bf3",
6254 b"OVERFLOW",
6255 b"WRAP",
6256 b"INCRBY",
6257 b"u2",
6258 b"0",
6259 b"5"
6260 ]),
6261 "*1\r\n:1\r\n"
6262 );
6263 assert_eq!(
6264 f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6265 "*1\r\n:4611686018427387904\r\n"
6266 );
6267 }
6268
6269 #[test]
6275 fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6276 let mut f = Fixture::new();
6277 let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6278 assert_eq!(
6279 f.run(&[
6280 b"BITFIELD",
6281 b"bad",
6282 b"SET",
6283 b"u8",
6284 b"0",
6285 b"1",
6286 b"GET",
6287 b"u99",
6288 b"0"
6289 ]),
6290 bad_type
6291 );
6292 assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6293 assert_eq!(
6294 f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6295 bad_type
6296 );
6297 assert_eq!(
6298 f.run(&[b"BITFIELD", b"bad", b"GET"]),
6299 "-ERR syntax error\r\n"
6300 );
6301 assert_eq!(
6302 f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6303 "-ERR syntax error\r\n"
6304 );
6305 assert_eq!(
6306 f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6307 "-ERR syntax error\r\n"
6308 );
6309 assert_eq!(
6310 f.run(&[
6311 b"BITFIELD",
6312 b"bad",
6313 b"OVERFLOW",
6314 b"NOPE",
6315 b"GET",
6316 b"u8",
6317 b"0"
6318 ]),
6319 "-ERR Invalid OVERFLOW type specified\r\n"
6320 );
6321 assert_eq!(
6322 f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6323 "-ERR value is not an integer or out of range\r\n"
6324 );
6325 for at in [&b"#-1"[..], b"abc"] {
6326 assert_eq!(
6327 f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6328 "-ERR bit offset is not an integer or out of range\r\n"
6329 );
6330 }
6331 }
6332
6333 #[test]
6335 fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6336 let mut f = Fixture::new();
6337 f.run(&[b"SET", b"n", b"123"]);
6338 assert_eq!(
6339 f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6340 "*1\r\n:49\r\n"
6341 );
6342 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6344
6345 assert_eq!(
6347 f.run(&[
6348 b"BITFIELD_RO",
6349 b"n",
6350 b"OVERFLOW",
6351 b"SAT",
6352 b"GET",
6353 b"u8",
6354 b"0"
6355 ]),
6356 "*1\r\n:49\r\n"
6357 );
6358 for sub in [&b"SET"[..], b"INCRBY"] {
6359 assert_eq!(
6360 f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6361 "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6362 );
6363 }
6364
6365 assert_eq!(
6366 f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6367 "*1\r\n:0\r\n"
6368 );
6369 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6370 }
6371
6372 #[test]
6374 fn an_offset_off_the_end_of_the_world_is_refused() {
6375 let mut f = Fixture::new();
6376 let bad = "-ERR bit offset is not an integer or out of range\r\n";
6377 for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6378 assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6379 assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6380 }
6381 for arg in [&b"2"[..], b"-1"] {
6382 assert_eq!(
6383 f.run(&[b"BITPOS", b"k", arg]),
6384 "-ERR The bit argument must be 1 or 0.\r\n"
6385 );
6386 }
6387 assert_eq!(
6388 f.run(&[b"BITPOS", b"k", b"abc"]),
6389 "-ERR value is not an integer or out of range\r\n"
6390 );
6391 assert_eq!(
6392 f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
6393 "-ERR value is not an integer or out of range\r\n"
6394 );
6395 let bad_bit = "-ERR bit is not an integer or out of range\r\n";
6396 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
6397 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
6398 }
6399
6400 #[test]
6402 fn every_bitmap_command_says_wrongtype() {
6403 let mut f = Fixture::new();
6404 f.run(&[b"LPUSH", b"l", b"x"]);
6405 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6406 let cases: &[&[&[u8]]] = &[
6407 &[b"SETBIT", b"l", b"0", b"1"],
6408 &[b"GETBIT", b"l", b"0"],
6409 &[b"BITCOUNT", b"l"],
6410 &[b"BITPOS", b"l", b"1"],
6411 &[b"BITOP", b"AND", b"d", b"l"],
6412 &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
6413 &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
6414 ];
6415 for case in cases {
6416 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
6417 }
6418 }
6419
6420 #[test]
6423 fn a_sketch_is_added_to_and_counted() {
6424 let mut f = Fixture::new();
6425 assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
6427 assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
6428 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
6429 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
6430 assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
6433 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
6434
6435 assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
6436 assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
6437 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6438 }
6439
6440 #[test]
6441 fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
6442 let mut f = Fixture::new();
6443 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6444 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";
6446 let mut reply = b"$27\r\n".to_vec();
6447 reply.extend_from_slice(want);
6448 reply.extend_from_slice(b"\r\n");
6449 assert_eq!(f.raw(&[b"GET", b"h"]), reply);
6450 }
6451
6452 #[test]
6453 fn counting_several_keys_counts_their_union() {
6454 let mut f = Fixture::new();
6455 f.run(&[b"PFADD", b"a", b"x", b"y"]);
6456 f.run(&[b"PFADD", b"b", b"y", b"z"]);
6457 assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
6458 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
6459 assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
6462 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
6463 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6464 }
6465
6466 #[test]
6467 fn a_merge_keeps_what_the_destination_had() {
6468 let mut f = Fixture::new();
6469 f.run(&[b"PFADD", b"a", b"x", b"y"]);
6470 f.run(&[b"PFADD", b"b", b"z"]);
6471 assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
6472 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
6473 f.run(&[b"PFADD", b"c", b"w"]);
6475 assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
6476 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
6477 assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
6480 assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
6481 }
6482
6483 #[test]
6484 fn the_debug_forms_answer_four_different_shapes() {
6485 let mut f = Fixture::new();
6486 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6487 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
6488 assert_eq!(
6489 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6490 "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
6491 );
6492 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
6493 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
6494 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
6495 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
6496 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6497 assert_eq!(
6499 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6500 "-ERR HLL encoding is not sparse\r\n"
6501 );
6502
6503 let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
6505 assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
6506 assert_eq!(reply.matches(":0\r\n").count(), 16381);
6507 assert_eq!(reply.matches(":1\r\n").count(), 2);
6508 assert_eq!(reply.matches(":2\r\n").count(), 1);
6509
6510 assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
6511 }
6512
6513 #[test]
6514 fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
6515 let mut f = Fixture::new();
6516 f.run(&[b"SET", b"plain", b"not a sketch"]);
6517 let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
6518 assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
6519 assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
6520 assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
6521 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
6522
6523 f.run(&[b"RPUSH", b"l", b"x"]);
6526 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6527 assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
6528 assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
6529 assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
6530 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6531 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
6532 }
6533
6534 #[test]
6535 fn pfdebug_has_its_own_complaints() {
6536 let mut f = Fixture::new();
6537 f.run(&[b"PFADD", b"h", b"a"]);
6538 assert_eq!(
6541 f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
6542 "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
6543 );
6544 let gone = "-ERR The specified key does not exist\r\n";
6546 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
6547 assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
6548 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
6549 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
6550 assert_eq!(
6551 f.run(&[b"PFDEBUG"]),
6552 "-ERR wrong number of arguments for 'pfdebug' command\r\n"
6553 );
6554 assert_eq!(
6555 f.run(&[b"PFSELFTEST", b"x"]),
6556 "-ERR wrong number of arguments for 'pfselftest' command\r\n"
6557 );
6558 }
6559
6560 #[test]
6561 fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
6562 let mut f = Fixture::new();
6563 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6564 let reply = f.raw(&[b"GET", b"h"]);
6567 let short = reply[5..reply.len() - 3].to_vec();
6568 f.run(&[b"SET", b"h", &short]);
6569 assert_eq!(
6570 f.run(&[b"PFCOUNT", b"h"]),
6571 "-INVALIDOBJ Corrupted HLL object detected\r\n"
6572 );
6573 }
6574
6575 #[test]
6576 fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
6577 let mut f = Fixture::new();
6578 f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
6581 for i in 0..10_000u32 {
6582 let ele = format!("e{i}");
6583 f.run(&[b"PFADD", b"big", ele.as_bytes()]);
6584 }
6585 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
6586 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
6587
6588 for key in [&b"small"[..], b"big"] {
6589 let mut copy = key.to_vec();
6590 copy.push(b'2');
6591 let bytes = payload(&f.raw(&[b"DUMP", key]));
6592 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
6593 assert_eq!(f.raw(&[b"GET", ©]), f.raw(&[b"GET", key]));
6596 assert_eq!(
6597 f.run(&[b"PFDEBUG", b"ENCODING", ©]),
6598 f.run(&[b"PFDEBUG", b"ENCODING", key])
6599 );
6600 assert_eq!(f.run(&[b"PFCOUNT", ©]), f.run(&[b"PFCOUNT", key]));
6601 }
6602 assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
6603 assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
6604 }
6605
6606 fn bulk(s: &str) -> String {
6610 format!("${}\r\n{s}\r\n", s.len())
6611 }
6612
6613 fn bulks(parts: &[&str]) -> String {
6616 let mut s = format!("*{}\r\n", parts.len());
6617 for p in parts {
6618 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
6619 }
6620 s
6621 }
6622
6623 #[test]
6624 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
6625 let mut f = Fixture::new();
6626 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
6630 assert_eq!(
6631 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6632 bulks(&["c", "b", "a"])
6633 );
6634 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
6635 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
6636 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
6637 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
6638 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
6639 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
6640 }
6641
6642 #[test]
6643 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
6644 let mut f = Fixture::new();
6645 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
6646 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
6647 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6648 f.run(&[b"RPUSH", b"k", b"a"]);
6649 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
6650 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
6651 assert_eq!(
6652 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6653 bulks(&["z", "a", "y"])
6654 );
6655 }
6656
6657 #[test]
6660 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
6661 let mut f = Fixture::new();
6662 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
6663 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
6664 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
6665 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
6666 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6667 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
6670 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
6671 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
6673 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6674 }
6675
6676 #[test]
6677 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
6678 let mut f = Fixture::new();
6679 f.run(&[b"RPUSH", b"k", b"a"]);
6680 let range = "-ERR value is out of range, must be positive\r\n";
6681 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
6682 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
6683 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
6684 assert_eq!(
6687 f.run(&[b"LPOP", b"k", b"1", b"2"]),
6688 "-ERR wrong number of arguments for 'lpop' command\r\n"
6689 );
6690 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
6691 }
6692
6693 #[test]
6694 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
6695 let mut f = Fixture::new();
6696 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6697 assert_eq!(
6698 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6699 bulks(&["a", "b", "c"])
6700 );
6701 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
6702 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
6703 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
6704 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
6705 assert_eq!(
6706 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
6707 bulks(&["a", "b", "c"])
6708 );
6709 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
6712 assert_eq!(
6713 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
6714 "-ERR value is not an integer or out of range\r\n"
6715 );
6716 }
6717
6718 #[test]
6719 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
6720 let mut f = Fixture::new();
6721 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6722 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
6723 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
6724 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
6725 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
6726 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
6727 assert_eq!(
6728 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6729 bulks(&["a", "b", "z"])
6730 );
6731 assert_eq!(
6734 f.run(&[b"LSET", b"k", b"99", b"z"]),
6735 "-ERR index out of range\r\n"
6736 );
6737 assert_eq!(
6738 f.run(&[b"LSET", b"nope", b"0", b"z"]),
6739 "-ERR no such key\r\n"
6740 );
6741 }
6742
6743 #[test]
6744 fn linsert_says_three_things_with_one_signed_number() {
6745 let mut f = Fixture::new();
6746 assert_eq!(
6749 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
6750 ":0\r\n"
6751 );
6752 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6753 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
6754 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
6755 assert_eq!(
6756 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6757 bulks(&["X", "a", "b", "Y"])
6758 );
6759 assert_eq!(
6760 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
6761 ":-1\r\n"
6762 );
6763 assert_eq!(
6764 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
6765 "-ERR syntax error\r\n"
6766 );
6767 }
6768
6769 #[test]
6770 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
6771 let mut f = Fixture::new();
6772 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
6773 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
6774 assert_eq!(
6775 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6776 bulks(&["b", "c", "a"])
6777 );
6778 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
6779 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6780 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
6781 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
6782 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6783 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
6784 }
6785
6786 #[test]
6787 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
6788 let mut f = Fixture::new();
6789 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
6790 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
6791 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6792 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
6795 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6796 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
6797 }
6798
6799 #[test]
6800 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
6801 let mut f = Fixture::new();
6802 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
6803 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
6804 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
6805 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
6806 assert_eq!(
6807 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
6808 "*2\r\n:0\r\n:3\r\n"
6809 );
6810 assert_eq!(
6811 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
6812 "*3\r\n:6\r\n:3\r\n:0\r\n"
6813 );
6814 assert_eq!(
6817 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
6818 "*1\r\n:0\r\n"
6819 );
6820 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
6823 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
6824 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
6825 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
6826 }
6827
6828 #[test]
6829 fn lpos_words_its_three_mistakes_the_way_redis_does() {
6830 let mut f = Fixture::new();
6831 f.run(&[b"RPUSH", b"p", b"a"]);
6832 assert_eq!(
6835 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
6836 "-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"
6837 );
6838 assert_eq!(
6839 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
6840 "-ERR COUNT can't be negative\r\n"
6841 );
6842 assert_eq!(
6843 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
6844 "-ERR MAXLEN can't be negative\r\n"
6845 );
6846 assert_eq!(
6847 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
6848 "-ERR syntax error\r\n"
6849 );
6850 assert_eq!(
6851 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
6852 "-ERR syntax error\r\n"
6853 );
6854 }
6855
6856 #[test]
6857 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
6858 let mut f = Fixture::new();
6859 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6860 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
6861 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6862 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
6863 assert_eq!(
6864 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
6865 "$1\r\na\r\n"
6866 );
6867 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
6868 f.run(&[b"DEL", b"r"]);
6871 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
6872 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
6873 assert_eq!(
6874 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
6875 bulks(&["3", "1", "2"])
6876 );
6877 assert_eq!(
6878 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
6879 "$-1\r\n"
6880 );
6881 assert_eq!(
6882 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
6883 "-ERR syntax error\r\n"
6884 );
6885 }
6886
6887 #[test]
6888 fn a_move_checks_the_destination_before_it_takes_anything() {
6889 let mut f = Fixture::new();
6890 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6891 f.run(&[b"SET", b"str", b"v"]);
6892 assert_eq!(
6893 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
6894 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6895 );
6896 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6898 }
6899
6900 #[test]
6901 fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
6902 for (from, to, order, want) in [
6906 ("LEFT", "RIGHT", "OBO", ["a", "b"]),
6907 ("LEFT", "RIGHT", "BULK", ["a", "b"]),
6908 ("LEFT", "LEFT", "OBO", ["b", "a"]),
6909 ("LEFT", "LEFT", "BULK", ["a", "b"]),
6910 ("RIGHT", "LEFT", "OBO", ["d", "e"]),
6911 ("RIGHT", "LEFT", "BULK", ["d", "e"]),
6912 ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
6913 ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
6914 ] {
6915 let mut f = Fixture::new();
6916 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
6917 let how = format!("{from} {to} {order}");
6918 let reply = f.run(&[
6919 b"LMOVEM",
6920 b"s",
6921 b"d",
6922 from.as_bytes(),
6923 to.as_bytes(),
6924 b"COUNT",
6925 b"2",
6926 order.as_bytes(),
6927 ]);
6928 assert_eq!(reply, bulks(&want), "the reply for {how}");
6929 assert_eq!(
6930 f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
6931 bulks(&want),
6932 "the destination for {how}"
6933 );
6934 }
6935 }
6936
6937 #[test]
6938 fn a_block_move_of_one_needs_no_count_at_all() {
6939 let mut f = Fixture::new();
6940 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
6941 assert_eq!(
6942 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
6943 bulks(&["a"])
6944 );
6945 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
6946 assert_eq!(
6949 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
6950 "-ERR syntax error\r\n"
6951 );
6952 assert_eq!(
6953 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
6954 "-ERR syntax error\r\n"
6955 );
6956 }
6957
6958 #[test]
6959 fn a_block_move_with_exactly_takes_all_of_them_or_none() {
6960 let mut f = Fixture::new();
6961 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
6962 assert_eq!(
6966 f.run(&[
6967 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
6968 ]),
6969 "*-1\r\n"
6970 );
6971 assert_eq!(
6972 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
6973 bulks(&["a", "b", "c"])
6974 );
6975 assert_eq!(
6977 f.run(&[
6978 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
6979 ]),
6980 bulks(&["a", "b", "c"])
6981 );
6982 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
6983 assert_eq!(
6984 f.run(&[
6985 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
6986 ]),
6987 "*-1\r\n"
6988 );
6989 }
6990
6991 #[test]
6992 fn a_block_move_onto_itself_rotates_by_the_count() {
6993 let mut f = Fixture::new();
6994 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
6995 assert_eq!(
6996 f.run(&[
6997 b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
6998 ]),
6999 bulks(&["a", "b"])
7000 );
7001 assert_eq!(
7002 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7003 bulks(&["c", "a", "b"])
7004 );
7005 }
7006
7007 #[test]
7008 fn a_block_move_reads_the_count_before_the_ordering_word() {
7009 let mut f = Fixture::new();
7010 f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7011 f.run(&[b"SET", b"str", b"v"]);
7012 let count = "-ERR count should be greater than 0\r\n";
7013 assert_eq!(
7014 f.run(&[
7015 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7016 ]),
7017 count
7018 );
7019 assert_eq!(
7020 f.run(&[
7021 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7022 ]),
7023 count
7024 );
7025 assert_eq!(
7026 f.run(&[
7027 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7028 ]),
7029 "-ERR syntax error\r\n"
7030 );
7031 assert_eq!(
7032 f.run(&[
7033 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7034 ]),
7035 "-ERR syntax error\r\n"
7036 );
7037 assert_eq!(
7040 f.run(&[
7041 b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7042 ]),
7043 count
7044 );
7045 assert_eq!(
7046 f.run(&[
7047 b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7048 ]),
7049 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7050 );
7051 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7052 }
7053
7054 #[test]
7055 fn lmpop_answers_from_the_first_key_that_has_anything() {
7056 let mut f = Fixture::new();
7057 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7058 assert_eq!(
7061 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7062 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7063 );
7064 assert_eq!(
7065 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7066 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7067 );
7068 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7069 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7072 }
7073
7074 #[test]
7075 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7076 let mut f = Fixture::new();
7077 f.run(&[b"RPUSH", b"k", b"a"]);
7078 assert_eq!(
7079 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7080 "-ERR numkeys should be greater than 0\r\n"
7081 );
7082 assert_eq!(
7083 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7084 "-ERR numkeys should be greater than 0\r\n"
7085 );
7086 assert_eq!(
7087 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7088 "-ERR count should be greater than 0\r\n"
7089 );
7090 assert_eq!(
7093 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7094 "-ERR syntax error\r\n"
7095 );
7096 assert_eq!(
7097 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7098 "-ERR syntax error\r\n"
7099 );
7100 assert_eq!(
7101 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7102 "-ERR syntax error\r\n"
7103 );
7104 assert_eq!(
7105 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7106 "-ERR syntax error\r\n"
7107 );
7108 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7109 }
7110
7111 #[test]
7112 fn every_list_command_says_wrongtype_and_writes_nothing() {
7113 let mut f = Fixture::new();
7114 f.run(&[b"SET", b"str", b"v"]);
7115 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7116 for cmd in [
7117 &[b"LPUSH".as_slice(), b"str", b"a"][..],
7118 &[b"RPUSH", b"str", b"a"],
7119 &[b"LPUSHX", b"str", b"a"],
7120 &[b"RPUSHX", b"str", b"a"],
7121 &[b"LPOP", b"str"],
7122 &[b"LPOP", b"str", b"2"],
7123 &[b"RPOP", b"str"],
7124 &[b"LLEN", b"str"],
7125 &[b"LRANGE", b"str", b"0", b"-1"],
7126 &[b"LINDEX", b"str", b"0"],
7127 &[b"LSET", b"str", b"0", b"a"],
7128 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7129 &[b"LREM", b"str", b"0", b"a"],
7130 &[b"LTRIM", b"str", b"0", b"-1"],
7131 &[b"LPOS", b"str", b"a"],
7132 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7133 &[b"RPOPLPUSH", b"str", b"d"],
7134 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7135 &[b"LMPOP", b"1", b"str", b"LEFT"],
7136 ] {
7137 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7138 }
7139 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7140 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7141 }
7142
7143 #[test]
7147 fn a_timeout_has_three_ways_of_being_wrong() {
7148 let mut f = Fixture::new();
7149 let not_float = "-ERR timeout is not a float or out of range\r\n";
7150 let range = "-ERR timeout is out of range\r\n";
7151 for (bad, want) in [
7152 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7153 (&[b"BLPOP", b"k", b"nan"], not_float),
7154 (&[b"BLPOP", b"k", b""], not_float),
7155 (&[b"BLPOP", b"k", b" 1"], not_float),
7158 (&[b"BLPOP", b"k", b"1 "], not_float),
7159 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7160 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7161 (&[b"BLPOP", b"k", b"1e400"], range),
7164 (&[b"BLPOP", b"k", b"inf"], range),
7165 (&[b"BLPOP", b"k", b"9999999999999999"], range),
7166 (&[b"BRPOP", b"k", b"abc"], not_float),
7167 (
7168 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7169 not_float,
7170 ),
7171 (
7172 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7173 "-ERR timeout is negative\r\n",
7174 ),
7175 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7176 ] {
7177 assert_eq!(f.run(bad), want, "for {bad:?}");
7178 }
7179 }
7180
7181 #[test]
7184 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7185 let mut f = Fixture::new();
7186 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7187 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7188 assert_eq!(flow, Flow::Block, "for {timeout:?}");
7189 assert!(out.is_empty(), "for {timeout:?}");
7190 }
7191 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7195 assert_eq!(flow, Flow::Block);
7196 assert!(out.is_empty());
7197 }
7198
7199 #[test]
7200 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7201 let mut f = Fixture::new();
7202 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7203
7204 assert_eq!(
7207 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7208 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7209 );
7210 assert_eq!(
7211 f.run(&[b"BRPOP", b"L", b"0"]),
7212 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7213 );
7214 assert_eq!(
7215 f.run(&[
7216 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7217 ]),
7218 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7219 );
7220 assert_eq!(
7221 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7222 "$1\r\nd\r\n"
7223 );
7224 assert_eq!(
7225 f.run(&[b"EXISTS", b"L"]),
7226 ":0\r\n",
7227 "and the key went with it"
7228 );
7229 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7230 f.run(&[b"RPUSH", b"D", b"x"]);
7233 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7234 assert_eq!(
7235 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7236 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7237 );
7238 }
7239
7240 #[test]
7241 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7242 let mut f = Fixture::new();
7243 f.run(&[b"RPUSH", b"k", b"a"]);
7244 for (bad, want) in [
7245 (
7246 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7247 "-ERR numkeys should be greater than 0\r\n",
7248 ),
7249 (
7250 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7251 "-ERR numkeys should be greater than 0\r\n",
7252 ),
7253 (
7256 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7257 "-ERR syntax error\r\n",
7258 ),
7259 (
7260 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7261 "-ERR syntax error\r\n",
7262 ),
7263 (
7264 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7265 "-ERR syntax error\r\n",
7266 ),
7267 (
7268 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7269 "-ERR syntax error\r\n",
7270 ),
7271 (
7274 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7275 "-ERR count should be greater than 0\r\n",
7276 ),
7277 (
7278 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7279 "-ERR count should be greater than 0\r\n",
7280 ),
7281 ] {
7282 assert_eq!(f.run(bad), want, "for {bad:?}");
7283 }
7284 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7285 }
7286
7287 #[test]
7288 fn a_blocking_move_reads_its_directions_before_its_timeout() {
7289 let mut f = Fixture::new();
7290 assert_eq!(
7293 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7294 "-ERR syntax error\r\n"
7295 );
7296 assert_eq!(
7297 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7298 "-ERR syntax error\r\n"
7299 );
7300 }
7301
7302 #[test]
7306 fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7307 let mut f = Fixture::new();
7308 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7309 assert_eq!(
7310 f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7311 (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7312 );
7313 assert_eq!(
7314 f.run(&[
7315 b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7316 ]),
7317 bulks(&["e", "d"])
7318 );
7319 assert_eq!(
7320 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7321 bulks(&["a", "e", "d"])
7322 );
7323 assert_eq!(
7325 f.run(&[
7326 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7327 ]),
7328 bulks(&["b", "c"])
7329 );
7330 assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7331 }
7332
7333 #[test]
7337 fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7338 let mut f = Fixture::new();
7339 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7340 assert_eq!(
7342 f.flow(&[
7343 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7344 ]),
7345 (Flow::Continue, bulks(&["a", "b"]))
7346 );
7347
7348 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7349 assert_eq!(
7352 f.flow(&[
7353 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7354 ])
7355 .0,
7356 Flow::Block
7357 );
7358 assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7359 }
7360
7361 #[test]
7362 fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7363 let mut f = Fixture::new();
7364 let syntax = "-ERR syntax error\r\n";
7365 assert_eq!(
7367 f.run(&[
7368 b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7369 ]),
7370 syntax
7371 );
7372 assert_eq!(
7374 f.run(&[
7375 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7376 ]),
7377 "-ERR timeout is not a float or out of range\r\n"
7378 );
7379 assert_eq!(
7380 f.run(&[
7381 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7382 ]),
7383 "-ERR timeout is negative\r\n"
7384 );
7385 assert_eq!(
7387 f.run(&[
7388 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
7389 ]),
7390 "-ERR count should be greater than 0\r\n"
7391 );
7392 assert_eq!(
7393 f.run(&[
7394 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
7395 ]),
7396 syntax
7397 );
7398 assert_eq!(
7401 f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
7402 syntax
7403 );
7404 assert_eq!(
7405 f.run(&[
7406 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
7407 ]),
7408 syntax
7409 );
7410 }
7411
7412 #[test]
7415 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
7416 let mut f = Fixture::new();
7417 f.run(&[b"SET", b"S", b"v"]);
7418 f.run(&[b"RPUSH", b"D", b"x"]);
7419 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7420
7421 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
7422 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
7425 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
7426 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
7427 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
7428 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
7431 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
7432 assert_eq!(
7433 f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
7434 wrong
7435 );
7436 assert_eq!(
7437 f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
7438 wrong
7439 );
7440
7441 assert_eq!(
7445 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7446 .0,
7447 Flow::Block
7448 );
7449 assert_eq!(
7453 f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7454 .0,
7455 Flow::Block
7456 );
7457 f.run(&[b"RPUSH", b"E", b"1", b"2"]);
7458 assert_eq!(
7459 f.flow(&[
7460 b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
7461 ])
7462 .0,
7463 Flow::Block
7464 );
7465 }
7466
7467 #[test]
7471 fn churning_lists_does_not_grow_the_server() {
7472 let mut f = Fixture::new();
7473 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
7474 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
7475 .into_iter()
7476 .chain(vals.iter().map(Vec::as_slice))
7477 .collect();
7478
7479 f.run(&args);
7480 f.run(&[b"DEL", b"k"]);
7481 f.server.compact_step();
7482 let after_first = f.server.memory_bytes();
7483
7484 for _ in 0..200 {
7485 f.run(&args);
7486 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
7487 f.server.compact_step();
7488 }
7489 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
7490 assert!(
7491 f.server.memory_bytes() <= after_first * 2,
7492 "held {} after two hundred passes against {after_first} after one",
7493 f.server.memory_bytes()
7494 );
7495 }
7496
7497 #[test]
7500 fn a_sorted_set_takes_scores_and_gives_them_back() {
7501 let mut f = Fixture::new();
7502 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
7503 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
7504 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
7505 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
7506 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
7507 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
7508 assert_eq!(
7509 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
7510 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
7511 );
7512 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
7513 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
7514 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
7516 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7517 }
7518
7519 #[test]
7520 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
7521 let mut f = Fixture::new();
7522 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
7523 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
7524 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
7525 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
7526
7527 f.out = Out::new(Proto::Resp3);
7528 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
7529 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
7530 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
7531 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
7532 }
7533
7534 #[test]
7535 fn the_zadd_options_gate_what_gets_written() {
7536 let mut f = Fixture::new();
7537 f.run(&[b"ZADD", b"z", b"5", b"a"]);
7538 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
7540 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
7541 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
7542 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
7543 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
7545 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
7546 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
7547 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
7549 assert_eq!(
7550 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
7551 ":2\r\n"
7552 );
7553 }
7554
7555 #[test]
7556 fn zadd_incr_answers_a_score_or_nothing_at_all() {
7557 let mut f = Fixture::new();
7558 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
7559 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
7560 assert_eq!(
7563 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
7564 "$-1\r\n"
7565 );
7566 assert_eq!(
7567 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
7568 "$-1\r\n"
7569 );
7570 assert_eq!(
7571 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
7572 "$-1\r\n"
7573 );
7574 assert_eq!(
7575 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
7576 "$1\r\n8\r\n"
7577 );
7578 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
7579 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
7580 }
7581
7582 #[test]
7583 fn the_two_infinities_will_not_be_added_together() {
7584 let mut f = Fixture::new();
7585 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
7586 let nan = "-ERR resulting score is not a number (NaN)\r\n";
7587 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
7588 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
7589 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
7590 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
7592 }
7593
7594 #[test]
7595 fn zadd_says_its_mistakes_the_way_redis_says_them() {
7596 let mut f = Fixture::new();
7597 assert_eq!(
7600 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
7601 "-ERR syntax error\r\n"
7602 );
7603 assert_eq!(
7604 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
7605 "-ERR XX and NX options at the same time are not compatible\r\n"
7606 );
7607 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
7608 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
7609 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
7610 assert_eq!(
7611 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
7612 "-ERR INCR option supports a single increment-element pair\r\n"
7613 );
7614 assert_eq!(
7616 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
7617 "-ERR syntax error\r\n"
7618 );
7619 assert_eq!(
7621 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
7622 "-ERR value is not a valid float\r\n"
7623 );
7624 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7625 }
7626
7627 #[test]
7628 fn a_rank_says_where_a_member_sits_from_either_end() {
7629 let mut f = Fixture::new();
7630 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7631 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
7632 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
7633 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
7634 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
7635 assert_eq!(
7637 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
7638 "*2\r\n:1\r\n$1\r\n2\r\n"
7639 );
7640 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
7641 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
7642 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
7643 assert_eq!(
7646 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
7647 "-ERR syntax error\r\n"
7648 );
7649 assert_eq!(
7650 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
7651 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
7652 );
7653 }
7654
7655 #[test]
7656 fn the_two_counts_read_their_two_kinds_of_bound() {
7657 let mut f = Fixture::new();
7658 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7659 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
7660 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
7661 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
7662 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
7663 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
7664 assert_eq!(
7665 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
7666 "-ERR min or max is not a float\r\n"
7667 );
7668
7669 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
7670 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
7671 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
7672 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
7673 assert_eq!(
7676 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
7677 "-ERR min or max not valid string range item\r\n"
7678 );
7679 }
7680
7681 #[test]
7687 fn one_range_command_selects_by_rank_or_score_or_name() {
7688 let mut f = Fixture::new();
7689 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7690 assert_eq!(
7691 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7692 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7693 );
7694 assert_eq!(
7695 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
7696 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7697 );
7698 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
7699 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
7700 assert_eq!(
7703 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
7704 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7705 );
7706 assert_eq!(
7707 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
7708 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7709 );
7710 assert_eq!(
7713 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
7714 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7715 );
7716 assert_eq!(
7717 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
7718 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7719 );
7720 assert_eq!(
7721 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
7722 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7723 );
7724 }
7725
7726 #[test]
7729 fn the_older_range_spellings_name_their_high_end_first() {
7730 let mut f = Fixture::new();
7731 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7732 assert_eq!(
7733 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
7734 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7735 );
7736 assert_eq!(
7737 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
7738 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7739 );
7740 assert_eq!(
7741 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
7742 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7743 );
7744 assert_eq!(
7745 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
7746 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7747 );
7748 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
7752 assert_eq!(
7753 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
7754 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
7755 );
7756 assert_eq!(
7757 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
7758 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
7759 );
7760 for cmd in [
7763 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
7764 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
7765 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
7766 ] {
7767 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
7768 }
7769 }
7770
7771 #[test]
7774 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
7775 let mut f = Fixture::new();
7776 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7777 assert_eq!(
7778 f.run(&[
7779 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
7780 ]),
7781 "*1\r\n$1\r\nb\r\n"
7782 );
7783 assert_eq!(
7785 f.run(&[
7786 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
7787 ]),
7788 "*0\r\n"
7789 );
7790 assert_eq!(
7791 f.run(&[
7792 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
7793 ]),
7794 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7795 );
7796 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";
7798 assert_eq!(
7799 f.run(&[
7800 b"ZRANGEBYSCORE",
7801 b"z",
7802 b"1",
7803 b"3",
7804 b"WITHSCORES",
7805 b"LIMIT",
7806 b"0",
7807 b"2"
7808 ]),
7809 both
7810 );
7811 assert_eq!(
7812 f.run(&[
7813 b"ZRANGEBYSCORE",
7814 b"z",
7815 b"1",
7816 b"3",
7817 b"LIMIT",
7818 b"0",
7819 b"2",
7820 b"WITHSCORES"
7821 ]),
7822 both
7823 );
7824 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
7827 assert_eq!(
7828 f.run(&[
7829 b"ZREVRANGE",
7830 b"z",
7831 b"0",
7832 b"-1",
7833 b"WITHSCORES",
7834 b"LIMIT",
7835 b"0",
7836 b"1"
7837 ]),
7838 needs_by
7839 );
7840 assert_eq!(
7841 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
7842 needs_by
7843 );
7844 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
7845 assert_eq!(
7846 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
7847 not_bylex
7848 );
7849 assert_eq!(
7850 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
7851 not_bylex
7852 );
7853 for cmd in [
7856 &[
7857 b"ZRANGE".as_slice(),
7858 b"z",
7859 b"0",
7860 b"-1",
7861 b"BYSCORE",
7862 b"BYLEX",
7863 ][..],
7864 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
7865 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
7866 ] {
7867 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
7868 }
7869 assert_eq!(
7870 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
7871 "-ERR min or max is not a float\r\n"
7872 );
7873 assert_eq!(
7874 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
7875 "-ERR min or max not valid string range item\r\n"
7876 );
7877 assert_eq!(
7878 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
7879 "-ERR value is not an integer or out of range\r\n"
7880 );
7881 }
7882
7883 #[test]
7886 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
7887 let mut f = Fixture::new();
7888 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7889 assert_eq!(
7890 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
7891 "*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"
7892 );
7893 f.out = Out::new(Proto::Resp3);
7894 assert_eq!(
7895 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
7896 "*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"
7897 );
7898 assert_eq!(
7899 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7900 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7901 );
7902 }
7903
7904 #[test]
7906 fn a_range_store_writes_the_window_into_another_key() {
7907 let mut f = Fixture::new();
7908 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7909 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
7910 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
7913 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7914 assert_eq!(
7915 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
7916 ":2\r\n"
7917 );
7918 assert_eq!(
7919 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
7920 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7921 );
7922 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
7925 assert_eq!(
7926 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
7927 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7928 );
7929 assert_eq!(
7932 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
7933 "-ERR syntax error\r\n"
7934 );
7935 }
7936
7937 #[test]
7940 fn the_three_removals_share_their_window_with_the_reads() {
7941 let mut f = Fixture::new();
7942 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7943 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
7944 assert_eq!(
7945 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7946 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7947 );
7948 assert_eq!(
7949 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
7950 ":1\r\n"
7951 );
7952 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
7953 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
7955 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7956 assert_eq!(
7957 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
7958 ":0\r\n"
7959 );
7960 assert_eq!(
7961 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
7962 "-ERR value is not an integer or out of range\r\n"
7963 );
7964 }
7965
7966 #[test]
7968 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
7969 let mut f = Fixture::new();
7970 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7971 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
7972 assert_eq!(
7973 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
7974 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
7975 );
7976 assert_eq!(
7979 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
7980 "*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"
7981 );
7982 assert_eq!(
7983 f.run(&[
7984 b"ZUNION",
7985 b"2",
7986 b"z",
7987 b"y",
7988 b"WEIGHTS",
7989 b"2",
7990 b"3",
7991 b"WITHSCORES"
7992 ]),
7993 "*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"
7994 );
7995 assert_eq!(
7996 f.run(&[
7997 b"ZUNION",
7998 b"2",
7999 b"z",
8000 b"y",
8001 b"AGGREGATE",
8002 b"MIN",
8003 b"WITHSCORES"
8004 ]),
8005 "*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"
8006 );
8007 assert_eq!(
8008 f.run(&[
8009 b"ZUNION",
8010 b"2",
8011 b"z",
8012 b"y",
8013 b"AGGREGATE",
8014 b"MAX",
8015 b"WITHSCORES"
8016 ]),
8017 "*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"
8018 );
8019 assert_eq!(
8020 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8021 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8022 );
8023 assert_eq!(
8024 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8025 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8026 );
8027 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8028 f.run(&[b"SADD", b"p", b"a", b"d"]);
8031 assert_eq!(
8032 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8033 "*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"
8034 );
8035 for cmd in [
8038 &[
8039 b"ZDIFF".as_slice(),
8040 b"2",
8041 b"z",
8042 b"y",
8043 b"WEIGHTS",
8044 b"1",
8045 b"1",
8046 ][..],
8047 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8048 ] {
8049 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8050 }
8051 }
8052
8053 #[test]
8055 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8056 let mut f = Fixture::new();
8057 f.run(&[b"ZADD", b"z", b"1", b"a"]);
8058 f.run(&[b"ZADD", b"y", b"2", b"b"]);
8059 assert_eq!(
8061 f.run(&[b"ZUNION", b"0", b"z"]),
8062 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8063 );
8064 assert_eq!(
8065 f.run(&[b"ZUNION", b"-1", b"z"]),
8066 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8067 );
8068 assert_eq!(
8069 f.run(&[b"ZINTERCARD", b"0", b"z"]),
8070 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8071 );
8072 assert_eq!(
8075 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8076 "-ERR syntax error\r\n"
8077 );
8078 assert_eq!(
8079 f.run(&[b"ZUNION", b"x", b"z"]),
8080 "-ERR value is not an integer or out of range\r\n"
8081 );
8082 assert_eq!(
8085 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8086 "-ERR syntax error\r\n"
8087 );
8088 assert_eq!(
8089 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8090 "-ERR weight value is not a float\r\n"
8091 );
8092 assert_eq!(
8093 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8094 "-ERR syntax error\r\n"
8095 );
8096 }
8097
8098 #[test]
8100 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8101 let mut f = Fixture::new();
8102 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8103 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8104 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8105 assert_eq!(
8106 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8107 "*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"
8108 );
8109 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8110 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8111 assert_eq!(
8114 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8115 ":0\r\n"
8116 );
8117 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8118 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8120 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8121 for cmd in [
8122 &[
8123 b"ZUNIONSTORE".as_slice(),
8124 b"d",
8125 b"2",
8126 b"z",
8127 b"y",
8128 b"WITHSCORES",
8129 ][..],
8130 &[
8131 b"ZDIFFSTORE",
8132 b"d",
8133 b"2",
8134 b"z",
8135 b"y",
8136 b"WEIGHTS",
8137 b"1",
8138 b"1",
8139 ],
8140 ] {
8141 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8142 }
8143 }
8144
8145 #[test]
8147 fn intercard_counts_and_stops_at_its_limit() {
8148 let mut f = Fixture::new();
8149 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8150 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8151 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8152 assert_eq!(
8154 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8155 ":2\r\n"
8156 );
8157 assert_eq!(
8158 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8159 ":1\r\n"
8160 );
8161 let bad = "-ERR LIMIT can't be negative\r\n";
8164 assert_eq!(
8165 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8166 bad
8167 );
8168 assert_eq!(
8169 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8170 bad
8171 );
8172 for cmd in [
8173 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8174 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8175 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8176 ] {
8177 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8178 }
8179 }
8180
8181 #[test]
8183 fn a_draw_answers_one_member_or_an_array_of_them() {
8184 let mut f = Fixture::new();
8185 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8186 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8189 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8190 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8191 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8192 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8195 assert!(all.starts_with("*3\r\n"), "{all}");
8196 for m in ["a", "b", "c"] {
8197 assert!(all.contains(m), "{all}");
8198 }
8199 assert!(
8202 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8203 "five draws with replacement"
8204 );
8205 assert!(
8206 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8207 .starts_with("*4\r\n"),
8208 "two pairs, flat on RESP2"
8209 );
8210 f.out = Out::new(Proto::Resp3);
8211 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8212 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8213 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8214 f.out = Out::new(Proto::Resp2);
8215 assert_eq!(
8216 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8217 "-ERR syntax error\r\n"
8218 );
8219 assert_eq!(
8220 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8221 "-ERR value is not an integer or out of range\r\n"
8222 );
8223 }
8224
8225 #[test]
8227 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8228 let mut f = Fixture::new();
8229 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8230 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";
8231 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8232 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8233 assert_eq!(
8234 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8235 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8236 );
8237 assert_eq!(
8238 f.run(&[b"ZSCAN", b"nokey", b"0"]),
8239 "*2\r\n$1\r\n0\r\n*0\r\n"
8240 );
8241 f.out = Out::new(Proto::Resp3);
8244 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8245 f.out = Out::new(Proto::Resp2);
8246 assert_eq!(
8247 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8248 "-ERR NOVALUES option can only be used in HSCAN\r\n"
8249 );
8250 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8251 assert_eq!(
8252 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8253 "-ERR syntax error\r\n"
8254 );
8255 }
8256
8257 #[test]
8259 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8260 let mut f = Fixture::new();
8261 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8262 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8264 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8265 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8266 assert_eq!(
8268 f.run(&[b"ZPOPMIN", b"z", b"2"]),
8269 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8270 );
8271 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8274 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8275 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8276 assert_eq!(
8278 f.run(&[b"ZPOPMIN", b"z", b"9"]),
8279 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8280 );
8281 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8282
8283 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8284 f.out = Out::new(Proto::Resp3);
8285 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8286 assert_eq!(
8287 f.run(&[b"ZPOPMIN", b"z", b"1"]),
8288 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8289 );
8290 f.out = Out::new(Proto::Resp2);
8291 let bad = "-ERR value is out of range, must be positive\r\n";
8294 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8295 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8296 assert_eq!(
8297 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8298 "-ERR syntax error\r\n"
8299 );
8300 }
8301
8302 #[test]
8304 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8305 let mut f = Fixture::new();
8306 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8307 assert_eq!(
8308 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8309 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8310 );
8311 assert_eq!(
8314 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8315 "*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"
8316 );
8317 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8319 f.out = Out::new(Proto::Resp3);
8320 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8321 f.out = Out::new(Proto::Resp2);
8322 let numkeys = "-ERR numkeys should be greater than 0\r\n";
8323 for bad in [
8324 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8325 &[b"ZMPOP", b"-1", b"z", b"MIN"],
8326 &[b"ZMPOP", b"x", b"z", b"MIN"],
8327 ] {
8328 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8329 }
8330 let count = "-ERR count should be greater than 0\r\n";
8331 for bad in [
8332 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8333 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8334 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8335 ] {
8336 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8337 }
8338 let syntax = "-ERR syntax error\r\n";
8339 for bad in [
8340 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8343 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8344 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8345 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8346 ] {
8347 assert_eq!(f.run(bad), syntax, "{bad:?}");
8348 }
8349 }
8350
8351 #[test]
8354 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8355 let mut f = Fixture::new();
8356 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8357 assert_eq!(
8358 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8359 (
8360 Flow::Continue,
8361 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8362 )
8363 );
8364 assert_eq!(
8365 f.run(&[b"BZPOPMAX", b"z", b"0"]),
8366 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8367 );
8368 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8369 assert_eq!(
8370 f.run(&[
8371 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8372 ]),
8373 "*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"
8374 );
8375 f.out = Out::new(Proto::Resp3);
8376 assert_eq!(
8377 f.run(&[b"BZPOPMIN", b"z", b"0"]),
8378 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8379 );
8380 f.out = Out::new(Proto::Resp2);
8381 assert_eq!(
8383 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8384 (Flow::Block, String::new())
8385 );
8386 assert_eq!(
8387 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
8388 (Flow::Block, String::new())
8389 );
8390 assert_eq!(
8393 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
8394 "-ERR timeout is not a float or out of range\r\n"
8395 );
8396 assert_eq!(
8397 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
8398 "-ERR numkeys should be greater than 0\r\n"
8399 );
8400 assert_eq!(
8401 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
8402 "-ERR timeout is negative\r\n"
8403 );
8404 }
8405
8406 #[test]
8410 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
8411 let mut f = Fixture::new();
8412 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
8413 assert_eq!(f.server.waiters().len(), 1);
8414 f.run(&[b"SET", b"z", b"v"]);
8417 let mut out = Out::new(Proto::Resp2);
8418 assert!(!f.server.serve_waiter(0, 0, &mut out));
8419 assert!(out.as_slice().is_empty());
8420 f.run(&[b"DEL", b"z"]);
8421 f.run(&[b"ZADD", b"z", b"5", b"m"]);
8422 assert!(f.server.serve_waiter(0, 0, &mut out));
8423 assert_eq!(
8424 core::str::from_utf8(out.as_slice()).expect("ascii"),
8425 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
8426 );
8427 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8430 }
8431
8432 #[test]
8433 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
8434 let mut f = Fixture::new();
8435 f.run(&[b"SET", b"s", b"v"]);
8436 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8437 for cmd in [
8438 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
8439 &[b"ZINCRBY", b"s", b"1", b"a"],
8440 &[b"ZCARD", b"s"],
8441 &[b"ZSCORE", b"s", b"a"],
8442 &[b"ZMSCORE", b"s", b"a"],
8443 &[b"ZREM", b"s", b"a"],
8444 &[b"ZRANK", b"s", b"a"],
8445 &[b"ZREVRANK", b"s", b"a"],
8446 &[b"ZCOUNT", b"s", b"1", b"2"],
8447 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
8448 &[b"ZRANGE", b"s", b"0", b"-1"],
8449 &[b"ZREVRANGE", b"s", b"0", b"-1"],
8450 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
8451 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
8452 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
8453 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
8454 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
8455 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
8456 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
8457 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
8458 &[b"ZUNION", b"1", b"s"],
8459 &[b"ZINTER", b"1", b"s"],
8460 &[b"ZDIFF", b"1", b"s"],
8461 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
8462 &[b"ZINTERSTORE", b"d", b"1", b"s"],
8463 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
8464 &[b"ZINTERCARD", b"1", b"s"],
8465 &[b"ZRANDMEMBER", b"s"],
8466 &[b"ZSCAN", b"s", b"0"],
8467 &[b"ZPOPMIN", b"s"],
8468 &[b"ZPOPMAX", b"s", b"2"],
8469 &[b"ZMPOP", b"1", b"s", b"MIN"],
8470 &[b"BZPOPMIN", b"s", b"0"],
8471 &[b"BZPOPMAX", b"s", b"0"],
8472 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
8473 ] {
8474 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
8475 }
8476 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
8477 }
8478
8479 #[test]
8483 fn churning_sorted_sets_does_not_grow_the_server() {
8484 let mut f = Fixture::new();
8485 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
8486 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
8487 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
8488 for i in 0..200 {
8489 args.push(&scores[i]);
8490 args.push(&members[i]);
8491 }
8492
8493 f.run(&args);
8494 f.run(&[b"DEL", b"z"]);
8495 f.server.compact_step();
8496 let after_first = f.server.memory_bytes();
8497
8498 for _ in 0..200 {
8499 f.run(&args);
8500 f.run(&[b"DEL", b"z"]);
8501 f.server.compact_step();
8502 }
8503 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8504 assert!(
8505 f.server.memory_bytes() <= after_first * 2,
8506 "held {} after two hundred passes against {after_first} after one",
8507 f.server.memory_bytes()
8508 );
8509 }
8510
8511 fn sicily(f: &mut Fixture) {
8519 f.run(&[
8520 b"GEOADD",
8521 b"Sicily",
8522 b"13.361389",
8523 b"38.115556",
8524 b"Palermo",
8525 b"15.087269",
8526 b"37.502669",
8527 b"Catania",
8528 ]);
8529 f.run(&[
8530 b"GEOADD",
8531 b"Sicily",
8532 b"13.583333",
8533 b"37.316667",
8534 b"Agrigento",
8535 ]);
8536 }
8537
8538 #[test]
8539 fn places_go_in_as_scores_and_come_back_as_positions() {
8540 let mut f = Fixture::new();
8541 assert_eq!(
8542 f.run(&[
8543 b"GEOADD",
8544 b"Sicily",
8545 b"13.361389",
8546 b"38.115556",
8547 b"Palermo",
8548 b"15.087269",
8549 b"37.502669",
8550 b"Catania"
8551 ]),
8552 ":2\r\n"
8553 );
8554 assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
8558 assert_eq!(
8559 f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
8560 "$16\r\n3479099956230698\r\n"
8561 );
8562 assert_eq!(
8563 f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
8564 "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
8565 );
8566 assert_eq!(
8567 f.run(&[
8568 b"GEOHASH",
8569 b"Sicily",
8570 b"Palermo",
8571 b"Catania",
8572 b"NonExisting"
8573 ]),
8574 "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
8575 );
8576 assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
8580 assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
8581 }
8582
8583 #[test]
8584 fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
8585 let mut f = Fixture::new();
8586 sicily(&mut f);
8587 assert_eq!(
8588 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
8589 "$11\r\n166274.1516\r\n"
8590 );
8591 assert_eq!(
8592 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
8593 "$8\r\n166.2742\r\n"
8594 );
8595 assert_eq!(
8596 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
8597 "$8\r\n103.3182\r\n"
8598 );
8599 assert_eq!(
8603 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
8604 "$-1\r\n"
8605 );
8606 assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
8607 assert_eq!(
8608 f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
8609 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
8610 );
8611 assert_eq!(
8612 f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
8613 "-ERR syntax error\r\n"
8614 );
8615 }
8616
8617 #[test]
8618 fn a_search_finds_what_is_inside_it_nearest_first() {
8619 let mut f = Fixture::new();
8620 sicily(&mut f);
8621 let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
8622 assert_eq!(
8623 f.run(&[
8624 b"GEOSEARCH",
8625 b"Sicily",
8626 b"FROMLONLAT",
8627 b"15",
8628 b"37",
8629 b"BYRADIUS",
8630 b"200",
8631 b"km",
8632 b"ASC"
8633 ]),
8634 all
8635 );
8636 assert_eq!(
8639 f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
8640 all
8641 );
8642 assert_eq!(
8643 f.run(&[
8644 b"GEORADIUS_RO",
8645 b"Sicily",
8646 b"15",
8647 b"37",
8648 b"200",
8649 b"km",
8650 b"ASC"
8651 ]),
8652 all
8653 );
8654 assert_eq!(
8657 f.run(&[
8658 b"GEORADIUS",
8659 b"Sicily",
8660 b"15",
8661 b"37",
8662 b"200",
8663 b"km",
8664 b"DESC",
8665 b"COUNT",
8666 b"1"
8667 ]),
8668 "*1\r\n$7\r\nPalermo\r\n"
8669 );
8670 assert_eq!(
8671 f.run(&[
8672 b"GEORADIUS",
8673 b"Sicily",
8674 b"15",
8675 b"37",
8676 b"200",
8677 b"km",
8678 b"COUNT",
8679 b"1"
8680 ]),
8681 "*1\r\n$7\r\nCatania\r\n"
8682 );
8683 let empty = "*0\r\n";
8686 assert_eq!(
8687 f.run(&[
8688 b"GEOSEARCH",
8689 b"Sicily",
8690 b"FROMLONLAT",
8691 b"15",
8692 b"37",
8693 b"BYRADIUS",
8694 b"1",
8695 b"km"
8696 ]),
8697 empty
8698 );
8699 assert_eq!(
8700 f.run(&[
8701 b"GEOSEARCH",
8702 b"nokey",
8703 b"FROMLONLAT",
8704 b"15",
8705 b"37",
8706 b"BYRADIUS",
8707 b"1",
8708 b"km"
8709 ]),
8710 empty
8711 );
8712 assert_eq!(
8713 f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
8714 empty
8715 );
8716 }
8717
8718 #[test]
8719 fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
8720 let mut f = Fixture::new();
8721 sicily(&mut f);
8722 assert_eq!(
8723 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
8724 "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
8725 );
8726 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";
8729 assert_eq!(
8730 f.run(&[
8731 b"GEORADIUSBYMEMBER_RO",
8732 b"Sicily",
8733 b"Agrigento",
8734 b"100",
8735 b"km",
8736 b"WITHDIST"
8737 ]),
8738 with_dist
8739 );
8740 assert_eq!(
8741 f.run(&[
8742 b"GEOSEARCH",
8743 b"Sicily",
8744 b"FROMMEMBER",
8745 b"Agrigento",
8746 b"BYRADIUS",
8747 b"100",
8748 b"km",
8749 b"ASC",
8750 b"WITHDIST"
8751 ]),
8752 with_dist
8753 );
8754 assert_eq!(
8755 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
8756 "-ERR could not decode requested zset member\r\n"
8757 );
8758 }
8759
8760 #[test]
8761 fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
8762 let mut f = Fixture::new();
8763 sicily(&mut f);
8764 assert_eq!(
8768 f.run(&[
8769 b"GEOSEARCH",
8770 b"Sicily",
8771 b"FROMLONLAT",
8772 b"15",
8773 b"37",
8774 b"BYBOX",
8775 b"400",
8776 b"400",
8777 b"km",
8778 b"ASC",
8779 b"WITHCOORD",
8780 b"WITHDIST",
8781 b"WITHHASH"
8782 ]),
8783 "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
8784 $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
8785 *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
8786 $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
8787 *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
8788 $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
8789 );
8790 }
8791
8792 #[test]
8793 fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
8794 let mut f = Fixture::new();
8795 sicily(&mut f);
8796 let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
8797 $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
8798 $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
8799 assert_eq!(
8800 f.run(&[
8801 b"GEOSEARCHSTORE",
8802 b"dst",
8803 b"Sicily",
8804 b"FROMLONLAT",
8805 b"15",
8806 b"37",
8807 b"BYRADIUS",
8808 b"200",
8809 b"km",
8810 b"ASC"
8811 ]),
8812 ":3\r\n"
8813 );
8814 assert_eq!(
8815 f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
8816 hashes
8817 );
8818 assert_eq!(
8821 f.run(&[
8822 b"GEORADIUS",
8823 b"Sicily",
8824 b"15",
8825 b"37",
8826 b"200",
8827 b"km",
8828 b"STORE",
8829 b"dst3"
8830 ]),
8831 ":3\r\n"
8832 );
8833 assert_eq!(
8834 f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
8835 hashes
8836 );
8837 assert_eq!(
8846 f.run(&[
8847 b"GEOSEARCHSTORE",
8848 b"dst2",
8849 b"Sicily",
8850 b"FROMLONLAT",
8851 b"15",
8852 b"37",
8853 b"BYRADIUS",
8854 b"200",
8855 b"km",
8856 b"ASC",
8857 b"STOREDIST"
8858 ]),
8859 ":3\r\n"
8860 );
8861 for (member, want) in [
8862 ("Catania", 56.441_257_870_158_19),
8863 ("Agrigento", 130.423_487_067_147_14),
8864 ("Palermo", 190.442_429_847_757_92),
8865 ] {
8866 let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
8867 let got: f64 = reply
8868 .trim_start_matches(|c: char| c != '\n')
8869 .trim()
8870 .parse()
8871 .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
8872 assert!(
8873 (got - want).abs() < 1e-9,
8874 "{member} scored {got} not {want}"
8875 );
8876 }
8877 assert_eq!(
8880 f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
8881 "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
8882 );
8883 assert_eq!(
8887 f.run(&[
8888 b"GEOSEARCHSTORE",
8889 b"dst",
8890 b"nokey",
8891 b"FROMLONLAT",
8892 b"15",
8893 b"37",
8894 b"BYRADIUS",
8895 b"200",
8896 b"km"
8897 ]),
8898 ":0\r\n"
8899 );
8900 assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
8901 }
8902
8903 #[test]
8904 fn the_gates_on_geoadd_are_the_ones_zadd_has() {
8905 let mut f = Fixture::new();
8906 sicily(&mut f);
8907 assert_eq!(
8910 f.run(&[
8911 b"GEOADD",
8912 b"Sicily",
8913 b"XX",
8914 b"CH",
8915 b"13.361389",
8916 b"38.115556",
8917 b"Palermo"
8918 ]),
8919 ":0\r\n"
8920 );
8921 assert_eq!(
8922 f.run(&[
8923 b"GEOADD",
8924 b"Sicily",
8925 b"NX",
8926 b"13.361389",
8927 b"38.9",
8928 b"Palermo"
8929 ]),
8930 ":0\r\n"
8931 );
8932 assert_eq!(
8933 f.run(&[
8934 b"GEOADD",
8935 b"Sicily",
8936 b"CH",
8937 b"13.361389",
8938 b"38.9",
8939 b"Palermo"
8940 ]),
8941 ":1\r\n"
8942 );
8943 assert_eq!(
8946 f.run(&[
8947 b"GEOADD",
8948 b"new",
8949 b"13.361389",
8950 b"38.115556",
8951 b"here",
8952 b"181",
8953 b"38",
8954 b"there"
8955 ]),
8956 "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
8957 );
8958 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
8959 assert_eq!(
8960 f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
8961 "-ERR value is not a valid float\r\n"
8962 );
8963 assert_eq!(
8966 f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
8967 "-ERR syntax error\r\n"
8968 );
8969 assert_eq!(
8970 f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
8971 "-ERR syntax error\r\n"
8972 );
8973 assert_eq!(
8974 f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
8975 "-ERR syntax error\r\n"
8976 );
8977 assert_eq!(
8978 f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
8979 "-ERR wrong number of arguments for 'geoadd' command\r\n"
8980 );
8981 }
8982
8983 #[test]
8986 fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
8987 let mut f = Fixture::new();
8988 sicily(&mut f);
8989 let cases: &[(&[&[u8]], &str)] = &[
8990 (
8991 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
8992 "-ERR need numeric radius\r\n",
8993 ),
8994 (
8995 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
8996 "-ERR radius cannot be negative\r\n",
8997 ),
8998 (
8999 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9000 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9001 ),
9002 (
9003 &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9004 "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9005 ),
9006 (
9007 &[
9008 b"GEOSEARCH",
9009 b"Sicily",
9010 b"FROMLONLAT",
9011 b"15",
9012 b"37",
9013 b"BYBOX",
9014 b"x",
9015 b"1",
9016 b"km",
9017 ],
9018 "-ERR need numeric width\r\n",
9019 ),
9020 (
9021 &[
9022 b"GEOSEARCH",
9023 b"Sicily",
9024 b"FROMLONLAT",
9025 b"15",
9026 b"37",
9027 b"BYBOX",
9028 b"1",
9029 b"y",
9030 b"km",
9031 ],
9032 "-ERR need numeric height\r\n",
9033 ),
9034 (
9035 &[
9036 b"GEOSEARCH",
9037 b"Sicily",
9038 b"FROMLONLAT",
9039 b"15",
9040 b"37",
9041 b"BYBOX",
9042 b"-1",
9043 b"1",
9044 b"km",
9045 ],
9046 "-ERR height or width cannot be negative\r\n",
9047 ),
9048 (
9049 &[
9050 b"GEOSEARCH",
9051 b"Sicily",
9052 b"FROMLONLAT",
9053 b"15",
9054 b"37",
9055 b"BYRADIUS",
9056 b"1",
9057 b"km",
9058 b"ANY",
9059 ],
9060 "-ERR the ANY argument requires COUNT argument\r\n",
9061 ),
9062 (
9063 &[
9064 b"GEOSEARCH",
9065 b"Sicily",
9066 b"FROMLONLAT",
9067 b"15",
9068 b"37",
9069 b"BYRADIUS",
9070 b"1",
9071 b"km",
9072 b"COUNT",
9073 b"0",
9074 ],
9075 "-ERR COUNT must be > 0\r\n",
9076 ),
9077 (
9078 &[
9079 b"GEOSEARCH",
9080 b"Sicily",
9081 b"BYRADIUS",
9082 b"1",
9083 b"km",
9084 b"BYBOX",
9085 b"1",
9086 b"1",
9087 b"km",
9088 ],
9089 "-ERR syntax error\r\n",
9090 ),
9091 (
9092 &[
9093 b"GEOSEARCH",
9094 b"Sicily",
9095 b"FROMMEMBER",
9096 b"Palermo",
9097 b"FROMLONLAT",
9098 b"1",
9099 b"2",
9100 b"BYRADIUS",
9101 b"1",
9102 b"km",
9103 ],
9104 "-ERR syntax error\r\n",
9105 ),
9106 (
9109 &[
9110 b"geosearch",
9111 b"Sicily",
9112 b"BYRADIUS",
9113 b"1",
9114 b"km",
9115 b"ASC",
9116 b"WITHDIST",
9117 ],
9118 "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9119 ),
9120 (
9121 &[
9122 b"GEOSEARCH",
9123 b"Sicily",
9124 b"FROMLONLAT",
9125 b"15",
9126 b"37",
9127 b"ASC",
9128 b"WITHDIST",
9129 ],
9130 "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9131 ),
9132 (
9135 &[
9136 b"GEOSEARCHSTORE",
9137 b"d",
9138 b"Sicily",
9139 b"FROMLONLAT",
9140 b"15",
9141 b"37",
9142 b"BYRADIUS",
9143 b"1",
9144 b"km",
9145 b"WITHCOORD",
9146 ],
9147 "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9148 ),
9149 (
9150 &[
9151 b"GEORADIUS",
9152 b"Sicily",
9153 b"15",
9154 b"37",
9155 b"1",
9156 b"km",
9157 b"WITHDIST",
9158 b"STORE",
9159 b"d",
9160 ],
9161 "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9162 ),
9163 (
9166 &[
9167 b"GEORADIUS_RO",
9168 b"Sicily",
9169 b"15",
9170 b"37",
9171 b"1",
9172 b"km",
9173 b"STORE",
9174 b"d",
9175 ],
9176 "-ERR syntax error\r\n",
9177 ),
9178 (
9179 &[
9180 b"GEOSEARCH",
9181 b"Sicily",
9182 b"FROMLONLAT",
9183 b"15",
9184 b"37",
9185 b"BYRADIUS",
9186 b"1",
9187 b"km",
9188 b"STOREDIST",
9189 ],
9190 "-ERR syntax error\r\n",
9191 ),
9192 ];
9193 for (parts, want) in cases {
9194 assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9195 }
9196 }
9197
9198 #[test]
9201 fn every_geo_command_says_wrongtype() {
9202 let mut f = Fixture::new();
9203 f.run(&[b"SET", b"s", b"v"]);
9204 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9205 let cases: &[&[&[u8]]] = &[
9206 &[b"GEOADD", b"s", b"13", b"38", b"m"],
9207 &[b"GEOPOS", b"s", b"m"],
9208 &[b"GEOHASH", b"s", b"m"],
9209 &[b"GEODIST", b"s", b"a", b"b"],
9210 &[
9211 b"GEOSEARCH",
9212 b"s",
9213 b"FROMLONLAT",
9214 b"15",
9215 b"37",
9216 b"BYRADIUS",
9217 b"1",
9218 b"km",
9219 ],
9220 &[
9221 b"GEOSEARCHSTORE",
9222 b"d",
9223 b"s",
9224 b"FROMLONLAT",
9225 b"15",
9226 b"37",
9227 b"BYRADIUS",
9228 b"1",
9229 b"km",
9230 ],
9231 &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9232 &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9233 &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9234 &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9235 ];
9236 for case in cases {
9237 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9238 }
9239 assert_eq!(
9242 f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9243 wrong
9244 );
9245 }
9246
9247 #[test]
9250 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9251 let mut f = Fixture::new();
9252 assert_eq!(
9255 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9256 ":3\r\n"
9257 );
9258 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9259 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9260 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9261 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9263 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9264 assert_eq!(
9265 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9266 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9267 );
9268 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9270 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9271 }
9272
9273 #[test]
9276 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9277 let mut f = Fixture::new();
9278 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9279 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9280 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9281 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9282 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9283 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9285 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9286 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9287
9288 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9292 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9293 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9294 assert_eq!(
9297 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9298 "-ERR array index overflow\r\n"
9299 );
9300 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9301 }
9302
9303 #[test]
9306 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9307 let mut f = Fixture::new();
9308 f.run(&[b"ARSET", b"a", b"1", b"x"]);
9309 assert_eq!(
9310 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9311 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9312 );
9313 assert_eq!(
9316 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9317 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9318 );
9319 assert_eq!(
9321 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9322 "*2\r\n$-1\r\n$-1\r\n"
9323 );
9324 assert_eq!(
9328 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9329 "-ERR range exceeds maximum of 1000000 items\r\n"
9330 );
9331 }
9332
9333 #[test]
9336 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9337 let mut f = Fixture::new();
9338 assert_eq!(
9339 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9340 "-ERR invalid array index\r\n"
9341 );
9342 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9343 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9344 assert_eq!(
9345 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9346 "-ERR invalid array index\r\n"
9347 );
9348 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9349 assert_eq!(
9352 f.run(&[b"ARGET", b"a", b"-1"]),
9353 "-ERR invalid array index\r\n"
9354 );
9355 assert_eq!(
9358 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9359 "-ERR wrong number of arguments for 'armset' command\r\n"
9360 );
9361 assert_eq!(
9362 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9363 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9364 );
9365 }
9366
9367 #[test]
9368 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9369 let mut f = Fixture::new();
9370 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9371 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9372 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9373 assert_eq!(
9376 f.run(&[
9377 b"ARDELRANGE",
9378 b"a",
9379 b"100",
9380 b"200",
9381 b"0",
9382 b"18446744073709551614"
9383 ]),
9384 ":2\r\n"
9385 );
9386 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9387 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
9388 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
9389 }
9390
9391 #[test]
9394 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
9395 let mut f = Fixture::new();
9396 let long = vec![b'v'; 200];
9397 f.run(&[
9398 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
9399 b"short", b"5", &long, b"6", b"-0",
9400 ]);
9401 assert_eq!(
9405 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
9406 format!(
9407 "*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",
9408 String::from_utf8_lossy(&long)
9409 )
9410 );
9411 }
9412
9413 #[test]
9414 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
9415 let mut f = Fixture::new();
9416 f.run(&[b"ARSET", b"a", b"0", b"x"]);
9417 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
9418 assert_eq!(
9419 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
9420 "$12\r\nsliced-array\r\n"
9421 );
9422 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
9424 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
9425 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
9426 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
9427 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
9428 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
9429 }
9430
9431 #[test]
9432 fn every_array_command_refuses_a_key_holding_something_else() {
9433 let mut f = Fixture::new();
9434 f.run(&[b"SET", b"s", b"v"]);
9435 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9436 for cmd in [
9437 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
9438 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
9439 &[b"ARGET".as_ref(), b"s", b"0"][..],
9440 &[b"ARMGET".as_ref(), b"s", b"0"][..],
9441 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
9442 &[b"ARLEN".as_ref(), b"s"][..],
9443 &[b"ARCOUNT".as_ref(), b"s"][..],
9444 &[b"ARDEL".as_ref(), b"s", b"0"][..],
9445 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
9446 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
9447 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
9448 &[b"ARNEXT".as_ref(), b"s"][..],
9449 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
9450 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
9451 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
9452 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
9453 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
9454 &[b"ARINFO".as_ref(), b"s"][..],
9455 ] {
9456 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
9457 }
9458 }
9459
9460 #[test]
9464 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
9465 let mut f = Fixture::new();
9466 f.run(&[b"SET", b"s", b"v"]);
9467 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9468 let bad = "-ERR invalid array index\r\n";
9469 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
9470 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
9471 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
9472 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
9473 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
9474 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
9475 f.run(&[b"ARSET", b"a", b"0", b"x"]);
9477 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
9478 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
9479 }
9480
9481 #[test]
9482 fn an_append_follows_a_cursor_the_client_can_move() {
9483 let mut f = Fixture::new();
9484 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
9485 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
9486 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
9487 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
9488 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
9489
9490 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
9493 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
9494 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
9495 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
9496 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
9497 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
9498 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
9499
9500 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
9503 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
9504 assert_eq!(
9505 f.run(&[b"ARINSERT", b"a", b"x"]),
9506 "-ERR insert index overflow\r\n"
9507 );
9508 assert_eq!(
9509 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
9510 "-ERR invalid array index\r\n"
9511 );
9512 }
9513
9514 #[test]
9515 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
9516 let mut f = Fixture::new();
9517 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
9518 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
9519 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
9520 assert_eq!(
9521 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
9522 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
9523 );
9524 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
9527 assert_eq!(
9528 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
9529 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
9530 );
9531 assert_eq!(
9534 f.run(&[b"ARRING", b"r", b"0", b"x"]),
9535 "-ERR size must be positive\r\n"
9536 );
9537 assert_eq!(
9538 f.run(&[b"ARRING", b"r", b"big", b"x"]),
9539 "-ERR invalid size\r\n"
9540 );
9541 }
9542
9543 #[test]
9544 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
9545 let mut f = Fixture::new();
9546 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
9547 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
9548 assert_eq!(
9549 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
9550 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
9551 );
9552 assert_eq!(
9553 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
9554 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
9555 );
9556 assert_eq!(
9557 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
9558 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
9559 "more than there is gets what there is"
9560 );
9561 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
9564 assert_eq!(
9565 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
9566 "-ERR syntax error\r\n"
9567 );
9568 assert_eq!(
9569 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
9570 "-ERR invalid COUNT\r\n"
9571 );
9572
9573 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
9576 assert_eq!(
9577 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
9578 "*2\r\n$-1\r\n$1\r\nz\r\n"
9579 );
9580 }
9581
9582 #[test]
9583 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
9584 let mut f = Fixture::new();
9585 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
9586 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
9587 assert_eq!(
9590 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
9591 "*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"
9592 );
9593 assert_eq!(
9594 f.run(&[
9595 b"ARSCAN",
9596 b"a",
9597 b"18446744073709551614",
9598 b"0",
9599 b"LIMIT",
9600 b"1"
9601 ]),
9602 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
9603 );
9604 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
9605 assert_eq!(
9606 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
9607 "-ERR LIMIT must be positive\r\n"
9608 );
9609 assert_eq!(
9610 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
9611 "-ERR syntax error\r\n"
9612 );
9613 assert_eq!(
9614 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
9615 "-ERR wrong number of arguments for 'arscan' command\r\n"
9616 );
9617 }
9618
9619 #[test]
9620 fn a_grep_answers_the_indexes_whose_elements_match() {
9621 let mut f = Fixture::new();
9622 assert_eq!(
9623 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
9624 "*0\r\n"
9625 );
9626 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
9627
9628 assert_eq!(
9631 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
9632 "*3\r\n:0\r\n:1\r\n:2\r\n"
9633 );
9634 assert_eq!(
9635 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
9636 "*3\r\n:2\r\n:1\r\n:0\r\n"
9637 );
9638 assert_eq!(
9639 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
9640 "*2\r\n:1\r\n:2\r\n"
9641 );
9642
9643 assert_eq!(
9646 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
9647 "*1\r\n:0\r\n"
9648 );
9649 assert_eq!(
9650 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
9651 "*2\r\n:0\r\n:3\r\n"
9652 );
9653 assert_eq!(
9654 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
9655 "*1\r\n:2\r\n"
9656 );
9657 assert_eq!(
9658 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
9659 "*2\r\n:1\r\n:2\r\n"
9660 );
9661
9662 let both: &[&[u8]] = &[
9665 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
9666 ];
9667 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
9668 assert_eq!(
9669 f.run(&[
9670 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
9671 ]),
9672 "*0\r\n"
9673 );
9674 assert_eq!(
9675 f.run(&[
9676 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
9677 ]),
9678 "*2\r\n:0\r\n:1\r\n"
9679 );
9680
9681 assert_eq!(
9684 f.run(&[
9685 b"ARGREP",
9686 b"a",
9687 b"-",
9688 b"+",
9689 b"MATCH",
9690 b"a",
9691 b"WITHVALUES",
9692 b"LIMIT",
9693 b"2"
9694 ]),
9695 "*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"
9696 );
9697 assert_eq!(
9698 f.run(&[
9699 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
9700 ]),
9701 "*1\r\n:3\r\n"
9702 );
9703 }
9704
9705 #[test]
9707 fn a_grep_reports_a_broken_command_the_way_redis_does() {
9708 let mut f = Fixture::new();
9709 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
9710 let syntax = "-ERR syntax error\r\n";
9711
9712 assert_eq!(
9715 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
9716 "-ERR invalid array index\r\n"
9717 );
9718 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
9719 assert_eq!(
9721 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
9722 syntax
9723 );
9724 assert_eq!(
9725 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
9726 syntax
9727 );
9728 assert_eq!(
9729 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
9730 syntax,
9731 "a command with no predicate in it at all"
9732 );
9733 assert_eq!(
9734 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
9735 "-ERR LIMIT must be positive\r\n"
9736 );
9737 assert_eq!(
9738 f.run(&[
9739 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
9740 ]),
9741 "-ERR value is not an integer or out of range\r\n"
9742 );
9743 assert_eq!(
9744 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
9745 "-ERR regular expression is empty\r\n"
9746 );
9747 assert_eq!(
9748 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
9749 "-ERR invalid regular expression: Missing ')'\r\n"
9750 );
9751 assert_eq!(
9752 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
9753 "-ERR regular expression backreferences are not supported\r\n"
9754 );
9755 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
9758 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
9759 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
9760 }
9761
9762 #[test]
9763 fn an_op_reduces_a_range_to_one_number() {
9764 let mut f = Fixture::new();
9765 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
9766 assert_eq!(
9767 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
9768 "$4\r\n-0.5\r\n"
9769 );
9770 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
9771 assert_eq!(
9772 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
9773 "$3\r\n2.5\r\n"
9774 );
9775 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
9776 assert_eq!(
9777 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
9778 ":1\r\n"
9779 );
9780 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
9783 assert_eq!(
9784 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
9785 "$19\r\n0.30000000000000004\r\n"
9786 );
9787 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
9788 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
9789
9790 f.run(&[b"ARSET", b"w", b"0", b"word"]);
9793 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
9794 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
9795 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
9796
9797 assert_eq!(
9798 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
9799 "-ERR unknown operation\r\n"
9800 );
9801 assert_eq!(
9802 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
9803 "-ERR MATCH requires a value argument\r\n"
9804 );
9805 assert_eq!(
9806 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
9807 "-ERR wrong number of arguments for 'arop' command\r\n"
9808 );
9809 }
9810
9811 #[test]
9812 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
9813 let mut f = Fixture::new();
9814 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
9815 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
9816 let short = f.run(&[b"ARINFO", b"a"]);
9817 assert!(
9818 short.starts_with("*14\r\n"),
9819 "seven pairs on RESP2: {short}"
9820 );
9821 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
9822 assert!(
9823 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
9824 "{short}"
9825 );
9826 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
9827 let full = f.run(&[b"ARINFO", b"a", b"full"]);
9828 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
9829 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
9832 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
9833 assert!(
9834 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
9835 "{full}"
9836 );
9837 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
9838
9839 let mut g = Fixture::new();
9841 g.run(&[b"HELLO", b"3"]);
9842 g.run(&[b"ARINSERT", b"a", b"x"]);
9843 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
9844 assert!(map.starts_with("%12\r\n"), "{map}");
9845 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
9846 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
9847 }
9848
9849 #[test]
9850 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
9851 let mut f = Fixture::new();
9852 for (score, want) in [
9855 ("3", "3"),
9856 ("3.5", "3.5"),
9857 ("0.3", "0.3"),
9858 ("1e30", "1e+30"),
9859 ("1e19", "1e+19"),
9860 ("1e-7", "1e-7"),
9861 ("0.000001", "0.000001"),
9862 ("4611686018427387904", "4611686018427387904"),
9863 ("-0", "-0"),
9864 ] {
9865 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
9866 assert_eq!(
9867 f.run(&[b"ZSCORE", b"z", b"m"]),
9868 format!("${}\r\n{want}\r\n", want.len()),
9869 "score {score}"
9870 );
9871 }
9872
9873 let mut g = Fixture::new();
9876 g.run(&[b"HELLO", b"3"]);
9877 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
9878 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
9879 assert_eq!(
9884 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
9885 "$31\r\n1000000000000000000000000000000\r\n"
9886 );
9887 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
9888 assert_eq!(
9889 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
9890 "$20\r\n10000000000000000000\r\n"
9891 );
9892 }
9893
9894 #[test]
9897 fn a_node_comes_back_with_the_fields_it_went_in_with() {
9898 let mut f = Fixture::new();
9899 assert_eq!(
9900 f.run(&[
9901 b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
9902 ]),
9903 ":1\r\n"
9904 );
9905 assert_eq!(
9911 f.run(&[b"G.NGET", b"social", b"ada"]),
9912 "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
9913 );
9914 assert_eq!(
9917 f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
9918 ":0\r\n"
9919 );
9920 assert_eq!(
9921 f.run(&[b"G.NGET", b"social", b"ada"]),
9922 "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
9923 );
9924 assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
9927 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
9928 assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
9929 assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
9930
9931 assert_eq!(
9934 f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
9935 "-ERR syntax error\r\n"
9936 );
9937 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
9938
9939 let mut g = Fixture::new();
9941 g.run(&[b"HELLO", b"3"]);
9942 g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
9943 assert_eq!(
9944 g.run(&[b"G.NGET", b"social", b"ada"]),
9945 "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
9946 );
9947 }
9948
9949 #[test]
9950 fn an_edge_creates_the_ends_it_needs() {
9951 let mut f = Fixture::new();
9952 assert_eq!(
9953 f.run(&[
9954 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
9955 ]),
9956 ":1\r\n"
9957 );
9958 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
9960 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
9961 assert_eq!(
9962 f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
9963 "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
9964 );
9965 assert_eq!(
9966 f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
9967 "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
9968 );
9969 assert_eq!(
9972 f.run(&[
9973 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
9974 ]),
9975 ":0\r\n"
9976 );
9977 assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
9978 assert_eq!(
9980 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
9981 ":1\r\n"
9982 );
9983 assert_eq!(
9984 f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
9985 ":1\r\n"
9986 );
9987
9988 assert_eq!(
9989 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
9990 ":1\r\n"
9991 );
9992 assert_eq!(
9993 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
9994 ":0\r\n"
9995 );
9996 assert_eq!(
9999 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10000 ":0\r\n"
10001 );
10002 assert_eq!(
10003 f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10004 ":0\r\n"
10005 );
10006 assert_eq!(
10007 f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10008 ":0\r\n"
10009 );
10010 }
10011
10012 #[test]
10015 fn a_hop_answers_a_cursor_and_a_page() {
10016 let mut f = Fixture::new();
10017 for i in 0..25u32 {
10018 let dst = format!("n{i}");
10019 f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10020 }
10021 let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10023 assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10024
10025 let mut seen = 0;
10026 let mut cursor = String::from("0");
10027 loop {
10028 let page = f.run(&[
10029 b"G.OUT",
10030 b"social",
10031 b"hub",
10032 b"FOLLOWS",
10033 b"COUNT",
10034 b"7",
10035 b"CURSOR",
10036 cursor.as_bytes(),
10037 ]);
10038 let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10039 cursor = head
10040 .rsplit("\r\n")
10041 .next()
10042 .expect("the cursor line")
10043 .to_string();
10044 seen += rest
10045 .split_once("\r\n")
10046 .expect("the page length")
10047 .0
10048 .parse::<usize>()
10049 .expect("a length");
10050 if cursor == "0" {
10051 break;
10052 }
10053 }
10054 assert_eq!(seen, 25, "every neighbour once across the pages");
10055
10056 assert_eq!(
10059 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10060 "*2\r\n$1\r\n0\r\n*0\r\n"
10061 );
10062 assert_eq!(
10063 f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10064 "*2\r\n$1\r\n0\r\n*0\r\n"
10065 );
10066 assert_eq!(
10067 f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10068 "*2\r\n$1\r\n0\r\n*0\r\n"
10069 );
10070 assert_eq!(
10071 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10072 "-ERR COUNT must be a positive integer\r\n"
10073 );
10074 assert_eq!(
10075 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10076 "-ERR syntax error\r\n"
10077 );
10078 }
10079
10080 #[test]
10081 fn a_degree_counts_one_way_or_both() {
10082 let mut f = Fixture::new();
10083 f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10084 f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10085 f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10086 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10087 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10088 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10089 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10090 assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10091 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10092 assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10093 assert_eq!(
10094 f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10095 "-ERR syntax error\r\n"
10096 );
10097 }
10098
10099 #[test]
10102 fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10103 let mut f = Fixture::new();
10104 for (src, dst) in [
10105 ("ada", "grace"),
10106 ("ada", "alan"),
10107 ("grace", "edsger"),
10108 ("alan", "edsger"),
10109 ("edsger", "barbara"),
10110 ] {
10111 f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10112 }
10113 assert_eq!(
10116 f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10117 "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10118 );
10119 assert_eq!(
10120 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10121 "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10122 );
10123 let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10124 assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10125 assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10126 assert_eq!(
10128 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10129 "*1\r\n$5\r\ngrace\r\n"
10130 );
10131 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10133 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10134 assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10135 assert_eq!(
10136 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10137 "-ERR DEPTH must be a positive integer\r\n"
10138 );
10139 assert_eq!(
10140 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10141 "-ERR syntax error\r\n"
10142 );
10143 }
10144
10145 #[test]
10148 fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10149 let mut f = Fixture::new();
10150 for i in 0..6u32 {
10153 let src = format!("n{i}");
10154 let dst = format!("n{}", i + 1);
10155 f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10156 }
10157 assert_eq!(
10158 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10159 "*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"
10160 );
10161 f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10162 assert_eq!(
10163 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10164 "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10165 );
10166 assert_eq!(
10169 f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10170 "*1\r\n$2\r\nn2\r\n"
10171 );
10172 assert_eq!(
10173 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10174 "*0\r\n"
10175 );
10176 assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10178 f.run(&[b"G.NADD", b"road", b"island"]);
10181 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10182 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10183 assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10184 assert_eq!(
10185 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10186 "-ERR syntax error\r\n"
10187 );
10188 }
10189
10190 #[test]
10194 fn the_keyspace_sees_a_graph_key_like_any_other() {
10195 let mut f = Fixture::new();
10196 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10197 assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10198 assert_eq!(
10199 f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10200 "$9\r\nadjacency\r\n"
10201 );
10202 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10203 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10204 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10205 let held = f.server.memory_bytes();
10209 for i in 0..200u32 {
10210 let dst = format!("n{i}");
10211 f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10212 }
10213 assert!(
10214 f.server.memory_bytes() > held,
10215 "two hundred edges cost something: {held} then {}",
10216 f.server.memory_bytes()
10217 );
10218 f.run(&[b"DEL", b"big"]);
10219
10220 assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10223 assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10224 assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10225 assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10226 assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10227 f.run(&[b"SELECT", b"1"]);
10228 assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10229
10230 assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10231 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10232 f.run(&[b"G.NADD", b"g", b"n"]);
10233 assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10234 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10235 }
10236
10237 #[test]
10240 fn a_graph_cannot_be_copied_or_dumped() {
10241 let mut f = Fixture::new();
10242 f.run(&[b"G.NADD", b"social", b"ada"]);
10243 assert_eq!(
10244 f.run(&[b"COPY", b"social", b"other"]),
10245 "-ERR COPY is not supported for a graph\r\n"
10246 );
10247 assert_eq!(
10248 f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10249 "-ERR COPY is not supported for a graph\r\n"
10250 );
10251 assert_eq!(
10252 f.run(&[b"DUMP", b"social"]),
10253 "-ERR DUMP is not supported for a graph\r\n"
10254 );
10255 assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10257 }
10258
10259 #[test]
10262 fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10263 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10264 let mut f = Fixture::new();
10265 f.run(&[b"G.NADD", b"social", b"ada"]);
10266 assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10267 assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10268 assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10269
10270 f.run(&[b"SET", b"str", b"v"]);
10271 for cmd in [
10272 vec![b"G.NADD".as_ref(), b"str", b"n"],
10273 vec![b"G.NGET".as_ref(), b"str", b"n"],
10274 vec![b"G.NDEL".as_ref(), b"str", b"n"],
10275 vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10276 vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10277 vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10278 vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10279 vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10280 vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10281 vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10282 ] {
10283 assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10284 }
10285 }
10286
10287 #[test]
10290 fn a_graph_goes_when_its_last_node_does() {
10291 let mut f = Fixture::new();
10292 f.run(&[
10293 b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10294 ]);
10295 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10296 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10298 assert_eq!(
10299 f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10300 ":0\r\n"
10301 );
10302 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10303 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10304
10305 assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10306 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10307 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10308 assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10309
10310 f.run(&[b"G.NADD", b"social", b"first"]);
10313 f.run(&[b"G.NADD", b"social", b"second"]);
10314 f.run(&[b"G.NDEL", b"social", b"first"]);
10315 f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10316 assert_eq!(
10317 f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10318 "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10319 );
10320 }
10321
10322 #[test]
10327 fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10328 let mut f = Fixture::new();
10329 let doc = br#"{"a":1,"b":{"c":true}}"#;
10330 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10331 assert_eq!(
10334 f.run(&[b"JSON.GET", b"doc"]),
10335 bulk(r#"{"a":1,"b":{"c":true}}"#)
10336 );
10337 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10338 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10339 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10340 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10343 assert_eq!(
10344 f.run(&[b"JSON.GET", b"doc", b".nope"]),
10345 "-ERR Path does not exist\r\n"
10346 );
10347 assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10348 assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10351 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10352 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10353 assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10354 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10355 }
10356
10357 #[test]
10363 fn the_two_json_errors_that_carry_no_prefix() {
10364 let mut f = Fixture::new();
10365 f.run(&[b"SET", b"plain", b"x"]);
10366 let wrong = "-Existing key has wrong Redis type\r\n";
10367 assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10368 assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10369 assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10370 assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10371 assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10372
10373 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10374 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10378 assert_eq!(
10379 f.run(&[b"JSON.GET", b"doc"]),
10380 bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10381 );
10382 assert_eq!(
10383 f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10384 "-Err wrong static path\r\n"
10385 );
10386 }
10387
10388 #[test]
10390 fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10391 let mut f = Fixture::new();
10392 assert_eq!(
10394 f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
10395 "-ERR new objects must be created at the root\r\n"
10396 );
10397 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
10398 assert_eq!(
10401 f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
10402 "-ERR new objects must be created at the root\r\n"
10403 );
10404 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
10405 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
10406
10407 f.run(&[
10408 b"JSON.SET",
10409 b"doc",
10410 b"$",
10411 br#"{"o":{},"arr":[1,2],"s":"x"}"#,
10412 ]);
10413 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10415 assert_eq!(
10418 f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
10419 "$-1\r\n"
10420 );
10421 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
10422 assert_eq!(
10424 f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
10425 "-ERR array index out of range\r\n"
10426 );
10427 assert_eq!(
10428 f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
10429 "-ERR array index out of range\r\n"
10430 );
10431 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
10432 assert_eq!(
10435 f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
10436 "$-1\r\n"
10437 );
10438 assert_eq!(
10439 f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
10440 "$-1\r\n"
10441 );
10442 assert_eq!(
10443 f.run(&[b"JSON.GET", b"doc"]),
10444 bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
10445 );
10446 assert!(
10450 f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
10451 .starts_with("-this is not the start of a value")
10452 );
10453 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
10454 }
10455
10456 #[test]
10459 fn the_json_commands_that_do_not_answer_text() {
10460 let mut f = Fixture::new();
10461 let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
10462 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10463
10464 assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
10465 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
10466 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
10467 assert_eq!(
10468 f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
10469 format!("*1\r\n{}", bulk("integer"))
10470 );
10471 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
10474 assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
10475
10476 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
10479 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
10480 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
10483 assert_eq!(
10484 f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
10485 "-ERR Path does not exist or not a bool\r\n"
10486 );
10487 assert_eq!(
10488 f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
10489 "-ERR Path does not exist or not a bool\r\n"
10490 );
10491 assert_eq!(
10492 f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
10493 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10494 );
10495
10496 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
10499 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
10500 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
10501 assert_eq!(
10502 f.run(&[b"JSON.GET", b"doc"]),
10503 bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
10504 );
10505
10506 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
10509 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
10510 assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
10515 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
10516 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10517 assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
10518 assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
10519 assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
10520 assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
10521 assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
10522 }
10523
10524 #[test]
10531 fn json_get_lays_out_the_wrapper_it_builds() {
10532 let mut f = Fixture::new();
10533 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
10534
10535 assert_eq!(
10536 f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
10537 bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
10538 );
10539 assert_eq!(
10541 f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
10542 bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
10543 );
10544 let fmt: &[&[u8]] = &[b"INDENT", b" ", b"NEWLINE", b"\n", b"SPACE", b" "];
10545 let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
10546 one.extend_from_slice(fmt);
10547 one.push(b"$.b");
10548 assert_eq!(
10549 f.run(&one),
10550 bulk("[\n [\n 1,\n {\n \"c\": 2\n }\n ]\n]")
10551 );
10552 let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
10553 two.extend_from_slice(fmt);
10554 two.push(b"$.a");
10555 two.push(b"$.nope");
10556 assert_eq!(
10557 f.run(&two),
10558 bulk("{\n \"$.a\": [\n 1\n ],\n \"$.nope\": []\n}")
10559 );
10560 let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
10563 root.push(b".a");
10564 assert_eq!(f.run(&root), bulk("1"));
10565 }
10566
10567 #[test]
10570 fn json_mget_answers_once_per_key_whatever_is_under_them() {
10571 let mut f = Fixture::new();
10572 f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
10573 f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
10574 f.run(&[b"SET", b"plain", b"x"]);
10575 assert_eq!(
10576 f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
10577 format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
10578 );
10579 assert_eq!(
10582 f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
10583 format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
10584 );
10585 assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
10588 }
10589
10590 #[test]
10598 fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
10599 let mut f = Fixture::new();
10600 let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
10601 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10602
10603 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
10604 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
10605 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
10606 assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
10607 assert_eq!(
10608 f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
10609 format!("*2\r\n{}{}", bulk("x"), bulk("y"))
10610 );
10611 assert_eq!(
10614 f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
10615 "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
10616 );
10617
10618 assert_eq!(
10621 f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
10622 "-ERR Path does not exist\r\n"
10623 );
10624 assert_eq!(
10625 f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
10626 "-ERR Path does not exist\r\n"
10627 );
10628 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
10629 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
10632 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
10635
10636 assert_eq!(
10640 f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
10641 "-ERR Path does not exist or not an array\r\n"
10642 );
10643 assert_eq!(
10644 f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
10645 "-ERR Path does not exist or not an object\r\n"
10646 );
10647 assert_eq!(
10648 f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
10649 "-WRONGTYPE wrong type of path value - expected object\r\n"
10650 );
10651 assert_eq!(
10652 f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
10653 "-WRONGTYPE wrong type of path value - expected string\r\n"
10654 );
10655
10656 assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
10659 assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
10660 assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
10661 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
10662 assert_eq!(
10663 f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
10664 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10665 );
10666 assert_eq!(
10668 f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
10669 "-ERR Path does not exist or not an object\r\n"
10670 );
10671 }
10672
10673 #[test]
10679 fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
10680 let mut f = Fixture::new();
10681 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
10682
10683 assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
10684 assert_eq!(
10685 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
10686 "*1\r\n:6\r\n"
10687 );
10688 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
10689
10690 assert_eq!(
10693 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
10694 ":7\r\n"
10695 );
10696 assert_eq!(
10697 f.run(&[b"JSON.GET", b"doc", b".a"]),
10698 bulk("[1,2,3,4,5,0,6]")
10699 );
10700 assert_eq!(
10701 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
10702 ":8\r\n"
10703 );
10704 assert_eq!(
10706 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
10707 "-ERR index out of bounds\r\n"
10708 );
10709 assert_eq!(
10710 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
10711 "-ERR index out of bounds\r\n"
10712 );
10713
10714 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
10717 assert_eq!(
10718 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
10719 ":3\r\n"
10720 );
10721 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
10722 assert_eq!(
10723 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
10724 ":2\r\n"
10725 );
10726 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
10727 assert_eq!(
10728 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
10729 ":0\r\n"
10730 );
10731 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10732
10733 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
10736 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
10737 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
10738 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
10739 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
10740
10741 for call in [
10744 &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
10745 &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
10746 &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
10747 &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
10748 ] {
10749 for path in [&b".n"[..], &b".nope"[..]] {
10750 let args: Vec<&[u8]> = call
10751 .iter()
10752 .map(|a| if *a == b"PATH" { path } else { *a })
10753 .collect();
10754 assert_eq!(
10755 f.run(&args),
10756 "-ERR Path does not exist or not an array\r\n",
10757 "{} {}",
10758 String::from_utf8_lossy(call[0]),
10759 String::from_utf8_lossy(path)
10760 );
10761 }
10762 }
10763
10764 assert_eq!(
10767 f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
10768 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10769 );
10770 assert_eq!(
10771 f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
10772 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10773 );
10774
10775 assert!(
10780 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
10781 .starts_with("-this is not the start of a value")
10782 );
10783 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10784 }
10785
10786 #[test]
10794 fn json_arrinsert_is_all_or_nothing_across_the_matches() {
10795 let mut f = Fixture::new();
10796 let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
10797 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10798 assert_eq!(
10799 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
10800 "-ERR index out of bounds\r\n"
10801 );
10802 assert_eq!(
10803 f.run(&[b"JSON.GET", b"doc"]),
10804 bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
10805 );
10806 assert_eq!(
10808 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
10809 "*3\r\n:4\r\n:3\r\n:2\r\n"
10810 );
10811 assert_eq!(
10812 f.run(&[b"JSON.GET", b"doc"]),
10813 bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
10814 );
10815 }
10816
10817 #[test]
10825 fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
10826 let mut f = Fixture::new();
10827 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
10828
10829 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
10830 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
10831 assert_eq!(
10832 f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
10833 "*1\r\n:1\r\n"
10834 );
10835
10836 assert_eq!(
10839 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
10840 ":3\r\n"
10841 );
10842 assert_eq!(
10845 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
10846 ":-1\r\n"
10847 );
10848
10849 for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
10852 assert_eq!(
10853 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
10854 ":3\r\n",
10855 "{}",
10856 String::from_utf8_lossy(start)
10857 );
10858 }
10859 assert_eq!(
10860 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
10861 ":0\r\n"
10862 );
10863 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
10866 assert_eq!(
10867 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
10868 ":-1\r\n"
10869 );
10870
10871 f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
10875 assert_eq!(
10876 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
10877 ":0\r\n"
10878 );
10879 assert_eq!(
10880 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
10881 ":1\r\n"
10882 );
10883 assert_eq!(
10884 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
10885 ":-1\r\n"
10886 );
10887
10888 assert_eq!(
10892 f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
10893 "-ERR Path does not exist\r\n"
10894 );
10895 assert_eq!(
10896 f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
10897 "-WRONGTYPE wrong type of path value - expected array\r\n"
10898 );
10899 assert_eq!(
10900 f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
10901 "-ERR Path does not exist\r\n"
10902 );
10903 assert_eq!(
10904 f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
10905 "-ERR Path does not exist\r\n"
10906 );
10907 }
10908
10909 #[test]
10912 fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
10913 let mut f = Fixture::new();
10914 let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
10915 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10916
10917 assert_eq!(
10920 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
10921 bulk("9").as_str()
10922 );
10923 assert_eq!(
10925 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
10926 bulk("[11]").as_str()
10927 );
10928 assert_eq!(
10931 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
10932 bulk("13.0").as_str()
10933 );
10934 assert_eq!(
10935 f.run(&[b"JSON.TYPE", b"doc", b".i"]),
10936 bulk("number").as_str()
10937 );
10938 assert_eq!(
10939 f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
10940 bulk("3.0").as_str()
10941 );
10942 assert_eq!(
10943 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
10944 bulk("-8").as_str()
10945 );
10946 f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
10949 assert_eq!(
10950 f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
10951 bulk("1.224744871391589").as_str()
10952 );
10953 assert_eq!(
10954 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
10955 "-ERR result is not a number\r\n"
10956 );
10957 f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
10961 assert_eq!(
10962 f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
10963 "-ERR numeric overflow\r\n"
10964 );
10965 f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
10966 assert_eq!(
10967 f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
10968 "-ERR numeric overflow\r\n"
10969 );
10970 f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
10972 assert_eq!(
10973 f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
10974 "-ERR result is not a number\r\n"
10975 );
10976
10977 assert_eq!(
10981 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
10982 bulk("[null]").as_str()
10983 );
10984 assert_eq!(
10985 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
10986 bulk("[]").as_str()
10987 );
10988 assert_eq!(
10989 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
10990 "-ERR Path does not exist or does not contains a number\r\n"
10991 );
10992 assert_eq!(
10993 f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
10994 "-ERR Path does not exist or does not contains a number\r\n"
10995 );
10996 assert_eq!(
10999 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11000 "-bad input number\r\n"
11001 );
11002 assert_eq!(
11003 f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11004 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11005 );
11006 assert_eq!(
11007 f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11008 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11009 );
11010 }
11011
11012 #[test]
11015 fn json_strappend_reads_its_shape_off_the_argument_count() {
11016 let mut f = Fixture::new();
11017 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11018
11019 assert_eq!(
11020 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11021 ":3\r\n"
11022 );
11023 assert_eq!(
11024 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11025 "*1\r\n:4\r\n"
11026 );
11027 assert_eq!(
11030 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11031 ":6\r\n"
11032 );
11033 f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11036 assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11037 assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11038
11039 assert_eq!(
11043 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11044 "-WRONGTYPE wrong type of path value - expected string\r\n"
11045 );
11046 assert_eq!(
11047 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11048 "*1\r\n$-1\r\n"
11049 );
11050 assert_eq!(
11051 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11052 "-ERR Path does not exist or not a string\r\n"
11053 );
11054 assert_eq!(
11055 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11056 "*0\r\n"
11057 );
11058 assert_eq!(
11059 f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11060 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11061 );
11062 }
11063
11064 #[test]
11067 fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11068 let mut f = Fixture::new();
11069 let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11072
11073 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11074 assert_eq!(
11075 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11076 ":4\r\n"
11077 );
11078 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11079 assert_eq!(
11080 f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11081 ":2\r\n"
11082 );
11083 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11084 assert_eq!(
11085 f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11086 ":1\r\n"
11087 );
11088 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11089 assert_eq!(
11090 f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11091 bulk("1").as_str()
11092 );
11093 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11094 assert_eq!(
11095 f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11096 bulk("13").as_str()
11097 );
11098 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11099 assert_eq!(
11100 f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11101 ":4\r\n"
11102 );
11103 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11104 assert_eq!(
11105 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11106 bulk("false").as_str()
11107 );
11108 assert_eq!(
11111 f.run(&[b"JSON.GET", b"doc", b".a"]),
11112 bulk("[false,true,false]").as_str()
11113 );
11114
11115 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11118 assert_eq!(
11119 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11120 ":3\r\n"
11121 );
11122 assert_eq!(
11123 f.run(&[b"JSON.GET", b"doc", b".a"]),
11124 bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11125 );
11126 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11129 assert_eq!(
11130 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11131 "-ERR Path does not exist or not an array\r\n"
11132 );
11133 assert_eq!(
11134 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11135 "-ERR Path does not exist or not a bool\r\n"
11136 );
11137 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11141 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11142 assert_eq!(
11143 f.run(&[b"JSON.GET", b"doc", b".a"]),
11144 bulk("[[],[2]]").as_str()
11145 );
11146 }
11147
11148 #[test]
11151 fn a_write_reaches_a_match_that_sits_inside_another_match() {
11152 let mut f = Fixture::new();
11153 let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11154
11155 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11156 assert_eq!(
11157 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11158 "*3\r\n:3\r\n:2\r\n:3\r\n"
11159 );
11160 assert_eq!(
11161 f.run(&[b"JSON.GET", b"doc", b"$"]),
11162 bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11163 );
11164
11165 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11168 assert_eq!(
11169 f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11170 "*3\r\n:1\r\n:1\r\n:1\r\n"
11171 );
11172 assert_eq!(
11173 f.run(&[b"JSON.GET", b"doc", b"$"]),
11174 bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11175 );
11176
11177 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11180 assert_eq!(
11181 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11182 bulk("[null,8,8]").as_str()
11183 );
11184 }
11185
11186 #[test]
11189 fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11190 let mut f = Fixture::new();
11191 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11192
11193 assert_eq!(
11196 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11197 bulk("[null]").as_str()
11198 );
11199 assert_eq!(
11200 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11201 bulk("[null]").as_str()
11202 );
11203 assert_eq!(
11204 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11205 bulk("[]").as_str()
11206 );
11207 assert_eq!(
11208 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11209 "-ERR Path does not exist or does not contains a number\r\n"
11210 );
11211 assert_eq!(
11213 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11214 "-bad input number\r\n"
11215 );
11216
11217 assert_eq!(
11219 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11220 "*1\r\n$-1\r\n"
11221 );
11222 assert_eq!(
11223 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11224 "-ERR Path does not exist or not a string\r\n"
11225 );
11226 assert_eq!(
11227 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11228 "-WRONGTYPE wrong type of path value - expected string\r\n"
11229 );
11230
11231 assert_eq!(
11233 f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11234 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11235 );
11236 assert_eq!(
11237 f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11238 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11239 );
11240 }
11241
11242 #[test]
11245 fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11246 let mut f = Fixture::new();
11247
11248 assert_eq!(
11252 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11253 "+OK\r\n"
11254 );
11255 assert_eq!(
11256 f.run(&[b"JSON.GET", b"doc", b"$"]),
11257 bulk(r#"[{"x":null,"y":1}]"#).as_str()
11258 );
11259
11260 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11263 assert_eq!(
11264 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11265 "+OK\r\n"
11266 );
11267 assert_eq!(
11268 f.run(&[b"JSON.GET", b"doc", b"$"]),
11269 bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11270 );
11271
11272 assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11274 assert_eq!(
11275 f.run(&[b"JSON.GET", b"doc", b"$"]),
11276 bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11277 );
11278
11279 assert_eq!(
11283 f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11284 "+OK\r\n"
11285 );
11286 assert_eq!(
11287 f.run(&[b"JSON.GET", b"doc", b"$"]),
11288 bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11289 );
11290
11291 assert_eq!(
11295 f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11296 "+OK\r\n"
11297 );
11298 assert_eq!(
11299 f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11300 bulk(r#"[{"z":null}]"#).as_str()
11301 );
11302 assert_eq!(
11303 f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11304 "$-1\r\n"
11305 );
11306 assert_eq!(
11307 f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11308 "-Err wrong static path\r\n"
11309 );
11310
11311 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11313 assert_eq!(
11314 f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11315 "+OK\r\n"
11316 );
11317 assert_eq!(
11318 f.run(&[b"JSON.GET", b"doc", b"$"]),
11319 bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11320 );
11321
11322 assert_eq!(
11324 f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11325 "-ERR syntax error\r\n"
11326 );
11327 assert_eq!(
11328 f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11329 "-ERR new objects must be created at the root\r\n"
11330 );
11331 f.run(&[b"SET", b"str", b"x"]);
11332 assert_eq!(
11333 f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11334 "-Existing key has wrong Redis type\r\n"
11335 );
11336 }
11337
11338 #[test]
11341 fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11342 let mut f = Fixture::new();
11343 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11344 assert_eq!(
11345 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11346 "+OK\r\n"
11347 );
11348 assert_eq!(
11352 f.run(&[b"JSON.GET", b"doc", b"$"]),
11353 bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11354 );
11355
11356 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11359 assert_eq!(
11360 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11361 "+OK\r\n"
11362 );
11363 assert_eq!(
11364 f.run(&[b"JSON.GET", b"doc", b"$"]),
11365 bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11366 );
11367 }
11368
11369 #[test]
11372 fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11373 let mut f = Fixture::new();
11374 let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11375 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11376
11377 assert_eq!(
11378 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11379 bulk(r#"["a","c"]"#).as_str()
11380 );
11381 assert_eq!(
11384 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11385 bulk(r#"["a","c"]"#).as_str()
11386 );
11387 assert_eq!(
11389 f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
11390 bulk(r#""a""#).as_str()
11391 );
11392 assert_eq!(
11393 f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
11394 "*1\r\n$6\r\nobject\r\n"
11395 );
11396
11397 assert_eq!(
11401 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
11402 bulk("[9,10]").as_str()
11403 );
11404 assert_eq!(
11405 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
11406 "+OK\r\n"
11407 );
11408 assert_eq!(
11409 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
11410 "-Err wrong static path\r\n"
11411 );
11412 assert_eq!(
11413 f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
11414 ":2\r\n"
11415 );
11416 assert_eq!(
11417 f.run(&[b"JSON.GET", b"doc", b"$"]),
11418 bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
11419 );
11420
11421 assert!(
11424 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
11425 .starts_with("-ERR")
11426 );
11427 assert!(
11428 f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
11429 .starts_with("-ERR")
11430 );
11431 }
11432
11433 #[test]
11436 fn a_filter_takes_the_membership_operators_and_the_methods_too() {
11437 let mut f = Fixture::new();
11438 let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
11439 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11440
11441 for (path, want) in [
11442 (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
11443 (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
11444 (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
11445 (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
11446 (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
11447 (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
11448 (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
11449 (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
11450 (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
11451 (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
11452 (b"$.box[?(@.n~)].t", "[]"),
11453 (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
11454 (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
11455 (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
11456 (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
11457 ] {
11458 assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
11459 }
11460
11461 assert_eq!(
11464 f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
11465 "+OK\r\n"
11466 );
11467 assert_eq!(
11468 f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
11469 bulk(r#"["b"]"#).as_str()
11470 );
11471 }
11472
11473 #[test]
11476 fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
11477 let mut f = Fixture::new();
11478 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
11479 assert_eq!(
11480 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11481 "+OK\r\n"
11482 );
11483 assert_eq!(
11484 f.run(&[b"JSON.GET", b"doc", b"$"]),
11485 bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
11486 );
11487 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
11490 assert_eq!(
11491 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11492 "+OK\r\n"
11493 );
11494 assert_eq!(
11495 f.run(&[b"JSON.GET", b"doc", b"$"]),
11496 bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
11497 );
11498 }
11499
11500 #[test]
11503 fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
11504 let mut f = Fixture::new();
11505 assert_eq!(
11506 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
11507 "+OK\r\n"
11508 );
11509 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
11510 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
11511
11512 assert_eq!(
11514 f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
11515 "+OK\r\n"
11516 );
11517 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
11518
11519 f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
11524 assert_eq!(
11525 f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
11526 "$-1\r\n"
11527 );
11528 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
11529 assert_eq!(
11530 f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
11531 "$-1\r\n"
11532 );
11533 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11534
11535 f.run(&[b"SET", b"str", b"x"]);
11540 assert_eq!(
11541 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
11542 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
11543 );
11544 assert_eq!(
11545 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
11546 "-Existing key has wrong Redis type\r\n"
11547 );
11548 assert_eq!(
11549 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
11550 "-ERR new objects must be created at the root\r\n"
11551 );
11552 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
11553
11554 assert_eq!(
11559 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
11560 "-Err wrong static path\r\n"
11561 );
11562 assert_eq!(
11563 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
11564 "-ERR array index out of range\r\n"
11565 );
11566 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11567
11568 f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
11572 assert_eq!(
11573 f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
11574 "+OK\r\n"
11575 );
11576 assert_eq!(
11577 f.run(&[b"JSON.GET", b"c", b"$"]),
11578 bulk(r#"[{"n":3}]"#).as_str()
11579 );
11580
11581 assert_eq!(
11584 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
11585 "-ERR wrong number of arguments for 'json.mset' command\r\n"
11586 );
11587 }
11588
11589 #[test]
11592 fn json_resp_answers_the_document_as_resp_types() {
11593 let mut f = Fixture::new();
11594 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
11595 assert_eq!(
11596 f.run(&[b"JSON.RESP", b"doc"]),
11597 "*5\r\n+{\r\n$1\r\na\r\n:1\r\n$1\r\nb\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
11598 );
11599 assert_eq!(
11601 f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
11602 "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
11603 );
11604
11605 f.run(&[
11606 b"JSON.SET",
11607 b"doc",
11608 b"$",
11609 br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
11610 ]);
11611 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
11612 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
11613 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
11616 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
11617 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
11618
11619 assert_eq!(
11622 f.run(&[b"JSON.RESP", b"doc", b".nope"]),
11623 "-ERR Path does not exist\r\n"
11624 );
11625 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
11626 assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
11627 assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
11628 }
11629
11630 #[test]
11634 fn json_debug_answers_a_byte_count_and_its_own_help() {
11635 let mut f = Fixture::new();
11636 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
11637 let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
11638 assert!(one.starts_with(':'), "{one}");
11639 assert_eq!(
11640 f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
11641 format!("*1\r\n{one}")
11642 );
11643 let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
11644 assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
11645
11646 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
11650 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
11651 assert_eq!(
11652 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
11653 "-ERR Path does not exist\r\n"
11654 );
11655 assert_eq!(
11656 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
11657 "*0\r\n"
11658 );
11659
11660 assert_eq!(
11661 f.run(&[b"JSON.DEBUG", b"HELP"]),
11662 "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
11663 $34\r\nHELP - this message\r\n"
11664 );
11665 assert_eq!(
11666 f.run(&[b"JSON.DEBUG", b"NOPE"]),
11667 "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
11668 );
11669 assert_eq!(
11670 f.run(&[b"JSON.DEBUG", b"MEMORY"]),
11671 "-ERR wrong number of arguments for 'json.debug' command\r\n"
11672 );
11673 }
11674
11675 #[test]
11680 fn the_first_vadd_decides_how_wide_the_set_is() {
11681 let mut f = Fixture::new();
11682 assert_eq!(
11683 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
11684 ":1\r\n"
11685 );
11686 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
11687 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11688 assert_eq!(
11691 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
11692 ":0\r\n"
11693 );
11694 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11695 assert_eq!(
11698 f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
11699 "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
11700 );
11701 assert_eq!(
11704 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
11705 "-ERR a cosine collection compares directions and a vector of length zero has none\r\n"
11706 );
11707 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
11710 assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
11711 assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
11712 }
11713
11714 #[test]
11717 fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
11718 let mut f = Fixture::new();
11719 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
11720 assert_eq!(
11723 f.run(&[b"VEMB", b"v", b"a"]),
11724 "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
11725 );
11726 assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
11727 assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
11728
11729 let mut f = Fixture::new();
11733 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
11734 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
11735 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
11736 assert_eq!(
11737 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
11738 "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
11739 $8\r\nopposite\r\n$1\r\n0\r\n"
11740 );
11741 assert_eq!(
11744 f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
11745 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11746 );
11747 assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
11750 assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
11751 assert_eq!(
11754 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
11755 "*1\r\n$6\r\nacross\r\n"
11756 );
11757 assert_eq!(
11758 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
11759 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11760 );
11761 assert_eq!(
11765 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
11766 "*1\r\n$6\r\nacross\r\n"
11767 );
11768
11769 let mut g = Fixture::new();
11772 g.run(&[b"HELLO", b"3"]);
11773 g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11774 assert_eq!(
11775 g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
11776 "%1\r\n$4\r\neast\r\n,1\r\n"
11777 );
11778 }
11779
11780 #[test]
11782 fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
11783 let mut f = Fixture::new();
11784 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11785 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11786 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
11787 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
11788 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
11791 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
11792 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
11794 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11795 assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
11798 assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
11799 assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
11800 assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
11803 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
11804 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
11805 assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
11806
11807 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11809 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11810 assert_eq!(
11811 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
11812 "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
11813 );
11814 }
11815
11816 #[test]
11819 fn vrem_takes_the_attribute_with_it() {
11820 let mut f = Fixture::new();
11821 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11822 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11823 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
11824 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
11825 assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
11826 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
11829
11830 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11833 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11834 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11835 f.run(&[b"VREM", b"v", b"east"]);
11836 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
11837 assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
11838 }
11839
11840 #[test]
11843 fn vinfo_says_partition_first() {
11844 let mut f = Fixture::new();
11845 f.run(&[
11846 b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
11847 ]);
11848 f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
11849 let info = f.run(&[b"VINFO", b"v"]);
11850 assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
11851 assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
11854 assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
11855 assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
11856 assert!(
11859 info.contains("$10\r\nquant-type\r\n$3\r\nf32\r\n"),
11860 "{info}"
11861 );
11862 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
11863 assert!(
11864 f.run(&[b"VINFO", b"v"])
11865 .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
11866 );
11867 assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
11868 }
11869
11870 #[test]
11873 fn reduce_is_refused_and_not_ignored() {
11874 let mut f = Fixture::new();
11875 let reduce = f.run(&[
11876 b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
11877 ]);
11878 assert!(
11879 reduce.starts_with("-ERR REDUCE is not supported."),
11880 "{reduce}"
11881 );
11882 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
11883 }
11884
11885 #[test]
11888 fn vsim_filter_reads_the_attributes() {
11889 let mut f = Fixture::new();
11890 for (name, x, y, attr) in [
11891 ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
11892 ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
11893 ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
11894 ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
11895 ] {
11896 f.run(&[
11897 b"VADD",
11898 b"v",
11899 b"VALUES",
11900 b"2",
11901 x.as_bytes(),
11902 y.as_bytes(),
11903 name.as_bytes(),
11904 b"SETATTR",
11905 attr.as_bytes(),
11906 ]);
11907 }
11908 assert_eq!(
11911 f.run(&[
11912 b"VSIM",
11913 b"v",
11914 b"VALUES",
11915 b"2",
11916 b"9",
11917 b"1",
11918 b"COUNT",
11919 b"2",
11920 b"FILTER",
11921 b".lang == \"en\"",
11922 ]),
11923 "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
11924 );
11925 assert_eq!(
11928 f.run(&[
11929 b"VSIM",
11930 b"v",
11931 b"VALUES",
11932 b"2",
11933 b"9",
11934 b"1",
11935 b"FILTER",
11936 b".lang == 'en' and .year > 1980",
11937 ]),
11938 "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
11939 );
11940 assert_eq!(
11942 f.run(&[
11943 b"VSIM",
11944 b"v",
11945 b"VALUES",
11946 b"2",
11947 b"9",
11948 b"1",
11949 b"FILTER",
11950 b".lang in ['fr', 'de']",
11951 ]),
11952 "*1\r\n$1\r\nb\r\n"
11953 );
11954 assert_eq!(
11955 f.run(&[
11956 b"VSIM",
11957 b"v",
11958 b"VALUES",
11959 b"2",
11960 b"9",
11961 b"1",
11962 b"FILTER",
11963 b".rating > 3"
11964 ]),
11965 "*0\r\n"
11966 );
11967 assert_eq!(
11970 f.run(&[
11971 b"VSIM",
11972 b"v",
11973 b"VALUES",
11974 b"2",
11975 b"9",
11976 b"1",
11977 b"TRUTH",
11978 b"FILTER",
11979 b".year < 1980",
11980 ]),
11981 "*1\r\n$1\r\nc\r\n"
11982 );
11983 f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
11986 assert_eq!(
11987 f.run(&[
11988 b"VSIM",
11989 b"v",
11990 b"VALUES",
11991 b"2",
11992 b"9",
11993 b"1",
11994 b"COUNT",
11995 b"1",
11996 b"FILTER",
11997 b".lang == \"en\"",
11998 ]),
11999 "*1\r\n$1\r\nb\r\n"
12000 );
12001 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12004 assert_eq!(
12005 f.run(&[
12006 b"VSIM",
12007 b"v",
12008 b"VALUES",
12009 b"2",
12010 b"9",
12011 b"1",
12012 b"COUNT",
12013 b"1",
12014 b"FILTER",
12015 b".lang == \"en\"",
12016 ]),
12017 "*1\r\n$1\r\nb\r\n"
12018 );
12019
12020 let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12023 assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12024 assert_eq!(
12025 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12026 "-ERR invalid FILTER expression\r\n"
12027 );
12028 assert_eq!(
12031 f.run(&[
12032 b"VSIM",
12033 b"v",
12034 b"VALUES",
12035 b"2",
12036 b"9",
12037 b"1",
12038 b"COUNT",
12039 b"1",
12040 b"FILTER-EF",
12041 b"500",
12042 b"FILTER",
12043 b".lang == 'en'",
12044 ]),
12045 "*1\r\n$1\r\nb\r\n"
12046 );
12047 assert_eq!(
12048 f.run(&[
12049 b"VSIM",
12050 b"v",
12051 b"VALUES",
12052 b"2",
12053 b"9",
12054 b"1",
12055 b"COUNT",
12056 b"1",
12057 b"FILTER-EF",
12058 b"0"
12059 ]),
12060 "*1\r\n$1\r\nb\r\n"
12061 );
12062 assert_eq!(
12063 f.run(&[
12064 b"VSIM",
12065 b"v",
12066 b"VALUES",
12067 b"2",
12068 b"9",
12069 b"1",
12070 b"FILTER-EF",
12071 b"lots"
12072 ]),
12073 "-ERR EF must be a positive integer\r\n"
12074 );
12075 }
12076
12077 #[test]
12080 fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12081 let mut f = Fixture::new();
12082 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12083 assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12084 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12085 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12086 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12087 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12088 assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12089 assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12090 assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12091 assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12092 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12093
12094 f.run(&[b"SET", b"s", b"1"]);
12096 assert_eq!(
12097 f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12098 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12099 );
12100 assert_eq!(
12101 f.run(&[b"VCARD", b"s"]),
12102 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12103 );
12104 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12105 assert_eq!(
12106 f.run(&[b"GET", b"v"]),
12107 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12108 );
12109 f.run(&[b"G.NADD", b"social", b"ada"]);
12113 assert_eq!(
12114 f.run(&[b"VCARD", b"social"]),
12115 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12116 );
12117 assert_eq!(
12118 f.run(&[b"G.NGET", b"v", b"ada"]),
12119 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12120 );
12121 }
12122
12123 #[test]
12126 fn vrandmember_has_the_two_shapes_srandmember_has() {
12127 let mut f = Fixture::new();
12128 for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12129 let x = (i + 1).to_string();
12130 f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12131 }
12132 let one = f.run(&[b"VRANDMEMBER", b"v"]);
12134 assert!(one.starts_with("$1\r\n"), "{one}");
12135 let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12137 assert!(all.starts_with("*3\r\n"), "{all}");
12138 for name in ["a", "b", "c"] {
12139 assert!(all.contains(name), "{all} is missing {name}");
12140 }
12141 all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12142 assert!(all.starts_with("*2\r\n"), "{all}");
12143 let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12145 assert!(many.starts_with("*5\r\n"), "{many}");
12146 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12148 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12149 }
12150
12151 #[test]
12154 fn vlinks_reports_one_layer_of_partition_neighbours() {
12155 let mut f = Fixture::new();
12156 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12157 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12158 assert_eq!(
12161 f.run(&[b"VLINKS", b"v", b"east"]),
12162 "*1\r\n*1\r\n$5\r\nnorth\r\n"
12163 );
12164 assert_eq!(
12165 f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12166 "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12167 );
12168 assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12169 assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12170 }
12171
12172 #[test]
12175 fn fp32_and_values_are_the_same_vector() {
12176 let mut f = Fixture::new();
12177 let mut blob = Vec::new();
12178 for x in [3.0f32, 4.0] {
12179 blob.extend_from_slice(&x.to_le_bytes());
12180 }
12181 assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12182 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12183 assert_eq!(
12184 f.run(&[b"VEMB", b"v", b"a"]),
12185 "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
12186 );
12187 let raw = f.run(&[b"VEMB", b"v", b"a", b"RAW"]);
12190 assert!(raw.starts_with("*3\r\n$3\r\nf32\r\n$8\r\n"), "{raw}");
12191 assert!(raw.ends_with("$1\r\n5\r\n"), "{raw}");
12192 assert_eq!(
12194 f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12195 "-ERR invalid vector specification\r\n"
12196 );
12197 assert_eq!(
12199 f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12200 "-ERR syntax error\r\n"
12201 );
12202 assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12203 }
12204
12205 #[test]
12210 fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12211 let mut f = Fixture::new();
12212 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12213 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12214 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12215 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12216 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12217 assert_eq!(
12220 f.run(&[b"BF.INFO", b"b"]),
12221 "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12222 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12223 +Expansion rate\r\n:2\r\n"
12224 );
12225 assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12226 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12227 assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12230 assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12231 }
12232
12233 #[test]
12239 fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12240 let mut f = Fixture::new();
12241 f.run(&[b"SET", b"s", b"text"]);
12242 assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12243 assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12244 for cmd in [
12245 vec![&b"BF.ADD"[..], b"s", b"x"],
12246 vec![&b"BF.MADD"[..], b"s", b"x"],
12247 vec![&b"BF.CARD"[..], b"s"],
12248 vec![&b"BF.INFO"[..], b"s"],
12249 vec![&b"BF.DEBUG"[..], b"s"],
12250 vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
12251 ] {
12252 let name = String::from_utf8_lossy(cmd[0]).into_owned();
12253 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12254 }
12255 assert_eq!(
12258 f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
12259 "-ERR bad error rate\r\n"
12260 );
12261 assert!(
12262 f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
12263 .starts_with("-WRONGTYPE")
12264 );
12265 }
12266
12267 #[test]
12270 fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
12271 let mut f = Fixture::new();
12272 assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
12273 for i in 0..10u32 {
12274 assert_eq!(
12275 f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
12276 ":1\r\n"
12277 );
12278 }
12279 assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
12280 assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
12281 assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
12282 assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
12285 assert_eq!(
12286 f.run(&[b"BF.DEBUG", b"g"]),
12287 "*3\r\n$7\r\nsize:11\r\n\
12288 $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
12289 $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
12290 );
12291
12292 assert_eq!(
12294 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
12295 "+OK\r\n"
12296 );
12297 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
12298 assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
12299 assert_eq!(
12300 f.run(&[b"BF.ADD", b"n", b"c"]),
12301 "-ERR non scaling filter is full\r\n"
12302 );
12303 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12306 assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
12309 f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
12310 assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
12311 assert_eq!(
12314 f.run(&[
12315 b"BF.RESERVE",
12316 b"q",
12317 b"0.01",
12318 b"2",
12319 b"NONSCALING",
12320 b"EXPANSION",
12321 b"2"
12322 ]),
12323 "-Nonscaling filters cannot expand\r\n"
12324 );
12325 }
12326
12327 #[test]
12330 fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
12331 let mut f = Fixture::new();
12332 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
12333 assert_eq!(
12334 f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
12335 "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
12336 );
12337 assert_eq!(
12338 f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
12339 "*2\r\n:1\r\n:0\r\n"
12340 );
12341 }
12342
12343 #[test]
12346 fn insert_is_a_reserve_and_a_madd_with_different_errors() {
12347 let mut f = Fixture::new();
12348 assert_eq!(
12349 f.run(&[
12350 b"BF.INSERT",
12351 b"i",
12352 b"CAPACITY",
12353 b"50",
12354 b"ERROR",
12355 b"0.001",
12356 b"ITEMS",
12357 b"a",
12358 b"b"
12359 ]),
12360 "*2\r\n:1\r\n:1\r\n"
12361 );
12362 assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
12363 assert_eq!(
12365 f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
12366 "-ERR not found\r\n"
12367 );
12368 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
12369 assert_eq!(
12372 f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
12373 "-Bad capacity\r\n"
12374 );
12375 assert_eq!(
12376 f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
12377 "-Bad error rate\r\n"
12378 );
12379 assert_eq!(
12380 f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
12381 "-Bad expansion\r\n"
12382 );
12383 assert_eq!(
12388 f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
12389 "*1\r\n:1\r\n"
12390 );
12391 assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
12392 assert_eq!(
12396 f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
12397 "*1\r\n:1\r\n"
12398 );
12399 assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
12400 assert_eq!(
12401 f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
12402 "*1\r\n:1\r\n"
12403 );
12404 assert_eq!(
12405 f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
12406 "-ERR not found\r\n"
12407 );
12408 assert_eq!(
12410 f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12411 "-Unknown argument received\r\n"
12412 );
12413 assert_eq!(
12415 f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12416 "*1\r\n:1\r\n"
12417 );
12418 assert!(
12420 f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
12421 .contains("wrong number of arguments")
12422 );
12423 }
12424
12425 #[test]
12428 fn a_dump_replays_into_a_filter_that_answers_the_same() {
12429 let mut f = Fixture::new();
12430 f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
12431 for i in 0..25u32 {
12432 f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
12433 }
12434 assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
12435
12436 let mut iter = b"0".to_vec();
12439 let mut chunks = 0;
12440 loop {
12441 let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
12442 let text = String::from_utf8_lossy(&raw).into_owned();
12443 let next = text
12444 .split("\r\n")
12445 .nth(1)
12446 .and_then(|n| n.strip_prefix(':'))
12447 .expect("a two element reply of an iterator and a chunk")
12448 .to_owned();
12449 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
12450 let data = &body[body
12451 .windows(2)
12452 .position(|w| w == b"\r\n")
12453 .expect("a length line")
12454 + 2..body.len() - 2];
12455 if next == "0" {
12456 assert!(data.is_empty(), "the last chunk is empty");
12457 break;
12458 }
12459 let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
12460 assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
12461 iter = next.into_bytes();
12462 chunks += 1;
12463 }
12464 assert_eq!(chunks, 3, "a header and one chunk per link");
12465
12466 assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
12467 assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
12468 for i in 0..25u32 {
12469 assert_eq!(
12470 f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
12471 ":1\r\n"
12472 );
12473 }
12474
12475 assert_eq!(
12478 f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
12479 "-ERR received bad data\r\n"
12480 );
12481 assert_eq!(
12482 f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
12483 "-ERR received bad data\r\n"
12484 );
12485 assert_eq!(
12487 f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
12488 "-ERR invalid offset - no link found\r\n"
12489 );
12490 assert_eq!(
12491 f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
12492 "-ERR Second argument must be numeric\r\n"
12493 );
12494 assert_eq!(
12497 f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
12498 "-Second argument must be numeric\r\n"
12499 );
12500 }
12501
12502 #[test]
12505 fn reserve_reads_its_numbers_the_way_string2ll_does() {
12506 let mut f = Fixture::new();
12507 for (args, want) in [
12508 (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
12509 (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
12510 (
12511 vec![&b"0"[..], b"10"],
12512 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12513 ),
12514 (
12515 vec![&b"1"[..], b"10"],
12516 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12517 ),
12518 (
12519 vec![&b"inf"[..], b"10"],
12520 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12521 ),
12522 (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
12523 (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
12524 (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
12525 (
12526 vec![&b"0.01"[..], b"0"],
12527 "-ERR capacity must be in the range [1, 1073741824]\r\n",
12528 ),
12529 (
12530 vec![&b"0.01"[..], b"1073741825"],
12531 "-ERR capacity must be in the range [1, 1073741824]\r\n",
12532 ),
12533 ] {
12534 let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
12535 cmd.extend(args.iter().copied());
12536 assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
12537 }
12538 assert_eq!(
12539 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
12540 "-ERR no expansion\r\n"
12541 );
12542 assert_eq!(
12543 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
12544 "-ERR bad expansion\r\n"
12545 );
12546 assert_eq!(
12547 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
12548 "-ERR expansion must be in the range [0, 32768]\r\n"
12549 );
12550 assert_eq!(
12552 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
12553 "+OK\r\n"
12554 );
12555 assert_eq!(
12556 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
12557 "-ERR item exists\r\n"
12558 );
12559 assert_eq!(
12560 f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
12561 "-Invalid information value\r\n"
12562 );
12563 assert!(
12564 f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
12565 .contains("wrong number of arguments")
12566 );
12567 }
12568
12569 #[test]
12571 fn the_bloom_family_answers_in_resp3_spelling_too() {
12572 let mut f = Fixture::new();
12573 f.out.set_proto(Proto::Resp3);
12574 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
12575 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
12576 assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
12577 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
12578 assert_eq!(
12579 f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
12580 "*2\r\n#t\r\n#f\r\n"
12581 );
12582 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
12584 assert_eq!(
12585 f.run(&[b"BF.INFO", b"b"]),
12586 "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12587 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
12588 +Expansion rate\r\n:2\r\n"
12589 );
12590 assert_eq!(
12593 f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
12594 "%1\r\n+Capacity\r\n:100\r\n"
12595 );
12596 }
12597
12598 fn cf_header(
12607 items: u64,
12608 buckets: u64,
12609 deletes: u64,
12610 filters: u64,
12611 geometry: [u16; 3],
12612 ) -> Vec<u8> {
12613 let mut out = Vec::with_capacity(38);
12614 for n in [items, buckets, deletes, filters] {
12615 out.extend_from_slice(&n.to_le_bytes());
12616 }
12617 for n in geometry {
12618 out.extend_from_slice(&n.to_le_bytes());
12619 }
12620 out
12621 }
12622
12623 #[test]
12627 fn cf_add_makes_the_filter_and_counts_the_copies() {
12628 let mut f = Fixture::new();
12629 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12630 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12631 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
12632 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
12635 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
12636 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
12637 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
12638 assert_eq!(
12639 f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
12640 "*2\r\n:1\r\n:0\r\n"
12641 );
12642 assert_eq!(
12645 f.run(&[b"CF.INFO", b"d"]),
12646 "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
12647 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
12648 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
12649 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
12650 );
12651 assert_eq!(
12652 f.run(&[b"CF.DEBUG", b"d"]),
12653 "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
12654 max_iterations:20 expansion:1\r\n"
12655 );
12656 assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
12657 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
12658
12659 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12661 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
12662 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12663 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
12664 assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
12665
12666 assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
12669 assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
12670 assert_eq!(
12671 f.run(&[b"CF.COMPACT", b"gone"]),
12672 "-Cuckoo filter was not found\r\n"
12673 );
12674 assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
12675 assert!(
12679 f.run(&[b"CF.COMPACT", b"a", b"b"])
12680 .contains("wrong number of arguments")
12681 );
12682 }
12683
12684 #[test]
12687 fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
12688 let mut f = Fixture::new();
12689 f.run(&[b"SET", b"s", b"text"]);
12690 assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
12691 assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12692 assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
12693 assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
12697 assert_eq!(
12698 f.run(&[b"CF.COMPACT", b"s"]),
12699 "-Cuckoo filter was not found\r\n"
12700 );
12701 for cmd in [
12702 vec![&b"CF.ADD"[..], b"s", b"x"],
12703 vec![&b"CF.ADDNX"[..], b"s", b"x"],
12704 vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
12705 vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
12706 vec![&b"CF.INFO"[..], b"s"],
12707 vec![&b"CF.DEBUG"[..], b"s"],
12708 vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
12709 vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
12710 vec![&b"CF.RESERVE"[..], b"s", b"64"],
12711 ] {
12712 let name = String::from_utf8_lossy(cmd[0]).into_owned();
12713 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12714 }
12715 }
12716
12717 #[test]
12720 fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
12721 let mut f = Fixture::new();
12722 assert_eq!(
12723 f.run(&[
12724 b"CF.RESERVE",
12725 b"r",
12726 b"64",
12727 b"BUCKETSIZE",
12728 b"1",
12729 b"MAXITERATIONS",
12730 b"7",
12731 b"EXPANSION",
12732 b"4"
12733 ]),
12734 "+OK\r\n"
12735 );
12736 assert_eq!(
12737 f.run(&[b"CF.DEBUG", b"r"]),
12738 "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
12739 max_iterations:7 expansion:4\r\n"
12740 );
12741 assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
12742
12743 assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
12744 assert_eq!(
12745 f.run(&[b"CF.RESERVE", b"q", b"1"]),
12746 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
12747 );
12748 assert_eq!(
12751 f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
12752 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
12753 );
12754 assert_eq!(
12755 f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
12756 "+OK\r\n"
12757 );
12758
12759 assert_eq!(
12764 f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
12765 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
12766 );
12767 assert_eq!(
12768 f.run(&[
12769 b"CF.RESERVE",
12770 b"q2",
12771 b"64",
12772 b"EXPANSION",
12773 b"xx",
12774 b"BUCKETSIZE",
12775 b"0"
12776 ]),
12777 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
12778 );
12779 assert_eq!(
12780 f.run(&[
12781 b"CF.RESERVE",
12782 b"q2",
12783 b"64",
12784 b"MAXITERATIONS",
12785 b"0",
12786 b"BUCKETSIZE",
12787 b"0"
12788 ]),
12789 "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
12790 );
12791 assert_eq!(
12795 f.run(&[
12796 b"CF.RESERVE",
12797 b"q3",
12798 b"64",
12799 b"BUCKETSIZE",
12800 b"1",
12801 b"BUCKETSIZE",
12802 b"0"
12803 ]),
12804 "+OK\r\n"
12805 );
12806 assert_eq!(
12809 f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
12810 "+OK\r\n"
12811 );
12812 assert_eq!(
12813 f.run(&[b"CF.DEBUG", b"q4"]),
12814 "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
12815 max_iterations:20 expansion:1\r\n"
12816 );
12817 assert!(
12820 f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
12821 .contains("wrong number of arguments")
12822 );
12823 }
12824
12825 #[test]
12828 fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
12829 let mut f = Fixture::new();
12830 assert_eq!(
12831 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
12832 "*2\r\n:1\r\n:1\r\n"
12833 );
12834 assert_eq!(
12835 f.run(&[b"CF.DEBUG", b"i"]),
12836 "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
12837 max_iterations:20 expansion:1\r\n"
12838 );
12839 assert_eq!(
12842 f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
12843 "*2\r\n:0\r\n:1\r\n"
12844 );
12845 assert_eq!(
12846 f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
12847 "-ERR not found\r\n"
12848 );
12849 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
12850
12851 assert_eq!(
12852 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
12853 "-Bad capacity\r\n"
12854 );
12855 assert_eq!(
12858 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
12859 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
12860 );
12861 assert_eq!(
12865 f.run(&[
12866 b"CF.INSERT",
12867 b"i",
12868 b"CAPACITY",
12869 b"8",
12870 b"CAPACITY",
12871 b"2",
12872 b"ITEMS",
12873 b"a"
12874 ]),
12875 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
12876 );
12877 assert_eq!(
12881 f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
12882 "*1\r\n:1\r\n"
12883 );
12884 assert_eq!(
12885 f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
12886 "*1\r\n:1\r\n"
12887 );
12888 assert_eq!(
12889 f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12890 "-Unknown argument received\r\n"
12891 );
12892 assert_eq!(
12894 f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12895 "*1\r\n:1\r\n"
12896 );
12897 assert!(
12899 f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
12900 .contains("wrong number of arguments")
12901 );
12902 assert!(
12903 f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
12904 .contains("wrong number of arguments")
12905 );
12906 }
12907
12908 #[test]
12911 fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
12912 let mut f = Fixture::new();
12913 f.run(&[
12914 b"CF.RESERVE",
12915 b"s",
12916 b"4",
12917 b"BUCKETSIZE",
12918 b"1",
12919 b"EXPANSION",
12920 b"0",
12921 ]);
12922 for i in 0..4u32 {
12923 assert_eq!(
12924 f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
12925 ":1\r\n"
12926 );
12927 }
12928 assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
12929 assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
12930 assert_eq!(
12933 f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
12934 "*2\r\n:-1\r\n:-1\r\n"
12935 );
12936 assert_eq!(
12937 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
12938 "*2\r\n:0\r\n:-1\r\n"
12939 );
12940
12941 let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
12947 assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
12948 assert_eq!(
12949 f.run(&[b"CF.ADD", b"g", b"q"]),
12950 "-Maximum expansions reached\r\n"
12951 );
12952 assert_eq!(
12953 f.run(&[b"CF.INFO", b"g"]),
12954 "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
12955 +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
12956 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
12957 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
12958 );
12959 }
12960
12961 #[test]
12965 fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
12966 let mut f = Fixture::new();
12967 f.run(&[
12968 b"CF.RESERVE",
12969 b"src",
12970 b"8",
12971 b"BUCKETSIZE",
12972 b"2",
12973 b"EXPANSION",
12974 b"2",
12975 ]);
12976 for i in 0..40u32 {
12977 f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
12978 }
12979 let mut pos = b"0".to_vec();
12983 let mut chunks = 0;
12984 loop {
12985 let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
12986 let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
12987 let next = head
12988 .split("\r\n")
12989 .nth(1)
12990 .and_then(|n| n.strip_prefix(':'))
12991 .expect("a two element reply of a position and a chunk")
12992 .to_owned();
12993 if next == "0" {
12994 assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
12995 break;
12996 }
12997 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
12998 let at = body
12999 .windows(2)
13000 .position(|w| w == b"\r\n")
13001 .expect("a length line")
13002 + 2;
13003 let data = &body[at..body.len() - 2];
13004 assert_eq!(
13005 f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13006 "+OK\r\n",
13007 "loading chunk {chunks}"
13008 );
13009 pos = next.into_bytes();
13010 chunks += 1;
13011 }
13012 assert!(chunks >= 2, "a header and at least one chunk");
13013
13014 assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13015 assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13016 for i in 0..40u32 {
13017 assert_eq!(
13018 f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13019 ":1\r\n"
13020 );
13021 }
13022
13023 f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13026 assert_eq!(
13027 f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13028 "*2\r\n:0\r\n$-1\r\n"
13029 );
13030
13031 assert_eq!(
13035 f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13036 "-Invalid position\r\n"
13037 );
13038 assert_eq!(
13039 f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13040 "-Invalid position\r\n"
13041 );
13042 assert_eq!(
13043 f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13044 "-Invalid position\r\n"
13045 );
13046 assert_eq!(
13047 f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13048 "-Couldn't load chunk!\r\n"
13049 );
13050 let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13052 assert_eq!(
13053 f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13054 "-ERR item exists\r\n"
13055 );
13056 assert_eq!(
13060 f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13061 "-Invalid header\r\n"
13062 );
13063 for (why, bad) in [
13064 ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13065 ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13066 (
13067 "a bucket count that is not a power of two",
13068 cf_header(0, 3, 0, 1, [2, 20, 1]),
13069 ),
13070 ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13071 ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13072 (
13073 "a growth nobody could reach",
13074 cf_header(0, 8, 0, 1, [2, 20, 32769]),
13075 ),
13076 (
13077 "a chain that cannot grow and did",
13078 cf_header(0, 8, 0, 2, [2, 20, 0]),
13079 ),
13080 (
13083 "a filter count that wraps",
13084 cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13085 ),
13086 ] {
13087 assert_eq!(
13088 f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13089 "-Couldn't create filter!\r\n",
13090 "{why}"
13091 );
13092 }
13093 }
13094
13095 #[test]
13098 fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13099 let mut f = Fixture::new();
13100 f.out.set_proto(Proto::Resp3);
13101 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13102 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13103 assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13104 assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13105 assert_eq!(
13106 f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13107 "*2\r\n#t\r\n#f\r\n"
13108 );
13109 assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13110 assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13111 assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13113 assert_eq!(
13114 f.run(&[b"CF.INFO", b"c"]),
13115 "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13116 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13117 +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13118 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13119 );
13120
13121 f.run(&[
13126 b"CF.RESERVE",
13127 b"s",
13128 b"4",
13129 b"BUCKETSIZE",
13130 b"1",
13131 b"EXPANSION",
13132 b"0",
13133 ]);
13134 assert_eq!(
13135 f.run(&[
13136 b"CF.INSERT",
13137 b"s",
13138 b"ITEMS",
13139 b"a",
13140 b"b",
13141 b"c",
13142 b"d",
13143 b"e",
13144 b"f"
13145 ]),
13146 "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13147 );
13148 assert_eq!(
13149 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13150 "*2\r\n:0\r\n:-1\r\n"
13151 );
13152 assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13153 assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13156 }
13157
13158 #[test]
13163 fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13164 let mut f = Fixture::new();
13165 assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13166 assert_eq!(
13167 f.run(&[b"CMS.INFO", b"d"]),
13168 "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13169 );
13170 assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13171 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13172 assert_eq!(
13175 f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13176 "+OK\r\n"
13177 );
13178 assert_eq!(
13179 f.run(&[b"CMS.INFO", b"p"]),
13180 "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13181 );
13182 assert_eq!(
13185 f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13186 "-CMS: key already exists\r\n"
13187 );
13188 assert_eq!(
13189 f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13190 "-CMS: invalid width\r\n"
13191 );
13192 assert_eq!(
13193 f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13194 "-CMS: invalid depth\r\n"
13195 );
13196 assert_eq!(
13197 f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13198 "-CMS: invalid overestimation value\r\n"
13199 );
13200 assert_eq!(
13201 f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13202 "-CMS: invalid prob value\r\n"
13203 );
13204 assert_eq!(
13208 f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13209 "-CMS: invalid init arguments\r\n"
13210 );
13211 assert_eq!(
13215 f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13216 "-CMS: Insufficient memory to create the key\r\n"
13217 );
13218 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13219 }
13220
13221 #[test]
13224 fn increments_are_parsed_whole_and_the_counters_saturate() {
13225 let mut f = Fixture::new();
13226 f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13227 assert_eq!(
13228 f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13229 "*2\r\n:3\r\n:4\r\n"
13230 );
13231 assert_eq!(
13234 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13235 "*2\r\n:4\r\n:5\r\n"
13236 );
13237 assert_eq!(
13239 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13240 "-CMS: Cannot parse number\r\n"
13241 );
13242 assert_eq!(
13243 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
13244 "-CMS: Number cannot be negative\r\n"
13245 );
13246 assert_eq!(
13247 f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
13248 "*2\r\n:5\r\n:4\r\n"
13249 );
13250 f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
13253 assert_eq!(
13254 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
13255 "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
13256 );
13257 assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
13258 f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
13261 f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
13262 f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
13263 assert_eq!(
13264 f.run(&[b"CMS.INFO", b"w"]),
13265 "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
13266 );
13267 assert!(
13270 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
13271 .contains("wrong number of arguments")
13272 );
13273 assert_eq!(
13274 f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
13275 "-CMS: key does not exist\r\n"
13276 );
13277 assert_eq!(
13278 f.run(&[b"CMS.QUERY", b"nope", b"a"]),
13279 "-CMS: key does not exist\r\n"
13280 );
13281 }
13282
13283 #[test]
13286 fn a_merge_lands_whole_or_not_at_all() {
13287 let mut f = Fixture::new();
13288 for name in [&b"m1"[..], b"m2", b"dst"] {
13289 f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
13290 }
13291 f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
13292 f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
13293 assert_eq!(
13294 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13295 "+OK\r\n"
13296 );
13297 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13298 assert_eq!(
13301 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13302 "+OK\r\n"
13303 );
13304 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13305 assert_eq!(
13306 f.run(&[
13307 b"CMS.MERGE",
13308 b"dst",
13309 b"2",
13310 b"m1",
13311 b"m2",
13312 b"WEIGHTS",
13313 b"2",
13314 b"3"
13315 ]),
13316 "+OK\r\n"
13317 );
13318 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13319 assert_eq!(
13322 f.run(&[
13323 b"CMS.MERGE",
13324 b"dst",
13325 b"1",
13326 b"m1",
13327 b"WEIGHTS",
13328 b"4611686018427387904"
13329 ]),
13330 "-CMS: MERGE overflow\r\n"
13331 );
13332 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13333 f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
13336 assert_eq!(
13337 f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
13338 "-CMS: key does not exist\r\n"
13339 );
13340 assert_eq!(
13341 f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
13342 "-CMS: Number of keys must be positive\r\n"
13343 );
13344 assert_eq!(
13345 f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
13346 "-CMS: wrong number of keys\r\n"
13347 );
13348 assert_eq!(
13349 f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
13350 "-CMS: wrong number of keys/weights\r\n"
13351 );
13352 assert_eq!(
13353 f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
13354 "-CMS: width/depth is not equal\r\n"
13355 );
13356 assert_eq!(
13357 f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
13358 "-CMS: key does not exist\r\n"
13359 );
13360 }
13361
13362 #[test]
13365 fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13366 let mut f = Fixture::new();
13367 f.run(&[b"SET", b"s", b"text"]);
13368 for cmd in [
13369 vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
13370 vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
13371 vec![&b"CMS.QUERY"[..], b"s", b"a"],
13372 vec![&b"CMS.INFO"[..], b"s"],
13373 vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
13374 ] {
13375 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13376 let reply = f.run(&cmd);
13377 assert!(
13380 reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
13381 "{name}: {reply}"
13382 );
13383 }
13384 f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
13385 assert_eq!(
13389 f.run(&[b"COPY", b"c", b"c2"]),
13390 "-ERR not supported for this module key\r\n"
13391 );
13392 assert_eq!(
13393 f.run(&[b"DUMP", b"c"]),
13394 "-ERR DUMP is not supported for this module key\r\n"
13395 );
13396 f.run(&[b"G.NADD", b"g", b"a"]);
13398 assert_eq!(
13399 f.run(&[b"COPY", b"g", b"g2"]),
13400 "-ERR COPY is not supported for a graph\r\n"
13401 );
13402 assert_eq!(
13403 f.run(&[b"DUMP", b"g"]),
13404 "-ERR DUMP is not supported for a graph\r\n"
13405 );
13406 assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
13409 assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
13410 assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
13411 assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
13412 assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
13413 }
13414
13415 #[test]
13420 fn a_reserve_takes_three_arguments_or_six() {
13421 let mut f = Fixture::new();
13422 assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
13423 assert_eq!(
13424 f.run(&[b"TOPK.INFO", b"t"]),
13425 "*8\r\n+k\r\n:5\r\n+width\r\n:8\r\n+depth\r\n:7\r\n+decay\r\n$3\r\n0.9\r\n"
13426 );
13427 for cmd in [
13430 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
13431 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
13432 ] {
13433 assert!(f.run(&cmd).contains("wrong number of arguments"));
13434 }
13435 assert_eq!(
13436 f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
13437 "+OK\r\n"
13438 );
13439 assert_eq!(
13442 f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
13443 "-TopK: key already exists\r\n"
13444 );
13445 assert_eq!(
13446 f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
13447 "-TopK: invalid k\r\n"
13448 );
13449 assert_eq!(
13450 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
13451 "-TopK: invalid width\r\n"
13452 );
13453 assert_eq!(
13454 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
13455 "-TopK: invalid depth\r\n"
13456 );
13457 assert_eq!(
13459 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
13460 "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
13461 );
13462 assert_eq!(
13463 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
13464 "+OK\r\n"
13465 );
13466 assert_eq!(
13468 f.run(&[
13469 b"TOPK.RESERVE",
13470 b"w",
13471 b"1",
13472 b"4294967295",
13473 b"4294967295",
13474 b"0.9"
13475 ]),
13476 "-ERR Insufficient memory to create topk data structure\r\n"
13477 );
13478 }
13479
13480 #[test]
13482 fn the_kept_set_is_what_query_and_list_answer_from() {
13483 let mut f = Fixture::new();
13484 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
13485 assert_eq!(
13488 f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
13489 "*2\r\n$-1\r\n$-1\r\n"
13490 );
13491 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
13492 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
13495 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
13496 assert_eq!(
13497 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
13498 "*3\r\n:1\r\n:0\r\n:1\r\n"
13499 );
13500 assert_eq!(
13502 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13503 "*3\r\n:11\r\n:1\r\n:6\r\n"
13504 );
13505 assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
13506 assert_eq!(
13507 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
13508 "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
13509 );
13510 assert_eq!(
13513 f.run(&[b"TOPK.LIST", b"t", b"w"]),
13514 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13515 );
13516 assert_eq!(
13517 f.run(&[b"TOPK.LIST", b"t", b""]),
13518 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13519 );
13520 assert_eq!(
13521 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
13522 "-WITHCOUNT keyword expected\r\n"
13523 );
13524 assert_eq!(
13527 f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
13528 "-WITHCOUNT keyword expected\r\n"
13529 );
13530 assert_eq!(
13531 f.run(&[b"TOPK.LIST", b"missing"]),
13532 "-TopK: key does not exist\r\n"
13533 );
13534 f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
13536 assert_eq!(
13537 f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
13538 "*1\r\n$-1\r\n"
13539 );
13540 assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
13541 assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
13542 }
13543
13544 #[test]
13547 fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
13548 let mut f = Fixture::new();
13549 f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
13550 assert_eq!(
13554 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
13555 format!(
13556 "*2\r\n$-1\r\n-{}\r\n",
13557 "TopK: increment must be an integer greater or equal to 0 and smaller or equal to 100,000"
13558 )
13559 );
13560 assert_eq!(
13561 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13562 "*3\r\n:3\r\n:0\r\n:0\r\n"
13563 );
13564 assert_eq!(
13566 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
13567 "*1\r\n$-1\r\n"
13568 );
13569 assert!(
13570 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
13571 .contains("smaller or equal to 100,000")
13572 );
13573 assert!(
13575 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
13576 .contains("wrong number of arguments")
13577 );
13578 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
13579 }
13580
13581 #[test]
13583 fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
13584 let mut f = Fixture::new();
13585 f.run(&[b"HELLO", b"3"]);
13586 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
13587 f.run(&[b"TOPK.ADD", b"t", b"a"]);
13588 assert_eq!(
13589 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
13590 "*2\r\n#t\r\n#f\r\n"
13591 );
13592 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
13594 assert_eq!(
13595 f.run(&[b"TOPK.INFO", b"t"]),
13596 "%4\r\n+k\r\n:2\r\n+width\r\n:8\r\n+depth\r\n:7\r\n+decay\r\n,0.5\r\n"
13597 );
13598 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
13599 }
13600
13601 #[test]
13604 fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13605 let mut f = Fixture::new();
13606 f.run(&[b"SET", b"s", b"text"]);
13607 for cmd in [
13608 vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
13609 vec![&b"TOPK.ADD"[..], b"s", b"a"],
13610 vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
13611 vec![&b"TOPK.QUERY"[..], b"s", b"a"],
13612 vec![&b"TOPK.COUNT"[..], b"s", b"a"],
13613 vec![&b"TOPK.LIST"[..], b"s"],
13614 vec![&b"TOPK.INFO"[..], b"s"],
13615 ] {
13616 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13617 let reply = f.run(&cmd);
13618 assert!(
13619 reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
13620 "{name}: {reply}"
13621 );
13622 }
13623 f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
13624 assert_eq!(
13625 f.run(&[b"COPY", b"t", b"t2"]),
13626 "-ERR not supported for this module key\r\n"
13627 );
13628 assert_eq!(
13629 f.run(&[b"DUMP", b"t"]),
13630 "-ERR DUMP is not supported for this module key\r\n"
13631 );
13632 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
13633 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
13634 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
13635 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
13636 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
13637 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
13638 assert_eq!(
13641 f.run(&[b"TOPK.INFO", b"t3"]),
13642 "-TopK: key does not exist\r\n"
13643 );
13644 }
13645
13646 #[test]
13651 fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
13652 let mut f = Fixture::new();
13653 assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
13654 assert_eq!(
13656 f.run(&[b"TDIGEST.INFO", b"t"]),
13657 "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
13658 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
13659 +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
13660 );
13661 assert_eq!(
13662 f.run(&[b"TDIGEST.CREATE", b"t"]),
13663 "-ERR T-Digest: key already exists\r\n"
13664 );
13665 assert!(
13667 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
13668 .contains("wrong number of arguments")
13669 );
13670 assert_eq!(
13671 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
13672 "+OK\r\n"
13673 );
13674 assert_eq!(
13675 f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
13676 "+OK\r\n"
13677 );
13678 assert_eq!(
13682 f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
13683 "-ERR T-Digest: error parsing compression parameter\r\n"
13684 );
13685 assert_eq!(
13686 f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
13687 "-ERR T-Digest: wrong keyword\r\n"
13688 );
13689 assert_eq!(
13690 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
13691 "-ERR T-Digest: error parsing compression parameter\r\n"
13692 );
13693 assert_eq!(
13694 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
13695 "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
13696 );
13697 assert_eq!(
13700 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
13701 "-ERR T-Digest: allocation failed\r\n"
13702 );
13703 assert_eq!(
13705 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
13706 "-ERR T-Digest: allocation failed\r\n"
13707 );
13708 assert_eq!(
13711 f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
13712 "-ERR T-Digest: key already exists\r\n"
13713 );
13714 }
13715
13716 #[test]
13719 fn the_quantile_family_answers_what_the_module_answers() {
13720 let mut f = Fixture::new();
13721 f.run(&[b"TDIGEST.CREATE", b"s"]);
13722 assert_eq!(
13723 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
13724 "+OK\r\n"
13725 );
13726 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
13727 assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
13728 assert_eq!(
13730 f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
13731 "*4\r\n$5\r\n0.125\r\n$5\r\n0.375\r\n$5\r\n0.625\r\n$5\r\n0.875\r\n"
13732 );
13733 assert_eq!(
13734 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
13735 "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
13736 );
13737 assert_eq!(
13740 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
13741 "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
13742 );
13743 assert_eq!(
13744 f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
13745 "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
13746 );
13747 assert_eq!(
13748 f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
13749 "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
13750 );
13751 assert_eq!(
13752 f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
13753 "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
13754 );
13755 assert_eq!(
13756 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
13757 "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
13758 );
13759 assert_eq!(
13760 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
13761 "$3\r\n2.5\r\n"
13762 );
13763 assert_eq!(
13764 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
13765 "$3\r\n2.5\r\n"
13766 );
13767 assert_eq!(
13769 f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
13770 "-ERR T-Digest: quantile should be in [0,1]\r\n"
13771 );
13772 assert_eq!(
13773 f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
13774 "-ERR T-Digest: error parsing quantile\r\n"
13775 );
13776 assert_eq!(
13777 f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
13778 "-ERR T-Digest: error parsing cdf\r\n"
13779 );
13780 assert_eq!(
13781 f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
13782 "-ERR T-Digest: error parsing value\r\n"
13783 );
13784 assert_eq!(
13785 f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
13786 "-ERR T-Digest: rank needs to be non negative\r\n"
13787 );
13788 assert_eq!(
13789 f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
13790 "-ERR T-Digest: error parsing rank\r\n"
13791 );
13792 assert_eq!(
13795 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
13796 "-ERR T-Digest: error parsing low_cut_percentile\r\n"
13797 );
13798 assert_eq!(
13799 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
13800 "-ERR T-Digest: error parsing high_cut_percentile\r\n"
13801 );
13802 assert_eq!(
13803 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
13804 "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
13805 );
13806 assert_eq!(
13807 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
13808 "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
13809 );
13810 }
13811
13812 #[test]
13815 fn an_empty_digest_has_an_answer_for_everything() {
13816 let mut f = Fixture::new();
13817 f.run(&[b"TDIGEST.CREATE", b"e"]);
13818 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
13819 assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
13820 assert_eq!(
13821 f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
13822 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
13823 );
13824 assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
13825 assert_eq!(
13826 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
13827 "$3\r\nnan\r\n"
13828 );
13829 assert_eq!(
13832 f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
13833 "*2\r\n:-2\r\n:-2\r\n"
13834 );
13835 assert_eq!(
13836 f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
13837 "*2\r\n:-2\r\n:-2\r\n"
13838 );
13839 assert_eq!(
13840 f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
13841 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
13842 );
13843 f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
13845 assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
13846 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
13847 f.run(&[b"TDIGEST.CREATE", b"e2"]);
13850 assert_eq!(
13851 f.run(&[b"TDIGEST.INFO", b"e"]),
13852 f.run(&[b"TDIGEST.INFO", b"e2"])
13853 );
13854 }
13855
13856 #[test]
13859 fn a_sample_is_read_the_way_redis_reads_a_double() {
13860 let mut f = Fixture::new();
13861 f.run(&[b"TDIGEST.CREATE", b"a"]);
13862 for bad in [
13865 &b"nan"[..],
13866 b"1e400",
13867 b"-1e400",
13868 b"1e309",
13869 b"1e-400",
13870 b"",
13871 b" 1",
13872 b"1 ",
13873 b"1e",
13874 b"--1",
13875 ] {
13876 assert_eq!(
13877 f.run(&[b"TDIGEST.ADD", b"a", bad]),
13878 "-ERR T-Digest: error parsing val parameter\r\n",
13879 "{}",
13880 String::from_utf8_lossy(bad)
13881 );
13882 }
13883 for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
13886 assert_eq!(
13887 f.run(&[b"TDIGEST.ADD", b"a", word]),
13888 "-ERR T-Digest: val parameter needs to be a finite number\r\n",
13889 "{}",
13890 String::from_utf8_lossy(word)
13891 );
13892 }
13893 for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
13896 assert_eq!(
13897 f.run(&[b"TDIGEST.ADD", b"a", good]),
13898 "+OK\r\n",
13899 "{}",
13900 String::from_utf8_lossy(good)
13901 );
13902 }
13903 assert!(
13905 f.run(&[b"TDIGEST.INFO", b"a"])
13906 .contains("Observations\r\n:6\r\n")
13907 );
13908 assert_eq!(
13911 f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
13912 "-ERR T-Digest: error parsing val parameter\r\n"
13913 );
13914 assert!(
13915 f.run(&[b"TDIGEST.INFO", b"a"])
13916 .contains("Observations\r\n:6\r\n")
13917 );
13918 }
13919
13920 #[test]
13923 fn a_merge_sweeps_the_destination_between_its_inputs() {
13924 let mut f = Fixture::new();
13925 f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
13926 f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
13927 f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
13928 f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
13929 assert_eq!(
13930 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
13931 "+OK\r\n"
13932 );
13933 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
13938 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
13939 assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
13940 assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
13941 assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
13942 assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
13943 assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
13944 assert!(
13947 f.run(&[b"TDIGEST.INFO", b"m1"])
13948 .contains("Merged nodes\r\n:3\r\n")
13949 );
13950 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
13953 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
13954 assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
13955 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
13956 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
13959 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
13960 assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
13961 assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
13962 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
13964 assert!(
13965 f.run(&[b"TDIGEST.INFO", b"d"])
13966 .contains("Compression\r\n:500\r\n")
13967 );
13968 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
13970 assert!(
13971 f.run(&[b"TDIGEST.INFO", b"d"])
13972 .contains("Observations\r\n:12\r\n")
13973 );
13974 assert_eq!(
13976 f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
13977 "-ERR T-Digest: error parsing numkeys\r\n"
13978 );
13979 assert_eq!(
13980 f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
13981 "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
13982 );
13983 assert!(
13984 f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
13985 .contains("wrong number of arguments")
13986 );
13987 assert!(
13988 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
13989 .contains("wrong number of arguments")
13990 );
13991 assert_eq!(
13992 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
13993 "-ERR T-Digest: wrong keyword\r\n"
13994 );
13995 assert_eq!(
13998 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
13999 "-ERR T-Digest: key does not exist\r\n"
14000 );
14001 assert!(
14002 f.run(&[b"TDIGEST.INFO", b"d"])
14003 .contains("Observations\r\n:12\r\n")
14004 );
14005 assert_eq!(
14008 f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14009 "-ERR T-Digest: key does not exist\r\n"
14010 );
14011 }
14012
14013 #[test]
14015 fn a_digest_answers_doubles_and_a_map_on_resp3() {
14016 let mut f = Fixture::new();
14017 f.run(&[b"HELLO", b"3"]);
14018 f.run(&[b"TDIGEST.CREATE", b"s"]);
14019 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14020 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14021 assert_eq!(
14022 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14023 "*2\r\n,1\r\n,4\r\n"
14024 );
14025 assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14026 assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14028 assert_eq!(
14029 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14030 "*1\r\n,-inf\r\n"
14031 );
14032 f.run(&[b"TDIGEST.CREATE", b"e"]);
14033 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14034 assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14036 assert_eq!(
14039 f.run(&[b"TDIGEST.INFO", b"s"]),
14040 "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14041 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14042 +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14043 );
14044 }
14045
14046 #[test]
14049 fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14050 let mut f = Fixture::new();
14051 f.run(&[b"SET", b"s", b"text"]);
14052 for cmd in [
14053 vec![&b"TDIGEST.CREATE"[..], b"s"],
14054 vec![&b"TDIGEST.RESET"[..], b"s"],
14055 vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14056 vec![&b"TDIGEST.MIN"[..], b"s"],
14057 vec![&b"TDIGEST.MAX"[..], b"s"],
14058 vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14059 vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14060 vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14061 vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14062 vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14063 vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14064 vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14065 vec![&b"TDIGEST.INFO"[..], b"s"],
14066 ] {
14067 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14068 let reply = f.run(&cmd);
14069 assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14070 }
14071 f.run(&[b"TDIGEST.CREATE", b"t"]);
14073 assert!(
14074 f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14075 .starts_with("-WRONGTYPE")
14076 );
14077 assert!(
14078 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14079 .starts_with("-WRONGTYPE")
14080 );
14081 assert_eq!(
14082 f.run(&[b"COPY", b"t", b"t2"]),
14083 "-ERR not supported for this module key\r\n"
14084 );
14085 assert_eq!(
14086 f.run(&[b"DUMP", b"t"]),
14087 "-ERR DUMP is not supported for this module key\r\n"
14088 );
14089 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14090 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14091 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14092 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14093 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14094 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14095 assert_eq!(
14098 f.run(&[b"TDIGEST.INFO", b"t3"]),
14099 "-ERR T-Digest: key does not exist\r\n"
14100 );
14101 assert_eq!(
14104 f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14105 "-ERR T-Digest: key does not exist\r\n"
14106 );
14107 }
14108
14109 #[test]
14112 fn xadd_ids_only_ever_go_up() {
14113 let mut f = Fixture::new();
14114 assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
14116 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
14118 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
14119 assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
14120 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
14121
14122 assert!(
14123 f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
14124 .contains("equal or smaller")
14125 );
14126 assert!(
14127 f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
14128 .contains("must be greater than 0-0")
14129 );
14130 assert!(
14131 f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
14132 .contains("Invalid stream ID")
14133 );
14134 assert!(
14137 f.run(&[b"XADD", b"s", b"*", b"a"])
14138 .contains("wrong number of arguments")
14139 );
14140
14141 assert_eq!(
14144 f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
14145 "$-1\r\n"
14146 );
14147 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
14148 assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
14149 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
14150 }
14151
14152 #[test]
14155 fn trimming_reads_its_options_the_way_redis_does() {
14156 let mut f = Fixture::new();
14157 for i in 1..=10u32 {
14158 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
14159 }
14160 assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
14161 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
14162 assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
14163 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14164
14165 assert!(
14169 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
14170 .contains("not an integer")
14171 );
14172 assert!(
14173 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
14174 .contains("MAXLEN argument must be >= 0")
14175 );
14176 assert!(
14179 f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
14180 .contains("without specifying a trimming strategy")
14181 );
14182 assert!(
14183 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
14184 .contains("without the special ~ option")
14185 );
14186 assert!(
14187 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
14188 .contains("at the same time are not compatible")
14189 );
14190 assert!(
14192 f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
14193 .contains("syntax error")
14194 );
14195 assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
14196 }
14197
14198 #[test]
14200 fn xrange_looks_the_key_up_before_it_reads_the_count() {
14201 let mut f = Fixture::new();
14202 f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
14203 f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
14204
14205 assert_eq!(
14206 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
14207 "*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\
14208 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
14209 );
14210 assert_eq!(
14211 f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
14212 "*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"
14213 );
14214 assert_eq!(
14218 f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
14219 "*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\
14220 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
14221 );
14222 assert_eq!(
14223 f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
14224 "*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"
14225 );
14226 assert!(
14227 f.run(&[b"XRANGE", b"s", b"(-", b"+"])
14228 .contains("Invalid stream ID")
14229 );
14230
14231 assert_eq!(
14235 f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
14236 "*0\r\n"
14237 );
14238 assert_eq!(
14239 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
14240 "*-1\r\n"
14241 );
14242 f.run(&[b"SET", b"str", b"v"]);
14243 assert!(
14244 f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
14245 .starts_with("-WRONGTYPE")
14246 );
14247 assert_eq!(
14249 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
14250 "*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"
14251 );
14252 }
14253
14254 #[test]
14256 fn a_bad_id_late_in_the_list_stops_the_whole_command() {
14257 let mut f = Fixture::new();
14258 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14259 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14260 assert!(
14261 f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
14262 .contains("Invalid stream ID")
14263 );
14264 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14265 assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
14266 assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
14267 assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
14268 assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
14269 }
14270
14271 #[test]
14273 fn xgroup_has_an_arity_per_subcommand() {
14274 let mut f = Fixture::new();
14275 assert!(
14276 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
14277 .contains("requires the key")
14278 );
14279 assert_eq!(
14280 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
14281 "+OK\r\n"
14282 );
14283 assert!(
14286 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
14287 .starts_with("-BUSYGROUP")
14288 );
14289 assert_eq!(
14290 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
14291 ":1\r\n"
14292 );
14293 assert_eq!(
14294 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
14295 ":0\r\n"
14296 );
14297 assert_eq!(
14298 f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
14299 ":0\r\n"
14300 );
14301
14302 let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
14304 assert!(
14305 short.contains("wrong number of arguments for 'xgroup|destroy' command"),
14306 "{short}"
14307 );
14308 let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
14310 assert!(
14311 odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
14312 "{odd}"
14313 );
14314 assert!(
14315 f.run(&[b"XGROUP", b"NOSUCH", b"s"])
14316 .contains("Try XGROUP HELP")
14317 );
14318
14319 assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
14320 assert!(
14321 f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
14322 .starts_with("-NOGROUP")
14323 );
14324 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
14325 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
14326 assert!(
14327 f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
14328 .contains("requires the key")
14329 );
14330 }
14331
14332 #[test]
14334 fn xreadgroup_hands_out_and_xack_takes_back() {
14335 let mut f = Fixture::new();
14336 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14337 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14338 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14339
14340 let first = f.run(&[
14341 b"XREADGROUP",
14342 b"GROUP",
14343 b"g",
14344 b"c1",
14345 b"COUNT",
14346 b"1",
14347 b"STREAMS",
14348 b"s",
14349 b">",
14350 ]);
14351 assert_eq!(
14352 first,
14353 "*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"
14354 );
14355 assert_eq!(
14358 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
14359 "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
14360 );
14361 assert_eq!(
14362 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
14363 "*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"
14364 );
14365
14366 assert_eq!(
14367 f.run(&[b"XPENDING", b"s", b"g"]),
14368 "*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"
14369 );
14370 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
14371 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
14372 assert_eq!(
14374 f.run(&[b"XPENDING", b"s", b"g"]),
14375 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
14376 );
14377
14378 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14381 f.run(&[b"XDEL", b"s", b"2-1"]);
14382 assert_eq!(
14383 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
14384 "*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"
14385 );
14386
14387 assert!(
14390 f.run(&[
14391 b"XREADGROUP",
14392 b"GROUP",
14393 b"nope",
14394 b"c",
14395 b"STREAMS",
14396 b"s",
14397 b"+"
14398 ])
14399 .starts_with("-NOGROUP")
14400 );
14401 assert!(
14402 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
14403 .contains("meaningless in the context of XREADGROUP")
14404 );
14405 assert!(
14406 f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
14407 .contains("only supported by XREADGROUP")
14408 );
14409 assert!(
14410 f.run(&[
14411 b"XREADGROUP",
14412 b"GROUP",
14413 b"g",
14414 b"c",
14415 b"STREAMS",
14416 b"s",
14417 b"a",
14418 b"b"
14419 ])
14420 .contains("Unbalanced 'xreadgroup' list of streams")
14421 );
14422 }
14423
14424 #[test]
14427 fn xread_with_no_block_writes_the_null_itself() {
14428 let mut f = Fixture::new();
14429 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14430 assert_eq!(
14431 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
14432 "*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"
14433 );
14434 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
14437 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
14438 f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
14439 assert_eq!(
14440 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
14441 "*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"
14442 );
14443 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
14445 assert_eq!(
14447 f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
14448 "*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"
14449 );
14450 assert_eq!(
14453 f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
14454 "*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"
14455 );
14456 assert!(
14458 f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
14459 .contains("not an integer")
14460 );
14461 assert!(
14462 f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
14463 .contains("timeout is negative")
14464 );
14465 assert!(
14466 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
14467 .contains("Unbalanced 'xread' list of streams")
14468 );
14469 }
14470
14471 #[test]
14473 fn a_blocked_xread_wakes_on_the_next_entry() {
14474 let mut f = Fixture::new();
14475 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14476 let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
14477 assert_eq!(flow, Flow::Block);
14478 assert!(reply.is_empty());
14479
14480 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
14483 assert_eq!(flow, Flow::Block);
14484 assert_eq!(f.server.waiters().len(), 2);
14485
14486 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14487 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";
14488 for at in 0..2 {
14489 let mut out = Out::new(Proto::Resp2);
14490 assert!(f.server.serve_waiter(at, 0, &mut out));
14491 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
14492 }
14493
14494 f.server.waiters_mut().forget(7);
14497 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
14498 assert_eq!(flow, Flow::Block);
14499 let mut out = Out::new(Proto::Resp2);
14500 assert!(!f.server.serve_waiter(0, 0, &mut out));
14501 assert!(out.as_slice().is_empty());
14502 assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
14503 assert_eq!(
14504 core::str::from_utf8(out.as_slice()).expect("ascii"),
14505 "*-1\r\n"
14506 );
14507 }
14508
14509 #[test]
14511 fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
14512 let mut f = Fixture::new();
14513 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14514 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
14515 let (flow, _) = f.flow(&[
14516 b"XREADGROUP",
14517 b"GROUP",
14518 b"g",
14519 b"c",
14520 b"BLOCK",
14521 b"0",
14522 b"STREAMS",
14523 b"s",
14524 b">",
14525 ]);
14526 assert_eq!(flow, Flow::Block);
14527
14528 f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
14529 let mut out = Out::new(Proto::Resp2);
14530 assert!(f.server.serve_waiter(0, 0, &mut out));
14531 assert_eq!(
14534 core::str::from_utf8(out.as_slice()).expect("ascii"),
14535 "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
14536 );
14537 }
14538
14539 #[test]
14541 fn xclaim_reads_ids_until_one_will_not_parse() {
14542 let mut f = Fixture::new();
14543 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14544 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14545 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14546 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14547
14548 assert!(
14551 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
14552 .contains("Unrecognized XCLAIM option '-'")
14553 );
14554 assert_eq!(
14555 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
14556 "*1\r\n$3\r\n1-1\r\n"
14557 );
14558 f.run(&[b"XDEL", b"s", b"2-1"]);
14561 assert_eq!(
14562 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
14563 "*0\r\n"
14564 );
14565 assert!(
14566 f.run(&[b"XPENDING", b"s", b"g"])
14567 .starts_with("*4\r\n:1\r\n")
14568 );
14569 assert!(
14570 f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
14571 .starts_with("-NOGROUP")
14572 );
14573 assert!(
14574 f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
14575 .contains("Invalid min-idle-time argument for XCLAIM")
14576 );
14577 }
14578
14579 #[test]
14581 fn xautoclaim_reports_what_it_dropped() {
14582 let mut f = Fixture::new();
14583 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14584 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14585 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14586 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14587 f.run(&[b"XDEL", b"s", b"1-1"]);
14588
14589 assert_eq!(
14592 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
14593 "*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"
14594 );
14595 assert!(
14596 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
14597 .contains("COUNT must be > 0")
14598 );
14599 assert!(
14600 f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
14601 .starts_with("-NOGROUP")
14602 );
14603 }
14604
14605 #[test]
14607 fn xdelex_answers_one_integer_an_id() {
14608 let mut f = Fixture::new();
14609 for i in 1..=4 {
14610 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
14611 }
14612 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14613 f.run(&[
14614 b"XREADGROUP",
14615 b"GROUP",
14616 b"g",
14617 b"c",
14618 b"COUNT",
14619 b"2",
14620 b"STREAMS",
14621 b"s",
14622 b">",
14623 ]);
14624
14625 assert_eq!(
14627 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
14628 "*2\r\n:1\r\n:-1\r\n"
14629 );
14630 assert!(
14633 f.run(&[b"XPENDING", b"s", b"g"])
14634 .starts_with("*4\r\n:2\r\n")
14635 );
14636 assert_eq!(
14638 f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
14639 "*1\r\n:1\r\n"
14640 );
14641 assert_eq!(
14643 f.run(&[b"XPENDING", b"s", b"g"]),
14644 "*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"
14645 );
14646
14647 assert_eq!(
14651 f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
14652 "*2\r\n:2\r\n:2\r\n"
14653 );
14654
14655 assert_eq!(
14657 f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
14658 "*2\r\n:-1\r\n:-1\r\n"
14659 );
14660 assert!(
14662 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
14663 .starts_with("-ERR Invalid stream ID")
14664 );
14665 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14666
14667 assert!(
14668 f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
14669 .contains("Number of IDs must be a positive integer")
14670 );
14671 assert!(
14672 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
14673 .contains("The `numids` parameter must match the number of arguments")
14674 );
14675 assert!(
14678 f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
14679 .starts_with("-ERR syntax error")
14680 );
14681 assert!(
14682 f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
14683 .starts_with("-ERR syntax error")
14684 );
14685 f.run(&[b"SET", b"str", b"v"]);
14687 assert!(
14688 f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
14689 .starts_with("-WRONGTYPE")
14690 );
14691 }
14692
14693 #[test]
14695 fn xackdel_reports_what_the_group_was_holding() {
14696 let mut f = Fixture::new();
14697 for i in 1..=3 {
14698 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
14699 }
14700 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14701 f.run(&[
14702 b"XREADGROUP",
14703 b"GROUP",
14704 b"g",
14705 b"c",
14706 b"COUNT",
14707 b"1",
14708 b"STREAMS",
14709 b"s",
14710 b">",
14711 ]);
14712
14713 assert_eq!(
14717 f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
14718 "*2\r\n:1\r\n:-1\r\n"
14719 );
14720 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14721
14722 assert_eq!(
14724 f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
14725 "*1\r\n:-1\r\n"
14726 );
14727 assert_eq!(
14728 f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
14729 "*1\r\n:-1\r\n"
14730 );
14731
14732 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
14735 f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
14736 assert_eq!(
14737 f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
14738 "*1\r\n:2\r\n"
14739 );
14740 assert_eq!(
14741 f.run(&[b"XPENDING", b"s", b"g"]),
14742 "*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"
14743 );
14744 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14745 }
14746
14747 #[test]
14749 fn xnack_releases_an_entry_for_the_next_claim() {
14750 let mut f = Fixture::new();
14751 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14752 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14753 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14754 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14755 f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
14758
14759 assert_eq!(
14760 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
14761 ":1\r\n"
14762 );
14763 assert_eq!(
14767 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
14768 "*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"
14769 );
14770 assert_eq!(
14772 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
14773 "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
14774 );
14775 assert_eq!(
14777 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
14778 "*-1\r\n"
14779 );
14780 assert_eq!(
14782 f.run(&[
14783 b"XAUTOCLAIM",
14784 b"s",
14785 b"g",
14786 b"c2",
14787 b"99999999",
14788 b"-",
14789 b"JUSTID"
14790 ]),
14791 "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
14792 );
14793
14794 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
14798 assert!(
14799 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14800 .contains(":-1\r\n:1\r\n")
14801 );
14802 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
14804 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
14805 assert!(
14806 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14807 .contains(":-1\r\n:0\r\n")
14808 );
14809 f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
14811 assert!(
14812 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14813 .contains(":9223372036854775807\r\n")
14814 );
14815 f.run(&[
14816 b"XNACK",
14817 b"s",
14818 b"g",
14819 b"FATAL",
14820 b"IDS",
14821 b"1",
14822 b"1-1",
14823 b"RETRYCOUNT",
14824 b"3",
14825 ]);
14826 assert!(
14827 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14828 .contains(":-1\r\n:3\r\n")
14829 );
14830
14831 f.run(&[b"XACK", b"s", b"g", b"2-1"]);
14835 assert_eq!(
14836 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
14837 ":0\r\n"
14838 );
14839 assert_eq!(
14840 f.run(&[
14841 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
14842 ]),
14843 ":1\r\n"
14844 );
14845 assert!(
14846 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14847 .contains(":-1\r\n:0\r\n")
14848 );
14849 assert_eq!(
14851 f.run(&[
14852 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
14853 ]),
14854 ":0\r\n"
14855 );
14856
14857 assert_eq!(
14860 f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
14861 "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
14862 );
14863 assert!(
14864 f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
14865 .starts_with("-ERR")
14866 );
14867 assert!(
14869 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
14870 .contains("numids must be a positive integer")
14871 );
14872 assert!(
14873 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
14874 .contains("number of IDs doesn't match numids")
14875 );
14876 assert!(
14879 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
14880 .contains("Unrecognized XNACK option '2-1'")
14881 );
14882 }
14883
14884 #[test]
14886 fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
14887 let mut f = Fixture::new();
14888 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14889 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14890 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14891 f.run(&[
14892 b"XREADGROUP",
14893 b"GROUP",
14894 b"g",
14895 b"c1",
14896 b"COUNT",
14897 b"1",
14898 b"STREAMS",
14899 b"s",
14900 b">",
14901 ]);
14902
14903 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
14904 assert!(info.starts_with("*20\r\n"), "{info}");
14907 assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
14908 assert!(
14909 info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
14910 "{info}"
14911 );
14912 assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
14913 assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
14914
14915 let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
14916 assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
14917 assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
14918 assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
14919 assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
14920
14921 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
14925 let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
14926 assert!(consumers.starts_with("*2\r\n"), "{consumers}");
14927 assert!(
14928 consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
14929 "{consumers}"
14930 );
14931 let c1 = consumers.find("c1").unwrap();
14933 let c2 = consumers.find("c2").unwrap();
14934 assert!(c1 < c2, "{consumers}");
14935
14936 let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
14937 assert!(full.starts_with("*18\r\n"), "{full}");
14938 assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
14939 assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
14940
14941 assert!(
14942 f.run(&[b"XINFO", b"STREAM", b"missing"])
14943 .contains("no such key")
14944 );
14945 assert!(
14946 f.run(&[b"XINFO", b"GROUPS", b"missing"])
14947 .contains("no such key")
14948 );
14949 assert!(
14950 f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
14951 .starts_with("-NOGROUP")
14952 );
14953 assert!(
14954 f.run(&[b"XINFO", b"NOSUCH", b"s"])
14955 .contains("Try XINFO HELP")
14956 );
14957 assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
14958 assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
14959 }
14960
14961 #[test]
14963 fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
14964 let mut f = Fixture::new();
14965 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14966 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14967 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14968
14969 let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
14970 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");
14971 assert_eq!(
14972 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
14973 "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
14974 );
14975 assert_eq!(
14977 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
14978 "*0\r\n"
14979 );
14980 assert_eq!(
14981 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
14982 list
14983 );
14984 assert!(
14986 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
14987 .contains("syntax error")
14988 );
14989 assert!(
14990 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
14991 .contains("syntax error")
14992 );
14993 assert_eq!(
14994 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
14995 "*0\r\n"
14996 );
14997 assert!(
14998 f.run(&[b"XPENDING", b"missing", b"g"])
14999 .starts_with("-NOGROUP")
15000 );
15001 }
15002
15003 #[test]
15005 fn xsetid_will_not_go_below_what_is_there() {
15006 let mut f = Fixture::new();
15007 f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
15008 assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
15009 assert_eq!(
15010 f.run(&[
15011 b"XSETID",
15012 b"s",
15013 b"10-1",
15014 b"ENTRIESADDED",
15015 b"7",
15016 b"MAXDELETEDID",
15017 b"9-1"
15018 ]),
15019 "+OK\r\n"
15020 );
15021 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
15022 assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
15023 assert!(
15024 info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
15025 "{info}"
15026 );
15027
15028 assert!(
15029 f.run(&[b"XSETID", b"s", b"1-1"])
15030 .contains("smaller than the target stream top item")
15031 );
15032 assert!(
15033 f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
15034 .contains("entries_added must be positive")
15035 );
15036 assert!(
15037 f.run(&[b"XSETID", b"missing", b"1-1"])
15038 .contains("no such key")
15039 );
15040 }
15041
15042 #[test]
15044 fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
15045 let mut f = Fixture::new();
15046 f.run(&[b"HELLO", b"3"]);
15047 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
15048 assert_eq!(
15051 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
15052 "%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"
15053 );
15054 assert_eq!(
15057 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
15058 "*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"
15059 );
15060 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
15061 }
15062
15063 struct Mem {
15069 blobs: Vec<Vec<u8>>,
15070 }
15071
15072 impl yo_kv::cold::Blocks for Mem {
15073 fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
15074 self.blobs.push(bytes.to_vec());
15075 Ok(yo_common::Addr::new(
15076 yo_common::Space::Log,
15077 (self.blobs.len() - 1) as u64,
15078 ))
15079 }
15080
15081 fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
15082 self.blobs
15083 .get(at.offset() as usize)
15084 .map(Vec::as_slice)
15085 .ok_or_else(|| {
15086 yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
15087 })
15088 }
15089
15090 fn bytes(&self) -> u64 {
15091 self.blobs.iter().map(|b| b.len() as u64).sum()
15092 }
15093 }
15094
15095 fn filled(attach: bool) -> (Fixture, usize) {
15099 let mut f = Fixture::new();
15100 if attach {
15101 f.server.db(0).attach(Box::new(Mem { blobs: Vec::new() }));
15102 }
15103 let val = vec![b'v'; 256];
15104 for i in 0..24000u32 {
15105 let k = format!("key:{i:08}");
15106 f.run(&[b"SET", k.as_bytes(), &val]);
15107 }
15108 let full = f.server.memory_bytes();
15109 assert!(full > 3 * 1024 * 1024, "the arena is several segments");
15110 (f, full)
15111 }
15112
15113 fn press(f: &mut Fixture, limit: usize) {
15119 let val = vec![b'v'; 256];
15120 for i in 0..3000u32 {
15121 let k = format!("new:{i:08}");
15122 assert_eq!(
15123 f.run(&[b"SET", k.as_bytes(), &val]),
15124 "+OK\r\n",
15125 "write {i} was refused"
15126 );
15127 f.server.refresh_memory();
15128 if f.server.memory_bytes() <= limit {
15129 return;
15130 }
15131 }
15132 panic!(
15133 "it never got under: {} against {limit}",
15134 f.server.memory_bytes()
15135 );
15136 }
15137
15138 #[test]
15139 fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
15140 let mut f = Fixture::new();
15141 assert_eq!(
15142 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
15143 "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
15144 "no limit is the default"
15145 );
15146 for (typed, bytes) in [
15149 (&b"0"[..], "0"),
15150 (b"1024", "1024"),
15151 (b"1k", "1000"),
15152 (b"1gb", "1073741824"),
15153 (b"-1", "-1"),
15154 ] {
15155 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
15156 assert_eq!(
15157 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
15158 format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
15159 "set {}",
15160 String::from_utf8_lossy(typed)
15161 );
15162 }
15163 for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
15164 assert_eq!(
15165 f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
15166 "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
15167 "refused {}",
15168 String::from_utf8_lossy(bad)
15169 );
15170 }
15171 let info = f.run(&[b"INFO", b"memory"]);
15173 assert!(info.contains("maxstore:-1"), "{info}");
15174 assert!(info.contains("yo_memory_regime:evict"), "{info}");
15175 assert!(info.contains("yo_store_bytes:0"), "{info}");
15176 }
15177
15178 #[test]
15179 fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
15180 let (mut f, full) = filled(true);
15184 let keys = f.run(&[b"DBSIZE"]);
15185 assert!(
15186 f.run(&[b"INFO", b"memory"])
15187 .contains("yo_memory_regime:migrate"),
15188 "a database with somewhere to put values migrates"
15189 );
15190
15191 let limit = full - 2 * 1024 * 1024;
15192 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
15193 f.run(&[
15194 b"CONFIG",
15195 b"SET",
15196 b"maxmemory",
15197 limit.to_string().as_bytes(),
15198 ]);
15199 press(&mut f, limit);
15200
15201 assert!(
15202 f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
15203 "nothing was thrown away"
15204 );
15205 let after: usize = f.run(&[b"DBSIZE"])[1..]
15206 .trim_end()
15207 .parse()
15208 .expect("a count");
15209 let before: usize = keys[1..].trim_end().parse().expect("a count");
15210 assert!(after > before, "the keys that came in are all still here");
15211 assert!(
15212 f.server.store_bytes() > 0,
15213 "and what came out of memory went to the file"
15214 );
15215 let val = format!("$256\r\n{}\r\n", "v".repeat(256));
15218 assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
15219 assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
15220 }
15221
15222 #[test]
15223 fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
15224 let (mut f, full) = filled(true);
15228 f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
15229 assert!(
15230 f.run(&[b"INFO", b"memory"])
15231 .contains("yo_memory_regime:evict"),
15232 "nothing may go to the file"
15233 );
15234
15235 let limit = full - 2 * 1024 * 1024;
15236 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
15237 f.run(&[
15238 b"CONFIG",
15239 b"SET",
15240 b"maxmemory",
15241 limit.to_string().as_bytes(),
15242 ]);
15243 press(&mut f, limit);
15244
15245 assert!(
15246 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
15247 "keys were thrown away, which is what was asked for"
15248 );
15249 assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
15250 }
15251
15252 #[test]
15253 fn a_full_file_goes_back_to_evicting() {
15254 let (mut f, full) = filled(true);
15258 f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
15259 let limit = full - 2 * 1024 * 1024;
15260 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
15261 f.run(&[
15262 b"CONFIG",
15263 b"SET",
15264 b"maxmemory",
15265 limit.to_string().as_bytes(),
15266 ]);
15267 press(&mut f, limit);
15268
15269 assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
15270 assert!(
15271 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
15272 "and then it started evicting"
15273 );
15274 assert!(
15275 f.run(&[b"INFO", b"memory"])
15276 .contains("yo_memory_regime:evict"),
15277 "and it says so"
15278 );
15279 }
15280}