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 search;
75mod server;
76mod sets;
77mod streams;
78mod strings;
79pub mod table;
80mod tdigest;
81mod topk;
82mod ts;
83mod vectors;
84mod vfilter;
85mod zsets;
86
87pub use args::Args;
88pub use blocking::{Parked, Waiters};
89pub use server::parse_memory;
90pub use table::{COMMANDS, Spec, arity_ok, lookup};
91
92use crate::reply::Out;
93use std::path::{Path, PathBuf};
94use yo_common::{Code, Error};
95use yo_kv::cold::Blocks;
96use yo_kv::{Clock, Db, Keyspace};
97use yo_search::Registry;
98
99pub const DATABASES: usize = 16;
106
107const ALL_DATABASES: u64 = if DATABASES == 64 {
114 u64::MAX
115} else {
116 (1u64 << DATABASES) - 1
117};
118const _: () = assert!(DATABASES <= 64);
119
120const EVICT_BUDGET: usize = 64;
132
133const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub enum Flow {
142 Continue,
144 Close,
146 Block,
153}
154
155#[derive(Debug, Clone, Copy, Default)]
161pub struct Stats {
162 pub clients: u64,
164 pub connections: u64,
166 pub commands: u64,
168}
169
170fn working_dir() -> PathBuf {
177 std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
178}
179
180#[derive(Debug, Clone, Copy, Default)]
189pub struct CommandStat {
190 pub calls: u64,
192 pub rejected: u64,
195 pub failed: u64,
197}
198
199impl CommandStat {
200 const fn seen(&self) -> bool {
206 self.calls != 0 || self.rejected != 0 || self.failed != 0
207 }
208}
209
210struct CommandStats(Box<[CommandStat]>);
217
218impl Default for CommandStats {
219 fn default() -> CommandStats {
220 CommandStats(vec![CommandStat::default(); table::count()].into_boxed_slice())
221 }
222}
223
224impl CommandStats {
225 fn at(&mut self, spec: &'static Spec) -> &mut CommandStat {
227 &mut self.0[table::index_of(spec)]
228 }
229}
230
231pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
237
238pub struct Server {
245 dbs: Vec<Db>,
246 width: usize,
252 clock: Clock,
253 started_ms: u64,
254 next_db: usize,
257 dirty: u64,
267 conn_bytes: usize,
269 maxmemory: u64,
274 store: Option<Box<StoreSource>>,
285 maxstore: Option<u64>,
301 used: usize,
313 evict_db: usize,
319 expire_db: usize,
326 expire_ms: u64,
329 waiters: Waiters,
331 peers: migrate::Peers,
336 pub stats: Stats,
338 cmdstats: CommandStats,
340 dir: PathBuf,
348 backup: backup::State,
353 search: Registry,
363 stopping: bool,
372}
373
374impl Server {
375 #[must_use]
377 pub fn new() -> Server {
378 let clock = Clock::system();
379 Server {
380 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
381 width: 1,
382 clock,
383 started_ms: clock.now_ms(),
384 next_db: 0,
385 dirty: ALL_DATABASES,
386 conn_bytes: 0,
387 maxmemory: 0,
388 store: None,
389 maxstore: None,
390 used: 0,
391 evict_db: 0,
392 expire_db: 0,
393 expire_ms: 0,
394 waiters: Waiters::default(),
395 peers: migrate::Peers::default(),
396 stats: Stats::default(),
397 cmdstats: CommandStats::default(),
398 dir: working_dir(),
399 backup: backup::State::default(),
400 search: Registry::new(),
401 stopping: false,
402 }
403 }
404
405 #[must_use]
416 pub fn with_width(width: usize) -> Server {
417 let clock = Clock::system();
418 let mut server = Server::new();
419 server.dbs = (0..DATABASES)
420 .map(|_| Db::with_clock(clock, width))
421 .collect();
422 server.width = server.dbs[0].width();
423 server
424 }
425
426 #[must_use]
428 pub fn with_clock(clock: Clock) -> Server {
429 Server {
430 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
431 width: 1,
432 clock,
433 started_ms: clock.now_ms(),
434 next_db: 0,
435 dirty: ALL_DATABASES,
436 conn_bytes: 0,
437 maxmemory: 0,
438 store: None,
439 maxstore: None,
440 used: 0,
441 evict_db: 0,
442 expire_db: 0,
443 expire_ms: 0,
444 waiters: Waiters::default(),
445 peers: migrate::Peers::default(),
446 stats: Stats::default(),
447 cmdstats: CommandStats::default(),
448 dir: working_dir(),
449 backup: backup::State::default(),
450 search: Registry::new(),
451 stopping: false,
452 }
453 }
454
455 pub fn striped(&mut self, i: usize) -> &mut Db {
473 self.dirty |= 1u64 << i;
474 &mut self.dbs[i]
475 }
476
477 fn keyspaces(&self) -> impl Iterator<Item = &Keyspace> {
484 self.dbs.iter().flat_map(Db::stripes)
485 }
486
487 fn keyspaces_mut(&mut self) -> impl Iterator<Item = &mut Keyspace> {
489 self.dbs.iter_mut().flat_map(Db::stripes_mut)
490 }
491
492 const fn slots(&self) -> usize {
498 DATABASES * self.width
499 }
500
501 const fn slot_db(&self, i: usize) -> usize {
503 i / self.width
504 }
505
506 fn slot_mut(&mut self, i: usize) -> &mut Keyspace {
508 let (db, stripe) = (i / self.width, i % self.width);
509 self.dbs[db].stripe_mut(stripe)
510 }
511
512 fn slot(&self, i: usize) -> &Keyspace {
514 let (db, stripe) = (i / self.width, i % self.width);
515 self.dbs[db].stripe(stripe)
516 }
517
518 #[must_use]
520 pub fn dir(&self) -> &Path {
521 &self.dir
522 }
523
524 pub fn set_dir(&mut self, dir: PathBuf) {
531 self.dir = dir;
532 }
533
534 pub fn backup_expire(&mut self) {
540 backup::expire(self);
541 }
542
543 pub fn stop(&mut self) {
549 self.stopping = true;
550 }
551
552 #[must_use]
558 pub fn stopping(&self) -> bool {
559 self.stopping
560 }
561
562 #[must_use]
569 pub fn striped_ref(&self, i: usize) -> &Db {
570 &self.dbs[i]
571 }
572
573 fn settings(&self) -> &Keyspace {
581 self.dbs[0].stripe(0)
582 }
583
584 pub fn refresh_clock(&mut self) {
590 self.clock.refresh();
591 let now = self.clock.now_ms();
592 for db in &mut self.dbs {
593 db.set_clock_ms(now);
594 }
595 }
596
597 pub fn advance_clock_ms(&mut self, ms: u64) {
603 let now = self.clock.now_ms() + ms;
604 self.set_clock_ms(now);
605 }
606
607 pub fn set_clock_ms(&mut self, ms: u64) {
615 self.clock.set(ms);
616 for db in &mut self.dbs {
617 db.set_clock_ms(ms);
618 }
619 }
620
621 #[must_use]
623 pub fn uptime_secs(&self) -> u64 {
624 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
625 }
626
627 #[must_use]
635 pub fn memory_bytes(&self) -> usize {
636 self.keyspaces().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
637 }
638
639 #[must_use]
645 pub fn dataset_bytes(&self) -> usize {
646 self.keyspaces()
647 .map(|db| db.map().arena().live_bytes() as usize)
648 .sum()
649 }
650
651 #[must_use]
653 pub fn arena_bytes(&self) -> usize {
654 self.keyspaces()
655 .map(|db| db.map().arena().reserved_bytes() as usize)
656 .sum()
657 }
658
659 #[must_use]
661 pub fn index_bytes(&self) -> usize {
662 self.keyspaces()
663 .map(|db| db.map().index().memory_bytes())
664 .sum()
665 }
666
667 #[must_use]
674 pub fn compaction(&self) -> yo_kv::Compaction {
675 self.keyspaces().map(|db| db.map().compaction()).fold(
676 yo_kv::Compaction::default(),
677 |a, b| yo_kv::Compaction {
678 walked: a.walked + b.walked,
679 moved: a.moved + b.moved,
680 bytes: a.bytes + b.bytes,
681 },
682 )
683 }
684
685 #[must_use]
687 pub fn segment_count(&self) -> usize {
688 self.keyspaces()
689 .map(|db| db.map().arena().resident_segments())
690 .sum()
691 }
692
693 #[must_use]
695 pub const fn conn_bytes(&self) -> usize {
696 self.conn_bytes
697 }
698
699 pub fn note_conn_bytes(&mut self, delta: isize) {
707 self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
708 }
709
710 #[must_use]
712 pub fn expired_keys(&self) -> u64 {
713 self.keyspaces().map(Keyspace::expired_keys).sum()
714 }
715
716 #[must_use]
718 pub fn evicted_keys(&self) -> u64 {
719 self.keyspaces().map(Keyspace::evicted_keys).sum()
720 }
721
722 pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
728 self.cmdstats
729 .0
730 .iter()
731 .enumerate()
732 .filter(|(_, row)| row.seen())
733 .map(|(at, row)| (table::name_at(at), *row))
734 }
735
736 #[must_use]
738 pub const fn maxmemory(&self) -> u64 {
739 self.maxmemory
740 }
741
742 pub fn set_maxmemory(&mut self, bytes: u64) {
756 self.maxmemory = bytes;
757 for db in &mut self.dbs {
758 db.track_memory(bytes != 0);
759 }
760 self.used = self.settled_memory();
761 }
762
763 pub fn set_store_source(
773 &mut self,
774 source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
775 ) {
776 self.store = Some(Box::new(source));
777 }
778
779 #[must_use]
781 pub const fn has_store_source(&self) -> bool {
782 self.store.is_some()
783 }
784
785 fn attach_store(&mut self, at: usize) {
792 if self.slot(at).store_bytes().is_some() {
793 return;
794 }
795 let Some(source) = self.store.as_mut() else {
796 return;
797 };
798 if let Some(blocks) = source(at) {
799 self.slot_mut(at).attach(blocks);
800 }
801 }
802
803 #[must_use]
805 pub const fn maxstore(&self) -> Option<u64> {
806 self.maxstore
807 }
808
809 pub const fn set_maxstore(&mut self, bytes: Option<u64>) {
815 self.maxstore = bytes;
816 }
817
818 #[must_use]
824 pub fn store_bytes(&self) -> u64 {
825 self.keyspaces().filter_map(Keyspace::store_bytes).sum()
826 }
827
828 #[must_use]
843 pub fn cold_stats(&self) -> yo_kv::tier::Stats {
844 let mut total = yo_kv::tier::Stats::default();
845 for db in self.keyspaces() {
846 let Some(tier) = db.tier() else { continue };
847 let s = tier.stats();
848 total.demoted += s.demoted;
849 total.promoted += s.promoted;
850 total.faults += s.faults;
851 total.served += s.served;
852 total.bytes_out += s.bytes_out;
853 total.bytes_in += s.bytes_in;
854 }
855 total
856 }
857
858 #[must_use]
865 pub fn regime(&self) -> &'static str {
866 if (0..self.slots()).any(|at| self.migrates(at)) {
867 "migrate"
868 } else {
869 "evict"
870 }
871 }
872
873 fn migrates(&self, at: usize) -> bool {
885 if self.maxstore == Some(0) {
886 return false;
887 }
888 match self.slot(at).store_bytes() {
889 Some(held) => self.maxstore.is_none_or(|cap| held < cap),
890 None => self.store.is_some(),
894 }
895 }
896
897 pub fn refresh_memory(&mut self) {
902 if self.maxmemory != 0 {
903 self.used = self.settled_memory();
904 }
905 }
906
907 fn settled_memory(&mut self) -> usize {
914 self.keyspaces_mut()
915 .map(Keyspace::settled_memory_bytes)
916 .sum::<usize>()
917 + self.conn_bytes
918 }
919
920 pub fn make_room(&mut self) -> bool {
954 if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
955 return true;
956 }
957 self.used = self.settled_memory();
962 let mut budget = EVICT_BUDGET;
963 while self.used as u64 > self.maxmemory {
964 let over = self.used - self.maxmemory as usize;
965 if !self.relieve_step(over) {
966 return false;
967 }
968 self.compact_hard_step();
969 self.used = self.settled_memory();
970 budget -= 1;
971 if budget == 0 {
972 break;
973 }
974 }
975 true
976 }
977
978 fn relieve_step(&mut self, over: usize) -> bool {
995 for turn in 0..self.slots() {
996 let i = (self.evict_db + turn) % self.slots();
997 let gave = if !self.slot(i).is_empty() && self.migrates(i) {
1000 self.attach_store(i);
1001 self.slot_mut(i)
1006 .relieve(over)
1007 .is_ok_and(yo_kv::tier::Relief::made_room)
1008 } else {
1009 self.slot_mut(i).evict_one()
1010 };
1011 if gave {
1012 self.evict_db = (i + 1) % self.slots();
1013 self.dirty |= 1u64 << self.slot_db(i);
1014 return true;
1015 }
1016 }
1017 false
1018 }
1019
1020 pub fn expire_slice(&mut self, budget: usize) -> usize {
1035 let now = self.clock.now_ms();
1036 if now == self.expire_ms {
1037 return 0;
1038 }
1039 self.expire_ms = now;
1040 self.expire_step(budget)
1041 }
1042
1043 pub fn expire_step(&mut self, budget: usize) -> usize {
1059 let mut spent = 0;
1060 for turn in 0..self.slots() {
1061 if spent >= budget {
1062 break;
1063 }
1064 let i = (self.expire_db + turn) % self.slots();
1065 let c = self.slot_mut(i).expire_cycle(budget - spent);
1066 spent += c.examined;
1067 if c.expired > 0 {
1068 self.expire_db = (i + 1) % self.slots();
1069 self.dirty |= 1u64 << self.slot_db(i);
1070 }
1071 }
1072 spent
1073 }
1074
1075 fn compact_hard_step(&mut self) -> Option<usize> {
1081 for turn in 0..self.slots() {
1082 let i = (self.next_db + turn) % self.slots();
1083 if let Some(moved) = self.slot_mut(i).compact_hard() {
1084 self.next_db = (i + 1) % self.slots();
1085 return Some(moved);
1086 }
1087 }
1088 None
1089 }
1090
1091 pub fn compact_step(&mut self) -> Option<usize> {
1104 for turn in 0..self.slots() {
1105 let i = (self.next_db + turn) % self.slots();
1106 let at = self.slot_db(i);
1110 if self.dirty & (1 << at) == 0 {
1111 continue;
1112 }
1113 if let Some(moved) = self.slot_mut(i).compact_step() {
1114 self.next_db = (i + 1) % self.slots();
1115 return Some(moved);
1116 }
1117 if i % self.width == self.width - 1 {
1121 self.dirty &= !(1u64 << at);
1122 }
1123 }
1124 None
1125 }
1126}
1127
1128impl Default for Server {
1129 fn default() -> Server {
1130 Server::new()
1131 }
1132}
1133
1134pub struct Session {
1136 db: usize,
1137 id: u64,
1138 name: Vec<u8>,
1139 sets: himport::Fieldsets,
1145}
1146
1147impl Session {
1148 #[must_use]
1150 pub fn new(id: u64) -> Session {
1151 Session {
1152 db: 0,
1153 id,
1154 name: Vec::new(),
1155 sets: himport::Fieldsets::default(),
1156 }
1157 }
1158
1159 #[must_use]
1161 pub const fn id(&self) -> u64 {
1162 self.id
1163 }
1164
1165 #[must_use]
1167 pub const fn db(&self) -> usize {
1168 self.db
1169 }
1170
1171 #[must_use]
1173 pub fn name(&self) -> &[u8] {
1174 &self.name
1175 }
1176
1177 pub fn reset(&mut self) {
1182 self.db = 0;
1183 self.name.clear();
1184 self.sets.clear();
1188 }
1189
1190 fn set_name(&mut self, name: &[u8]) {
1192 yo_alloc::allow(|| {
1193 self.name.clear();
1194 self.name.extend_from_slice(name);
1195 });
1196 }
1197}
1198
1199pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1204 if args.is_empty() {
1207 return Flow::Continue;
1208 }
1209 resolved(server, session, lookup(args.name()), args, out)
1210}
1211
1212pub fn resolved(
1224 server: &mut Server,
1225 session: &mut Session,
1226 spec: Option<&'static Spec>,
1227 args: Args<'_>,
1228 out: &mut Out,
1229) -> Flow {
1230 if args.is_empty() {
1231 return Flow::Continue;
1232 }
1233 server.stats.commands += 1;
1234
1235 let Some(spec) = spec else {
1236 write_error(out, &args::unknown_command(args));
1237 return Flow::Continue;
1238 };
1239 if !arity_ok(spec, args.len()) {
1240 server.cmdstats.at(spec).rejected += 1;
1241 write_error(out, &args::wrong_arity(spec.name));
1242 return Flow::Continue;
1243 }
1244
1245 if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1255 server.cmdstats.at(spec).rejected += 1;
1256 out.error_line(b"OOM ", OOM);
1257 return Flow::Continue;
1258 }
1259
1260 server.dirty |= match spec.group {
1267 "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1268 | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1269 1u64 << session.db
1270 }
1271 _ => ALL_DATABASES,
1272 };
1273
1274 let mark = out.len();
1275 let done = if spec.flags.contains(&"blocking") {
1282 blocking::execute(server, session, spec, args, out)
1283 } else {
1284 match spec.group {
1285 "string" => {
1286 let db = session.db;
1287 strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1288 }
1289 "bitmap" => {
1293 let db = session.db;
1294 bits::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1295 }
1296 "hyperloglog" => {
1299 let db = session.db;
1300 hll::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1301 }
1302 "set" => {
1303 let db = session.db;
1304 sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1305 }
1306 "hash" if spec.name == "himport" => {
1311 let db = session.db;
1312 himport::execute(&mut server.dbs[db], &mut session.sets, args, out)
1313 .map(|()| Flow::Continue)
1314 }
1315 "hash" => {
1316 let db = session.db;
1317 hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1318 }
1319 "list" => {
1320 let db = session.db;
1321 lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1322 }
1323 "zset" => {
1324 let db = session.db;
1325 zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1326 }
1327 "geo" => {
1331 let db = session.db;
1332 geo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1333 }
1334 "array" => {
1335 let db = session.db;
1336 arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1337 }
1338 "graph" => {
1339 let db = session.db;
1340 graph::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1341 }
1342 "json" => {
1346 let db = session.db;
1347 json::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1348 }
1349 "vector" => {
1350 let db = session.db;
1351 vectors::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1352 }
1353 "bloom" => {
1354 let db = session.db;
1355 bloom::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1356 }
1357 "cuckoo" => {
1358 let db = session.db;
1359 cuckoo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1360 }
1361 "cms" => {
1362 let db = session.db;
1363 cms::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1364 }
1365 "topk" => {
1366 let db = session.db;
1367 topk::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1368 }
1369 "tdigest" => {
1370 let db = session.db;
1371 tdigest::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1372 }
1373 "ts" => {
1374 let db = session.db;
1375 ts::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1376 }
1377 "stream" => {
1382 let db = session.db;
1383 let now = server.now_ms();
1384 streams::execute(&mut server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1385 }
1386 "keyspace" if spec.name == "migrate" => {
1390 migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1391 }
1392 "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
1395 .map(|()| Flow::Continue),
1396 "search" => {
1399 search::execute(&mut server.search, spec, args, out).map(|()| Flow::Continue)
1400 }
1401 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1402 _ => server::execute(server, session, spec, args, out),
1403 }
1404 };
1405 let flow = match done {
1406 Ok(flow) => flow,
1407 Err(e) => {
1408 out.truncate(mark);
1409 write_error(out, &e);
1410 Flow::Continue
1411 }
1412 };
1413
1414 let row = server.cmdstats.at(spec);
1425 row.calls += 1;
1426 if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1427 row.failed += 1;
1428 }
1429 flow
1430}
1431
1432fn write_error(out: &mut Out, e: &Error) {
1442 let prefix: &[u8] = match e.code() {
1443 Code::WrongType => b"WRONGTYPE ",
1444 Code::Corrupt => b"INVALIDOBJ ",
1448 _ => b"ERR ",
1449 };
1450 out.error_line(prefix, e.message().as_bytes());
1451}
1452
1453#[cfg(test)]
1454mod tests {
1455 use super::*;
1456 use crate::proto::{Limits, Proto};
1457 use crate::request::Argv;
1458
1459 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1464 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1465 for p in parts {
1466 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1467 wire.extend_from_slice(p);
1468 wire.extend_from_slice(b"\r\n");
1469 }
1470 wire
1471 }
1472
1473 struct Fixture {
1475 server: Server,
1476 session: Session,
1477 argv: Argv,
1478 out: Out,
1479 }
1480
1481 impl Fixture {
1482 fn new() -> Fixture {
1483 Fixture::on(Server::new())
1484 }
1485
1486 fn striped(width: usize) -> Fixture {
1488 Fixture::on(Server::with_width(width))
1489 }
1490
1491 fn on(server: Server) -> Fixture {
1492 Fixture {
1493 server,
1494 session: Session::new(7),
1495 argv: Argv::new(),
1496 out: Out::new(Proto::Resp2),
1497 }
1498 }
1499
1500 fn run(&mut self, parts: &[&[u8]]) -> String {
1502 self.flow(parts).1
1503 }
1504
1505 fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1511 let wire = encode(parts);
1512 self.argv.decode(&wire, &Limits::default()).unwrap();
1513 self.out.clear();
1514 execute(
1515 &mut self.server,
1516 &mut self.session,
1517 Args::new(&self.argv, &wire),
1518 &mut self.out,
1519 );
1520 self.out.as_slice().to_vec()
1521 }
1522
1523 fn advance(&mut self, ms: u64) {
1525 self.server.advance_clock_ms(ms);
1526 }
1527
1528 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1530 let wire = encode(parts);
1531 self.argv.decode(&wire, &Limits::default()).unwrap();
1532 self.out.clear();
1533 let flow = execute(
1534 &mut self.server,
1535 &mut self.session,
1536 Args::new(&self.argv, &wire),
1537 &mut self.out,
1538 );
1539 (
1540 flow,
1541 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1542 )
1543 }
1544 }
1545
1546 #[test]
1550 fn rewriting_the_same_keys_does_not_grow_the_server() {
1551 let mut f = Fixture::new();
1552 let val = vec![b'v'; 1024];
1553 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1554
1555 for k in &keys {
1556 f.run(&[b"SET", k, &val]);
1557 }
1558 f.server.compact_step();
1559 let after_first = f.server.memory_bytes();
1560
1561 for _ in 0..500 {
1566 for k in &keys {
1567 f.run(&[b"SET", k, &val]);
1568 }
1569 f.server.compact_step();
1570 }
1571
1572 assert!(
1573 f.server.memory_bytes() <= after_first * 2,
1574 "held {} after five hundred passes against {after_first} after one",
1575 f.server.memory_bytes()
1576 );
1577 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1578 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1579 }
1580
1581 #[test]
1595 fn a_database_nobody_started_on_is_still_collected() {
1596 let mut f = Fixture::new();
1597 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1598 let val = vec![b'v'; 1024];
1599 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1600
1601 for k in &keys {
1602 f.run(&[b"SET", k, &val]);
1603 }
1604 while f.server.compact_step().is_some() {}
1605 assert_eq!(
1606 f.server.dirty & (1 << 9),
1607 0,
1608 "database nine was drained and should not be asked again until it is written to"
1609 );
1610 let after_first = f.server.memory_bytes();
1611
1612 for _ in 0..500 {
1613 for k in &keys {
1614 f.run(&[b"SET", k, &val]);
1615 }
1616 f.server.compact_step();
1617 }
1618
1619 assert!(
1620 f.server.memory_bytes() <= after_first * 2,
1621 "held {} after five hundred passes against {after_first} after one",
1622 f.server.memory_bytes()
1623 );
1624 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1625 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1626 f.run(&[b"SELECT", b"0"]);
1628 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1629 }
1630
1631 #[test]
1632 fn a_command_goes_from_bytes_to_bytes() {
1633 let mut f = Fixture::new();
1634 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1635 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1636 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1637 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1638 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1640 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1641 }
1642
1643 #[test]
1644 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1645 let mut f = Fixture::new();
1646 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1647 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1650 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1651 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1652 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1654 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1655 }
1656
1657 #[test]
1658 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1659 let mut f = Fixture::new();
1660 f.run(&[b"SET", b"k", b"v"]);
1661 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1664 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1665 }
1666
1667 #[test]
1668 fn touch_counts_the_way_exists_counts() {
1669 let mut f = Fixture::new();
1670 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1671 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1672 assert_eq!(
1673 f.run(&[b"TOUCH", b"a", b"a"]),
1674 ":2\r\n",
1675 "twice counts twice"
1676 );
1677 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1678 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1679 }
1680
1681 #[test]
1682 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1683 let mut f = Fixture::new();
1684 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1685 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1686
1687 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1688 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1689 assert_eq!(
1690 f.run(&[b"TTL", b"b"]),
1691 ":100\r\n",
1692 "the source's and not b's"
1693 );
1694 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1695 }
1696
1697 #[test]
1698 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1699 let mut f = Fixture::new();
1700 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1701 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1704 }
1705
1706 #[test]
1707 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1708 let mut f = Fixture::new();
1709 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1710
1711 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1712 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1713 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1716 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1717 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1718 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1719 }
1720
1721 #[test]
1722 fn renaming_a_set_does_not_touch_a_member() {
1723 let mut f = Fixture::new();
1724 for i in 0..300 {
1725 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1726 }
1727 let before = f.server.memory_bytes();
1728
1729 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1730 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1731 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1732 assert!(
1733 f.server.memory_bytes().abs_diff(before) < 256,
1734 "the members were copied: {} against {before}",
1735 f.server.memory_bytes()
1736 );
1737 }
1738
1739 #[test]
1740 fn a_copy_is_a_second_value_and_not_a_second_name() {
1741 let mut f = Fixture::new();
1742 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1743
1744 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1745 f.run(&[b"SADD", b"t", b"m3"]);
1746 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1747 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1748 }
1749
1750 #[test]
1760 fn every_type_can_be_copied() {
1761 let mut f = Fixture::new();
1762 f.run(&[b"SET", b"str", b"v1"]);
1763 f.run(&[b"SADD", b"set", b"m1"]);
1764 f.run(&[b"HSET", b"hash", b"f", b"v"]);
1765 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1766 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1767
1768 for name in [
1769 &b"str"[..],
1770 &b"set"[..],
1771 &b"hash"[..],
1772 &b"list"[..],
1773 &b"zset"[..],
1774 ] {
1775 let dst = [name, b":copy"].concat();
1776 assert_eq!(
1777 f.run(&[b"COPY", name, &dst]),
1778 ":1\r\n",
1779 "copying {}",
1780 String::from_utf8_lossy(name)
1781 );
1782 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1783 }
1784
1785 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1786 let mut want = String::from("*2\r\n");
1787 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1788 want
1789 });
1790 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1791
1792 f.run(&[b"RPUSH", b"list:copy", b"c"]);
1794 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1795 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1796 }
1797
1798 #[test]
1799 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1800 let mut f = Fixture::new();
1801 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1802 f.run(&[b"SET", b"b", b"v2"]);
1803
1804 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1805 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1806 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1807 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1808 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1809 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1810 }
1811
1812 #[test]
1813 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1814 let mut f = Fixture::new();
1815 f.run(&[b"SET", b"a", b"v1"]);
1816
1817 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1820 f.run(&[b"SELECT", b"1"]);
1821 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1822 assert_eq!(
1823 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1824 ":0\r\n",
1825 "taken"
1826 );
1827 assert_eq!(
1828 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1829 ":1\r\n"
1830 );
1831 }
1832
1833 #[test]
1834 fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1835 let mut f = Fixture::new();
1836 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1837 assert_eq!(
1838 f.run(&[b"SORT", b"l"]),
1839 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1840 );
1841 assert_eq!(
1843 f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1844 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1845 );
1846 assert_eq!(
1847 f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1848 "*1\r\n$1\r\n2\r\n"
1849 );
1850 }
1851
1852 #[test]
1853 fn sort_reads_a_key_per_element_for_by_and_for_get() {
1854 let mut f = Fixture::new();
1855 f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1856 f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1857 assert_eq!(
1860 f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1861 "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1862 );
1863 }
1864
1865 #[test]
1866 fn sort_store_writes_a_list_and_answers_its_length() {
1867 let mut f = Fixture::new();
1868 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1869 assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1870 assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1871 assert_eq!(
1872 f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1873 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1874 );
1875 assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1878 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1879 }
1880
1881 #[test]
1882 fn sort_ro_does_not_know_the_word_store() {
1883 let mut f = Fixture::new();
1884 f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1885 assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1886 assert_eq!(
1887 f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1888 "-ERR syntax error\r\n"
1889 );
1890 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1891 }
1892
1893 #[test]
1894 fn sort_refuses_what_it_cannot_sort() {
1895 let mut f = Fixture::new();
1896 assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1897 f.run(&[b"SET", b"s", b"x"]);
1898 assert_eq!(
1899 f.run(&[b"SORT", b"s"]),
1900 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1901 );
1902 f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1903 assert_eq!(
1904 f.run(&[b"SORT", b"words"]),
1905 "-ERR One or more scores can't be converted into double\r\n"
1906 );
1907 assert_eq!(
1908 f.run(&[b"SORT", b"words", b"ALPHA"]),
1909 "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1910 );
1911 assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1912 }
1913
1914 #[test]
1915 fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1916 let mut f = Fixture::new();
1917 assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1918 assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1919 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1920 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1921 assert_eq!(
1922 f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1923 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1924 );
1925 assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1928 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1929 }
1930
1931 #[test]
1932 fn move_answers_zero_when_either_end_says_no() {
1933 let mut f = Fixture::new();
1934 assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1935 assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1936 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1937 assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1938 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1939 assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1942 assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1943 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1944 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1945 }
1946
1947 #[test]
1948 fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1949 let mut f = Fixture::new();
1950 assert_eq!(
1951 f.run(&[b"MOVE", b"a", b"0"]),
1952 "-ERR source and destination objects are the same\r\n"
1953 );
1954 assert_eq!(
1955 f.run(&[b"MOVE", b"a", b"99"]),
1956 "-ERR DB index is out of range\r\n"
1957 );
1958 assert_eq!(
1959 f.run(&[b"MOVE", b"a", b"-1"]),
1960 "-ERR DB index is out of range\r\n"
1961 );
1962 assert_eq!(
1963 f.run(&[b"MOVE", b"a", b"x"]),
1964 "-ERR value is not an integer or out of range\r\n"
1965 );
1966 }
1967
1968 #[test]
1969 fn swapdb_swaps_what_two_connections_would_see() {
1970 let mut f = Fixture::new();
1971 assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1972 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1973 assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1974 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1975
1976 assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1977 assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1979 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1980 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1981 assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1983 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1984 }
1985
1986 #[test]
1987 fn swapdb_says_which_index_it_could_not_read() {
1988 let mut f = Fixture::new();
1989 assert_eq!(
1990 f.run(&[b"SWAPDB", b"x", b"1"]),
1991 "-ERR invalid first DB index\r\n"
1992 );
1993 assert_eq!(
1994 f.run(&[b"SWAPDB", b"0", b"y"]),
1995 "-ERR invalid second DB index\r\n"
1996 );
1997 assert_eq!(
2001 f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2002 "-ERR invalid first DB index\r\n"
2003 );
2004 assert_eq!(
2005 f.run(&[b"SWAPDB", b"0", b"99"]),
2006 "-ERR DB index is out of range\r\n"
2007 );
2008 assert_eq!(
2009 f.run(&[b"SWAPDB", b"-1", b"0"]),
2010 "-ERR DB index is out of range\r\n"
2011 );
2012 }
2013
2014 #[test]
2015 fn wait_answers_zero_replicas_without_waiting() {
2016 let mut f = Fixture::new();
2017 assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2018 assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2019 assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2022 assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2025 assert_eq!(
2026 f.run(&[b"WAIT", b"x", b"0"]),
2027 "-ERR value is not an integer or out of range\r\n"
2028 );
2029 assert_eq!(
2030 f.run(&[b"WAIT", b"0", b"-1"]),
2031 "-ERR timeout is negative\r\n"
2032 );
2033 assert_eq!(
2034 f.run(&[b"WAIT", b"0", b"1.5"]),
2035 "-ERR timeout is not an integer or out of range\r\n"
2036 );
2037 }
2038
2039 #[test]
2040 fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2041 let mut f = Fixture::new();
2042 assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2043 assert_eq!(
2044 f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2045 "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2046 );
2047 assert_eq!(
2048 f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2049 "-ERR value is out of range, value must between 0 and 1\r\n"
2050 );
2051 assert_eq!(
2052 f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2053 "-ERR value is out of range, must be positive\r\n"
2054 );
2055 assert_eq!(
2058 f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2059 "-ERR timeout is negative\r\n"
2060 );
2061 }
2062
2063 fn payload(reply: &[u8]) -> Vec<u8> {
2067 let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2068 reply[head + 2..reply.len() - 2].to_vec()
2069 }
2070
2071 #[test]
2072 fn a_value_survives_a_dump_and_a_restore() {
2073 let mut f = Fixture::new();
2074 f.run(&[b"SET", b"s", b"hello"]);
2075 f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2076 f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2077 f.run(&[b"SADD", b"u", b"x", b"y"]);
2078 f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2079 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2080
2081 for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2082 let mut copy = key.to_vec();
2083 copy.push(b'2');
2084 let bytes = payload(&f.raw(&[b"DUMP", key]));
2085 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
2086 assert_eq!(f.run(&[b"TYPE", ©]), f.run(&[b"TYPE", key]));
2087 }
2088
2089 assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2090 assert_eq!(
2091 f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2092 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2093 );
2094 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2095 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2096 assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2097 assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2098 assert_eq!(
2101 f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2102 f.run(&[b"OBJECT", b"ENCODING", b"t"])
2103 );
2104 }
2105
2106 #[test]
2107 fn a_dumped_hash_keeps_its_field_deadlines() {
2108 let mut f = Fixture::new();
2109 f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2110 assert_eq!(
2111 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2112 "*1\r\n:1\r\n"
2113 );
2114 let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2115 assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2116 assert_eq!(
2117 f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2118 "*2\r\n:-1\r\n:100\r\n"
2119 );
2120 }
2121
2122 #[test]
2123 fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2124 let mut f = Fixture::new();
2125 f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2126 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2127 assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2128 assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2129 assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2130 assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2131 assert_eq!(
2134 f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2135 "+OK\r\n"
2136 );
2137 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2138 }
2139
2140 #[test]
2141 fn dump_answers_nothing_for_a_key_that_is_not_there() {
2142 let mut f = Fixture::new();
2143 assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2144 f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2145 f.advance(50);
2146 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2147 }
2148
2149 #[test]
2150 fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2151 let mut f = Fixture::new();
2152 f.run(&[b"SET", b"a", b"first"]);
2153 f.run(&[b"SET", b"b", b"second"]);
2154 let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2155 assert_eq!(
2156 f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2157 "-BUSYKEY Target key name already exists.\r\n"
2158 );
2159 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2160 assert_eq!(
2161 f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2162 "+OK\r\n"
2163 );
2164 assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2165 }
2166
2167 #[test]
2171 fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2172 let mut f = Fixture::new();
2173 f.run(&[b"SET", b"a", b"v"]);
2174 assert_eq!(
2175 f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2176 "-BUSYKEY Target key name already exists.\r\n"
2177 );
2178 assert_eq!(
2181 f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2182 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2183 );
2184 }
2185
2186 #[test]
2187 fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2188 let mut f = Fixture::new();
2189 f.run(&[b"SET", b"a", b"hello"]);
2190 let good = payload(&f.raw(&[b"DUMP", b"a"]));
2191
2192 let mut flipped = good.clone();
2193 flipped[2] ^= 0x40;
2194 assert_eq!(
2195 f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2196 "-ERR DUMP payload version or checksum are wrong\r\n"
2197 );
2198 assert_eq!(
2199 f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2200 "-ERR DUMP payload version or checksum are wrong\r\n"
2201 );
2202 let mut truncated = good[..1].to_vec();
2206 truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2207 let crc = yo_common::crc::crc64(0, &truncated);
2208 truncated.extend_from_slice(&crc.to_le_bytes());
2209 assert_eq!(
2210 f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2211 "-ERR Bad data format\r\n"
2212 );
2213 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2214 }
2215
2216 #[test]
2217 fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2218 let mut f = Fixture::new();
2219 f.run(&[b"SET", b"a", b"v"]);
2220 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2221 assert_eq!(
2222 f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2223 "-ERR Invalid TTL value, must be >= 0\r\n"
2224 );
2225 assert_eq!(
2226 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2227 "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2228 );
2229 assert_eq!(
2230 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2231 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2232 );
2233 assert_eq!(
2235 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2236 "+OK\r\n"
2237 );
2238 assert_eq!(
2239 f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2240 "+OK\r\n"
2241 );
2242 }
2243
2244 #[test]
2248 fn restore_takes_idletime_or_freq_and_not_both() {
2249 let mut f = Fixture::new();
2250 f.run(&[b"SET", b"a", b"v"]);
2251 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2252 assert_eq!(
2253 f.run(&[
2254 b"RESTORE",
2255 b"b",
2256 b"0",
2257 &bytes,
2258 b"IDLETIME",
2259 b"1",
2260 b"FREQ",
2261 b"2"
2262 ]),
2263 "-ERR syntax error\r\n"
2264 );
2265 assert_eq!(
2266 f.run(&[
2267 b"RESTORE",
2268 b"b",
2269 b"0",
2270 &bytes,
2271 b"FREQ",
2272 b"2",
2273 b"IDLETIME",
2274 b"1"
2275 ]),
2276 "-ERR syntax error\r\n"
2277 );
2278 assert_eq!(
2279 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2280 "-ERR syntax error\r\n"
2281 );
2282 assert_eq!(
2283 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2284 "-ERR syntax error\r\n"
2285 );
2286 }
2287
2288 #[test]
2289 fn copy_checks_its_options_before_it_looks_for_anything() {
2290 let mut f = Fixture::new();
2291 assert_eq!(
2294 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2295 "-ERR DB index is out of range\r\n"
2296 );
2297 assert_eq!(
2298 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2299 "-ERR DB index is out of range\r\n"
2300 );
2301 assert_eq!(
2302 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2303 "-ERR value is not an integer or out of range\r\n"
2304 );
2305 assert_eq!(
2306 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2307 "-ERR syntax error\r\n"
2308 );
2309 assert_eq!(
2310 f.run(&[b"COPY", b"a", b"a"]),
2311 "-ERR source and destination objects are the same\r\n"
2312 );
2313 assert_eq!(
2315 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2316 ":0\r\n"
2317 );
2318 }
2319
2320 #[test]
2321 fn time_is_two_bulk_strings_and_moves() {
2322 let mut f = Fixture::new();
2323 let first = f.run(&[b"TIME"]);
2324 assert!(first.starts_with("*2\r\n$"), "got {first}");
2325 let parts: Vec<&str> = first.split("\r\n").collect();
2326 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2327 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2328 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2329 assert!((0..1_000_000).contains(µs), "got {micros}");
2330 assert_ne!(first, f.run(&[b"TIME"]));
2334 }
2335
2336 #[test]
2337 fn a_keyspace_scan_walks_every_key_once() {
2338 let mut f = Fixture::new();
2339 for i in 0..500 {
2340 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2341 }
2342
2343 let mut seen: Vec<String> = Vec::new();
2344 let mut cursor = "0".to_owned();
2345 let mut calls = 0;
2346 loop {
2347 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2348 seen.extend(keys);
2349 cursor = next;
2350 calls += 1;
2351 assert!(calls < 10_000, "the cursor is not advancing");
2352 if cursor == "0" {
2353 break;
2354 }
2355 }
2356
2357 seen.sort();
2358 seen.dedup();
2359 assert_eq!(seen.len(), 500, "every key once and only once");
2360 assert!(calls > 1, "500 keys came back in one batch");
2363 }
2364
2365 #[test]
2366 fn a_scan_narrows_by_pattern_and_by_type() {
2367 let mut f = Fixture::new();
2368 f.run(&[b"SET", b"str", b"v"]);
2369 f.run(&[b"SADD", b"members", b"a"]);
2370 f.run(&[b"HSET", b"fields", b"f", b"v"]);
2371
2372 let all = |f: &mut Fixture, args: &[&[u8]]| {
2373 let mut out: Vec<String> = Vec::new();
2374 let mut cursor = "0".to_owned();
2375 loop {
2376 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2377 line.extend_from_slice(args);
2378 let (next, keys) = scan_reply(&f.run(&line));
2379 out.extend(keys);
2380 cursor = next;
2381 if cursor == "0" {
2382 break;
2383 }
2384 }
2385 out.sort();
2386 out
2387 };
2388
2389 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2390 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2391 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2392 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2394 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2396 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2397 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2399 }
2400
2401 #[test]
2402 fn a_scan_says_what_is_wrong_with_it() {
2403 let mut f = Fixture::new();
2404 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2405 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2406 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2407 assert_eq!(
2408 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2409 "-ERR syntax error\r\n"
2410 );
2411 assert_eq!(
2412 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2413 "-ERR value is not an integer or out of range\r\n"
2414 );
2415 assert_eq!(
2416 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2417 "-ERR syntax error\r\n"
2418 );
2419 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2424 }
2425
2426 #[test]
2427 fn keys_and_randomkey_look_at_the_whole_database() {
2428 let mut f = Fixture::new();
2429 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2430 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2431
2432 for name in ["one", "two", "three"] {
2433 f.run(&[b"SET", name.as_bytes(), b"v"]);
2434 }
2435 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2436 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2437 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2438
2439 for _ in 0..50 {
2440 let got = f.run(&[b"RANDOMKEY"]);
2441 assert!(
2442 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2443 "got {got}"
2444 );
2445 }
2446 }
2447
2448 #[test]
2449 fn a_walk_does_not_answer_keys_that_have_expired() {
2450 let mut f = Fixture::new();
2451 f.run(&[b"SET", b"alive", b"v"]);
2452 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2453 f.server.advance_clock_ms(2);
2454 assert_eq!(
2455 f.run(&[b"DBSIZE"]),
2456 ":2\r\n",
2457 "nothing has collected it yet"
2458 );
2459
2460 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2461 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2462 assert_eq!(keys, ["alive"]);
2463 for _ in 0..20 {
2464 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2465 }
2466 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2469 }
2470
2471 #[test]
2472 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2473 let mut f = Fixture::new();
2474 f.run(&[b"SET", b"k", b"v"]);
2475 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2476 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2477
2478 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2479 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2480 let ms = int(&f.run(&[b"PTTL", b"k"]));
2481 assert!((99_000..=100_000).contains(&ms), "got {ms}");
2482
2483 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2485 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2486 assert_eq!(at, (at_ms + 500) / 1000);
2487 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2488
2489 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2490 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2491 assert_eq!(
2492 f.run(&[b"PERSIST", b"k"]),
2493 ":0\r\n",
2494 "nothing to take off the second time"
2495 );
2496 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2497 assert_eq!(
2498 f.run(&[b"GET", b"k"]),
2499 "$1\r\nv\r\n",
2500 "and the value went through all of that untouched"
2501 );
2502 }
2503
2504 #[test]
2505 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2506 let mut f = Fixture::new();
2507 f.run(&[b"SET", b"str", b"v"]);
2508 f.run(&[b"SADD", b"set", b"a", b"b"]);
2509 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2510
2511 for key in [b"str".as_slice(), b"set", b"hash"] {
2512 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2513 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2514 }
2515 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2518 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2519 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2520 }
2521
2522 #[test]
2523 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2524 let mut f = Fixture::new();
2525 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2526 f.run(&[b"SET", key, b"v"]);
2527 }
2528 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2532 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2533 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2534 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2535 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2536 assert_eq!(
2537 f.run(&[b"EXPIRE", b"a", b"100"]),
2538 ":0\r\n",
2539 "and the key really went, so there is nothing to put a deadline on"
2540 );
2541 }
2542
2543 #[test]
2544 fn the_four_conditions_decide_whether_the_deadline_moves() {
2545 let mut f = Fixture::new();
2546 f.run(&[b"SET", b"k", b"v"]);
2547
2548 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2549 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2550 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2551 assert_eq!(
2552 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2553 ":1\r\n",
2554 "no deadline reads as infinitely far away, so LT passes where GT fails"
2555 );
2556
2557 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2558 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2559 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2560 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2561 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2562 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2563
2564 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2567 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2568 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2569 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2570 }
2571
2572 #[test]
2573 fn the_conditions_are_a_set_and_not_a_keyword() {
2574 let mut f = Fixture::new();
2575 f.run(&[b"SET", b"k", b"v"]);
2576
2577 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2578 assert_eq!(
2579 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2580 ":0\r\n",
2581 "the same keyword twice means it once, and NX now has a deadline to fail on"
2582 );
2583
2584 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2587 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2588 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2589 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2590 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2591 f.run(&[b"PERSIST", b"k"]);
2592 assert_eq!(
2593 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2594 ":0\r\n",
2595 "where LT on its own would have taken it"
2596 );
2597 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2598 }
2599
2600 #[test]
2601 fn a_key_is_gone_once_its_moment_passes() {
2602 let mut f = Fixture::new();
2603 f.run(&[b"SET", b"k", b"v"]);
2604 f.run(&[b"EXPIRE", b"k", b"100"]);
2605
2606 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2607 f.server.set_clock_ms(at as u64 + 1);
2608 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2609 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2610 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2611 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2612 }
2613
2614 #[test]
2615 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2616 let mut f = Fixture::new();
2617 f.run(&[b"SET", b"k", b"v"]);
2618 for (bad, want) in [
2619 (
2620 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2621 "-ERR value is not an integer or out of range\r\n",
2622 ),
2623 (
2624 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2625 "-ERR Unsupported option MAYBE\r\n",
2626 ),
2627 (
2628 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2629 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2630 ),
2631 (
2632 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2633 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2634 ),
2635 (
2636 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2637 "-ERR GT and LT options at the same time are not compatible\r\n",
2638 ),
2639 (
2642 &[b"EXPIRE", b"k", b"9223372036854775807"],
2643 "-ERR invalid expire time in 'expire' command\r\n",
2644 ),
2645 (
2646 &[b"EXPIREAT", b"k", b"9223372036854775807"],
2647 "-ERR invalid expire time in 'expireat' command\r\n",
2648 ),
2649 (
2650 &[b"PEXPIRE", b"k", b"9223372036854775807"],
2651 "-ERR invalid expire time in 'pexpire' command\r\n",
2652 ),
2653 ] {
2654 assert_eq!(f.run(bad), want, "for {bad:?}");
2655 }
2656 assert_eq!(
2657 f.run(&[b"TTL", b"k"]),
2658 ":-1\r\n",
2659 "and none of those put a deadline on anything"
2660 );
2661
2662 assert_eq!(
2666 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2667 ":1\r\n"
2668 );
2669 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2670 }
2671
2672 #[test]
2673 fn flushing_empties_this_database_or_every_one_of_them() {
2674 let mut f = Fixture::new();
2675 f.run(&[b"SELECT", b"0"]);
2676 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2677 f.run(&[b"SELECT", b"1"]);
2678 f.run(&[b"SET", b"c", b"3"]);
2679 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2680 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2683 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2684 f.run(&[b"SELECT", b"0"]);
2686 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2687 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2688 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2689 f.run(&[b"SELECT", b"1"]);
2690 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2691 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2694 assert_eq!(
2695 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2696 "-ERR syntax error\r\n"
2697 );
2698 }
2699
2700 #[test]
2701 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2702 let mut f = Fixture::new();
2703 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2704 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2705 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2706 assert_eq!(
2709 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2710 "*2\r\n:0\r\n:0\r\n"
2711 );
2712 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2713 assert_eq!(
2714 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2715 "*0\r\n"
2716 );
2717 assert_eq!(
2718 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2719 "-ERR Library not found\r\n"
2720 );
2721
2722 assert_eq!(
2725 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2726 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2727 );
2728 assert_eq!(
2729 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2730 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2731 );
2732 assert_eq!(
2735 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2736 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2737 );
2738 assert_eq!(
2739 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2740 "-ERR Unknown argument bogus\r\n"
2741 );
2742 assert_eq!(
2743 f.run(&[b"SCRIPT", b"EXISTS"]),
2744 "-ERR wrong number of arguments for 'script|exists' command\r\n"
2745 );
2746
2747 assert_eq!(
2750 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2751 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2752 );
2753 assert_eq!(
2754 f.run(&[b"FUNCTION", b"STATS"]),
2755 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2756 );
2757 }
2758
2759 #[test]
2760 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2761 let mut f = Fixture::new();
2762 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2763 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2764 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2765 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2768 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2769 f.run(&[b"SET", b"k", b"hello"]);
2772 assert_eq!(
2773 f.run(&[b"INCR", b"k"]),
2774 "-ERR value is not an integer or out of range\r\n"
2775 );
2776 assert_eq!(
2777 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2778 "-ERR increment would produce NaN or Infinity\r\n"
2779 );
2780 }
2781
2782 #[test]
2787 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2788 let mut f = Fixture::new();
2789 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2790 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2793 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2794 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2795 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2796 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2797 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2798 assert_eq!(
2799 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2800 "*2\r\n:1\r\n:0\r\n",
2801 "a refused increment reports the value it left alone and applied nothing"
2802 );
2803 assert_eq!(
2804 f.run(&[
2805 b"INCREX",
2806 b"n",
2807 b"BYINT",
2808 b"5",
2809 b"UBOUND",
2810 b"3",
2811 b"SATURATE"
2812 ]),
2813 "*2\r\n:3\r\n:2\r\n"
2814 );
2815 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2816 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2817 }
2818
2819 #[test]
2820 fn the_same_answers_come_out_in_resp3_spelling() {
2821 let mut f = Fixture::new();
2822 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2823 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2824 assert_eq!(
2827 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2828 "*2\r\n,1.5\r\n,1.5\r\n"
2829 );
2830 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2831 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2834 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2835 }
2836
2837 #[test]
2838 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2839 let mut f = Fixture::new();
2840 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2841 assert_eq!(flow, Flow::Continue);
2842 assert_eq!(
2843 reply,
2844 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2845 );
2846 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2849 assert_eq!(reply.matches("\r\n").count(), 1);
2850 }
2851
2852 #[test]
2853 fn arity_is_checked_before_the_command_is() {
2854 let mut f = Fixture::new();
2855 assert_eq!(
2856 f.run(&[b"GET"]),
2857 "-ERR wrong number of arguments for 'get' command\r\n"
2858 );
2859 assert_eq!(
2860 f.run(&[b"MSET", b"k"]),
2861 "-ERR wrong number of arguments for 'mset' command\r\n"
2862 );
2863 assert_eq!(
2867 f.run(&[b"PING", b"a", b"b"]),
2868 "-ERR wrong number of arguments for 'ping' command\r\n"
2869 );
2870 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2871 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2872 assert_eq!(
2874 f.run(&[b"DELEX", b"k", b"IFEQ"]),
2875 "-ERR wrong number of arguments for 'delex' command\r\n"
2876 );
2877 }
2878
2879 #[test]
2883 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2884 let mut f = Fixture::new();
2885 let syntax = "-ERR syntax error\r\n";
2886 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2887 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2888 assert_eq!(
2889 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2890 syntax
2891 );
2892 assert_eq!(
2893 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2894 syntax
2895 );
2896 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2897 assert_eq!(
2899 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2900 "+OK\r\n"
2901 );
2902 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2903 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2904 assert_eq!(
2906 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2907 syntax
2908 );
2909 assert_eq!(
2910 f.run(&[b"INCREX", b"n", b"ENX"]),
2911 "-ERR ENX flag requires an expiration\r\n"
2912 );
2913 assert_eq!(
2914 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2915 "-ERR UBOUND is not an integer or out of range\r\n"
2916 );
2917 assert_eq!(
2918 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2919 "-ERR LBOUND can't be greater than UBOUND\r\n"
2920 );
2921 assert_eq!(
2922 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2923 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2924 );
2925 }
2926
2927 #[test]
2931 fn the_expiry_rules_are_redis_own() {
2932 let mut f = Fixture::new();
2933 let bad = "-ERR invalid expire time in 'set' command\r\n";
2934 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2935 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2936 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2937 assert_eq!(
2938 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2939 bad
2940 );
2941 assert_eq!(
2942 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2943 "-ERR value is not an integer or out of range\r\n"
2944 );
2945 assert_eq!(
2946 f.run(&[b"SETEX", b"k", b"0", b"v"]),
2947 "-ERR invalid expire time in 'setex' command\r\n"
2948 );
2949 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2950 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2951 assert_eq!(
2952 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2953 "-ERR syntax error\r\n",
2954 "the option list is still checked before the key is looked up"
2955 );
2956 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2958 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2959 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2960 }
2961
2962 #[test]
2963 fn mset_takes_its_pairs_from_the_read_buffer() {
2964 let mut f = Fixture::new();
2965 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2966 assert_eq!(
2967 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2968 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2969 );
2970 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2971 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2972 assert_eq!(
2973 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2974 "-ERR wrong number of key-value pairs\r\n"
2975 );
2976 assert_eq!(
2977 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2978 "-ERR invalid numkeys value\r\n"
2979 );
2980 assert_eq!(
2981 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2982 "-ERR invalid numkeys value\r\n"
2983 );
2984 }
2985
2986 #[test]
2987 fn lcs_answers_the_length_the_string_and_the_runs() {
2988 let mut f = Fixture::new();
2989 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2990 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
2991 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
2992 assert_eq!(
2993 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
2994 "*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"
2995 );
2996 assert_eq!(
2999 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3000 "$6\r\nmytext\r\n"
3001 );
3002 }
3003
3004 #[test]
3005 fn select_moves_the_connection_and_the_databases_stay_apart() {
3006 let mut f = Fixture::new();
3007 f.run(&[b"SET", b"k", b"zero"]);
3008 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3009 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3010 f.run(&[b"SET", b"k", b"four"]);
3011 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3012 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3013 assert_eq!(
3014 f.run(&[b"SELECT", b"99"]),
3015 "-ERR DB index is out of range\r\n"
3016 );
3017 assert_eq!(
3018 f.run(&[b"SELECT", b"-1"]),
3019 "-ERR DB index is out of range\r\n"
3020 );
3021 assert_eq!(
3022 f.run(&[b"SELECT", b"abc"]),
3023 "-ERR value is not an integer or out of range\r\n"
3024 );
3025 f.run(&[b"SELECT", b"4"]);
3027 f.run(&[b"RESET"]);
3028 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3029 }
3030
3031 #[test]
3032 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3033 let mut f = Fixture::new();
3034 let reply = f.run(&[b"HELLO"]);
3035 assert!(reply.starts_with("*14\r\n"), "{reply}");
3036 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3037 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3038 assert!(
3039 reply.contains(":7\r\n"),
3040 "the connection id is in there: {reply}"
3041 );
3042 assert_eq!(
3043 f.run(&[b"HELLO", b"4"]),
3044 "-NOPROTO unsupported protocol version\r\n"
3045 );
3046 assert_eq!(
3047 f.run(&[b"HELLO", b"abc"]),
3048 "-ERR Protocol version is not an integer or out of range\r\n"
3049 );
3050 assert_eq!(
3051 f.run(&[b"HELLO", b"3", b"SETNAME"]),
3052 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3053 );
3054 assert!(
3055 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3056 .starts_with("%7\r\n")
3057 );
3058 assert_eq!(f.session.name(), b"bob");
3059 f.run(&[b"RESET"]);
3060 assert_eq!(f.session.name(), b"");
3061 }
3062
3063 #[test]
3064 fn command_describes_this_server_in_the_shape_a_driver_reads() {
3065 let mut f = Fixture::new();
3066 let count = format!(":{}\r\n", COMMANDS.len());
3067 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3068 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3069 assert_eq!(
3070 info,
3071 "*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\
3072 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3073 );
3074 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3076 assert_eq!(
3077 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3078 "*1\r\n$8\r\ngetrange\r\n"
3079 );
3080 assert_eq!(
3081 f.run(&[b"COMMAND", b"NOPE"]),
3082 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3083 );
3084 }
3085
3086 #[test]
3090 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3091 let mut f = Fixture::new();
3092 assert_eq!(
3093 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3094 "*1\r\n$1\r\nk\r\n"
3095 );
3096 assert_eq!(
3097 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3098 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3099 );
3100 assert_eq!(
3101 f.run(&[
3102 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3103 ]),
3104 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3105 );
3106 assert_eq!(
3107 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3108 "-ERR The command has no key arguments\r\n"
3109 );
3110 assert_eq!(
3111 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3112 "-ERR Invalid number of arguments specified for command\r\n"
3113 );
3114 }
3115
3116 #[test]
3117 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3118 let mut f = Fixture::new();
3119 assert_eq!(
3120 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3121 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3122 );
3123 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3126 assert!(both.starts_with("*6\r\n"), "{both}");
3127 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3128 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3129 assert_eq!(
3130 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3131 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3132 );
3133 assert_eq!(
3134 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3135 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3136 );
3137 assert_eq!(
3138 f.run(&[b"CONFIG", b"GET"]),
3139 "-ERR wrong number of arguments for 'config|get' command\r\n"
3140 );
3141 assert_eq!(
3145 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3146 "-ERR wrong number of arguments for 'config|set' command\r\n"
3147 );
3148 assert_eq!(
3149 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3150 "-ERR syntax error\r\n"
3151 );
3152 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3153 assert_eq!(
3154 f.run(&[b"CONFIG", b"REWRITE"]),
3155 "-ERR The server is running without a config file\r\n"
3156 );
3157 }
3158
3159 #[test]
3160 fn the_eviction_policy_reads_back_what_was_written_to_it() {
3161 let mut f = Fixture::new();
3162 assert_eq!(
3163 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3164 "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3165 );
3166 assert_eq!(
3167 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3168 "+OK\r\n",
3169 "the name is matched without regard to case, like every other one"
3170 );
3171 assert_eq!(
3172 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3173 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3174 );
3175 assert!(
3177 f.run(&[b"INFO", b"memory"])
3178 .contains("maxmemory_policy:allkeys-lfu"),
3179 "INFO and CONFIG disagree about the policy"
3180 );
3181 assert_eq!(
3185 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3186 "-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"
3187 );
3188 assert_eq!(
3191 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3192 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3193 );
3194 f.run(&[
3195 b"CONFIG",
3196 b"SET",
3197 b"hash-max-listpack-entries",
3198 b"7",
3199 b"maxmemory-policy",
3200 b"nonsense",
3201 ]);
3202 assert_eq!(
3203 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3204 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3205 );
3206 }
3207
3208 #[test]
3209 fn the_three_eviction_numbers_read_back_too() {
3210 let mut f = Fixture::new();
3211 for (name, default, set) in [
3212 ("maxmemory-samples", "5", "12"),
3213 ("lfu-log-factor", "10", "3"),
3214 ("lfu-decay-time", "1", "60"),
3215 ] {
3216 let get = || {
3217 format!(
3218 "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3219 name.len(),
3220 default.len()
3221 )
3222 };
3223 assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3224 assert_eq!(
3225 f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3226 "+OK\r\n"
3227 );
3228 assert_eq!(
3229 f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3230 format!(
3231 "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3232 name.len(),
3233 set.len()
3234 )
3235 );
3236 assert_eq!(
3239 f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3240 format!(
3241 "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3242 )
3243 );
3244 }
3245 }
3246
3247 #[test]
3248 fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3249 let mut f = Fixture::new();
3250 assert_eq!(
3251 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3252 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3253 "no limit is the default"
3254 );
3255 for (typed, bytes) in [
3258 (&b"1024"[..], "1024"),
3259 (b"1k", "1000"),
3260 (b"1kb", "1024"),
3261 (b"1M", "1000000"),
3262 (b"1Mb", "1048576"),
3263 (b"1gb", "1073741824"),
3264 (b"100mb", "104857600"),
3265 ] {
3266 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3267 assert_eq!(
3268 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3269 format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3270 "set {}",
3271 String::from_utf8_lossy(typed)
3272 );
3273 }
3274 assert!(
3275 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3276 "the report agrees with the setting"
3277 );
3278
3279 for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3282 assert_eq!(
3283 f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3284 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3285 "refused {}",
3286 String::from_utf8_lossy(bad)
3287 );
3288 }
3289 assert!(
3290 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3291 "and the refusal left the old one alone"
3292 );
3293 }
3294
3295 #[test]
3296 fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3297 let mut f = Fixture::new();
3298 f.run(&[b"SET", b"here", b"already"]);
3299 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3303 assert_eq!(
3304 f.run(&[b"SET", b"k", b"v"]),
3305 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3306 );
3307 assert_eq!(
3308 f.run(&[b"LPUSH", b"l", b"v"]),
3309 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3310 );
3311 assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3313 assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3314 assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3315
3316 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3318 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3319 }
3320
3321 #[test]
3322 fn an_allkeys_policy_makes_room_instead_of_refusing() {
3323 let mut f = Fixture::new();
3324 let val = vec![b'v'; 256];
3325 for i in 0..24000u32 {
3326 let k = format!("key:{i:08}");
3327 f.run(&[b"SET", k.as_bytes(), &val]);
3328 }
3329 let full = f.server.memory_bytes();
3330 assert!(
3331 full > 3 * 1024 * 1024,
3332 "the arena is several segments: {full}"
3333 );
3334
3335 let limit = full - 2 * 1024 * 1024;
3339 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3340 f.run(&[
3341 b"CONFIG",
3342 b"SET",
3343 b"maxmemory",
3344 limit.to_string().as_bytes(),
3345 ]);
3346
3347 for i in 0..2000u32 {
3351 let k = format!("new:{i:08}");
3352 assert_eq!(
3353 f.run(&[b"SET", k.as_bytes(), &val]),
3354 "+OK\r\n",
3355 "write {i} was refused"
3356 );
3357 f.server.refresh_memory();
3358 if f.server.memory_bytes() <= limit {
3359 break;
3360 }
3361 }
3362 assert!(
3363 f.server.memory_bytes() <= limit,
3364 "it never got under: {} against {limit}",
3365 f.server.memory_bytes()
3366 );
3367 let info = f.run(&[b"INFO", b"stats"]);
3368 assert!(!info.contains("evicted_keys:0"), "{info}");
3369 assert!(
3370 f.run(&[b"DBSIZE"]) != ":0\r\n",
3371 "and it did not empty the database to get there"
3372 );
3373 }
3374
3375 #[test]
3376 fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3377 let mut f = Fixture::new();
3384 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3385 let big = vec![b'v'; 200];
3386
3387 for i in 0..400u32 {
3388 let n = i.to_string();
3389 let n = n.as_bytes();
3390 f.run(&[b"SADD", b"s", n]);
3391 f.run(&[b"SADD", b"s2", &big]);
3392 f.run(&[b"HSET", b"h", n, &big]);
3393 f.run(&[b"RPUSH", b"l", &big]);
3394 f.run(&[b"ZADD", b"z", n, n]);
3395 f.run(&[b"ARSET", b"a", n, &big]);
3396 if i % 7 == 0 {
3397 f.run(&[b"SREM", b"s", n]);
3398 f.run(&[b"HDEL", b"h", n]);
3399 f.run(&[b"LPOP", b"l"]);
3400 f.run(&[b"ZREM", b"z", n]);
3401 f.run(&[b"ARDEL", b"a", n]);
3402 }
3403 if i % 53 == 0 {
3404 f.run(&[b"DEL", b"s2"]);
3407 }
3408 assert_eq!(
3409 f.server.settled_memory(),
3410 f.server.memory_bytes(),
3411 "after round {i}"
3412 );
3413 }
3414
3415 assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3418 assert!(
3419 f.server.memory_bytes() > 512 * 1024,
3420 "{}",
3421 f.server.memory_bytes()
3422 );
3423
3424 f.run(&[b"FLUSHALL"]);
3426 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3427 }
3428
3429 #[test]
3430 fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3431 let mut f = Fixture::new();
3436 for i in 0..200u32 {
3437 let n = i.to_string();
3438 f.run(&[b"SADD", b"s", n.as_bytes()]);
3439 f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3440 }
3441 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3442 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3443
3444 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3445 for i in 200..400u32 {
3446 let n = i.to_string();
3447 f.run(&[b"SADD", b"s", n.as_bytes()]);
3448 }
3449 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3450 assert_eq!(
3451 f.server.settled_memory(),
3452 f.server.memory_bytes(),
3453 "the writes it was not watching are in the number it started from"
3454 );
3455 }
3456
3457 #[test]
3458 fn evicted_keys_and_expired_keys_are_different_numbers() {
3459 let mut f = Fixture::new();
3460 f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3463 f.server.advance_clock_ms(20);
3464 f.run(&[b"GET", b"gone"]);
3465 let info = f.run(&[b"INFO", b"stats"]);
3466 assert!(info.contains("expired_keys:1"), "{info}");
3467 assert!(info.contains("evicted_keys:0"), "{info}");
3468 }
3469
3470 #[test]
3471 fn the_object_subcommands_follow_the_policy() {
3472 let mut f = Fixture::new();
3473 f.run(&[b"SET", b"s", b"v"]);
3474 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3478 assert!(
3479 f.run(&[b"OBJECT", b"FREQ", b"s"])
3480 .starts_with("-ERR An LFU maxmemory policy is not selected"),
3481 );
3482
3483 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3484 assert!(
3485 f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3486 .starts_with("-ERR An LFU maxmemory policy is selected"),
3487 );
3488 assert!(
3493 f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3494 "FREQ should answer under an LFU policy"
3495 );
3496 }
3497
3498 #[test]
3499 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3500 let mut f = Fixture::new();
3501 f.run(&[b"SET", b"s", b"hello"]);
3502 f.run(&[b"SET", b"n", b"123"]);
3503 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3504 f.run(&[b"SADD", b"ss", b"a", b"b"]);
3505 f.run(&[b"HSET", b"h", b"f", b"v"]);
3506 for (key, want) in [
3507 (b"s".as_slice(), "embstr"),
3508 (b"n", "int"),
3509 (b"si", "intset"),
3510 (b"ss", "listpack"),
3511 (b"h", "listpack"),
3512 ] {
3513 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3514 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3515 }
3516
3517 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3520 assert_eq!(
3521 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3522 "$10\r\nlistpackex\r\n"
3523 );
3524
3525 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3526 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3527 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3528 }
3529
3530 #[test]
3531 fn object_answers_nil_for_a_key_that_is_not_there() {
3532 let mut f = Fixture::new();
3533 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3534 assert_eq!(
3535 f.run(&[b"OBJECT", sub, b"nokey"]),
3536 "$-1\r\n",
3537 "a nil and not an error, which is what 8.10.1 does"
3538 );
3539 }
3540 f.run(&[b"SET", b"s", b"v"]);
3543 assert!(
3544 f.run(&[b"OBJECT", b"FREQ", b"s"])
3545 .starts_with("-ERR An LFU maxmemory policy is not"),
3546 );
3547 assert_eq!(
3548 f.run(&[b"OBJECT", b"NOPE", b"s"]),
3549 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3550 );
3551 assert_eq!(
3552 f.run(&[b"OBJECT", b"ENCODING"]),
3553 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3554 );
3555 assert_eq!(
3556 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3557 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3558 );
3559 assert_eq!(
3560 f.run(&[b"OBJECT"]),
3561 "-ERR wrong number of arguments for 'object' command\r\n"
3562 );
3563 }
3564
3565 #[test]
3566 fn config_moves_the_ladder_and_object_encoding_agrees() {
3567 let mut f = Fixture::new();
3568 assert_eq!(
3569 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3570 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3571 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3572 );
3573 assert_eq!(
3576 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3577 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3578 );
3579 assert!(
3580 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3581 .starts_with("*8\r\n")
3582 );
3583 assert!(
3584 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3585 .starts_with("*6\r\n")
3586 );
3587
3588 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3589 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3590
3591 assert_eq!(
3592 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3593 "+OK\r\n",
3594 "written under the old name and read back under the new one"
3595 );
3596 assert_eq!(
3597 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3598 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3599 );
3600 assert_eq!(
3601 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3602 "$8\r\nlistpack\r\n",
3603 "the hash that already exists is left exactly where it was"
3604 );
3605 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3606 assert_eq!(
3607 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3608 "$9\r\nhashtable\r\n",
3609 "and the next one built goes straight to a table"
3610 );
3611
3612 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3614 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3615 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3616 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3617 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3618 assert_eq!(
3619 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3620 "$9\r\nhashtable\r\n"
3621 );
3622 }
3623
3624 #[test]
3625 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3626 let mut f = Fixture::new();
3627 assert_eq!(
3628 f.run(&[
3629 b"CONFIG",
3630 b"SET",
3631 b"hash-max-listpack-entries",
3632 b"7",
3633 b"set-max-listpack-entries",
3634 b"abc"
3635 ]),
3636 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3637 );
3638 assert_eq!(
3639 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3640 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3641 "the pair in front of the bad one did not go in"
3642 );
3643 assert_eq!(
3646 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3647 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3648 );
3649 assert_eq!(
3650 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3651 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3652 );
3653 assert_eq!(
3656 f.run(&[
3657 b"CONFIG",
3658 b"SET",
3659 b"set-max-intset-entries",
3660 b"99999999999999999999"
3661 ]),
3662 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3663 );
3664 assert_eq!(
3665 f.run(&[
3666 b"CONFIG",
3667 b"SET",
3668 b"set-max-intset-entries",
3669 b"9223372036854775807"
3670 ]),
3671 "+OK\r\n"
3672 );
3673 }
3674
3675 #[test]
3676 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3677 let mut f = Fixture::new();
3678 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3679 f.run(&[b"SELECT", b"3"]);
3680 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3681 assert_eq!(
3682 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3683 "$9\r\nhashtable\r\n",
3684 "these are one server wide number in Redis, whatever a Keyspace carries"
3685 );
3686 }
3687
3688 #[test]
3689 fn info_reports_the_numbers_it_can_stand_behind() {
3690 let mut f = Fixture::new();
3691 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3692 let all = f.run(&[b"INFO"]);
3693 assert!(all.contains("redis_version:8.8.0"), "{all}");
3694 assert!(
3695 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3696 "{all}"
3697 );
3698 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3699 assert!(all.contains("role:master"), "{all}");
3700 let clients = f.run(&[b"INFO", b"clients"]);
3702 assert!(clients.contains("connected_clients:0"), "{clients}");
3703 assert!(!clients.contains("redis_version"), "{clients}");
3704 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3705 }
3706
3707 #[test]
3714 fn commandstats_is_asked_for_and_replication_is_not() {
3715 let mut f = Fixture::new();
3716 for arg in ["", "all", "default", "everything"] {
3717 let info = if arg.is_empty() {
3718 f.run(&[b"INFO"])
3719 } else {
3720 f.run(&[b"INFO", arg.as_bytes()])
3721 };
3722 assert!(info.contains("redis_version"), "{arg}: {info}");
3723 assert!(info.contains("used_cpu_user"), "{arg}: {info}");
3724 assert!(info.contains("used_memory"), "{arg}: {info}");
3725 assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
3726 let asked = arg == "all" || arg == "everything";
3727 assert_eq!(
3728 info.contains("rejected_calls"),
3729 asked,
3730 "{arg} should{} carry the command counters: {info}",
3731 if asked { "" } else { " not" }
3732 );
3733 }
3734
3735 let cpu = f.run(&[b"INFO", b"cpu"]);
3736 assert!(cpu.contains("used_cpu_user"), "{cpu}");
3737 assert!(!cpu.contains("used_memory"), "{cpu}");
3738
3739 let stats = f.run(&[b"INFO", b"commandSTATS"]);
3742 assert!(!stats.contains("used_memory"), "{stats}");
3743 assert!(stats.contains("rejected_calls"), "{stats}");
3744
3745 let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
3747 assert!(pair.contains("used_cpu_user"), "{pair}");
3748 assert!(!pair.contains("master_repl_offset"), "{pair}");
3749
3750 let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
3751 assert!(with_all.contains("used_memory"), "{with_all}");
3752 assert!(with_all.contains("master_repl_offset"), "{with_all}");
3753 assert!(with_all.contains("rejected_calls"), "{with_all}");
3754 assert_eq!(
3756 with_all.matches("used_cpu_user_children").count(),
3757 1,
3758 "{with_all}"
3759 );
3760
3761 let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
3762 assert!(with_default.contains("used_memory"), "{with_default}");
3763 assert!(
3764 with_default.contains("master_repl_offset"),
3765 "{with_default}"
3766 );
3767 assert!(!with_default.contains("rejected_calls"), "{with_default}");
3768 assert_eq!(
3769 with_default.matches("used_cpu_user_children").count(),
3770 1,
3771 "{with_default}"
3772 );
3773 }
3774
3775 #[test]
3784 fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
3785 let mut f = Fixture::new();
3786 let info = f.run(&[b"INFO", b"memory"]);
3787 for field in [
3788 "total_system_memory:",
3789 "mem_cgroup_limit:",
3790 "mem_limit:",
3791 "mem_budget:",
3792 ] {
3793 assert!(info.contains(field), "no {field} in {info}");
3794 }
3795
3796 let field = |name: &str| -> u64 {
3797 info.lines()
3798 .find_map(|l| l.strip_prefix(name))
3799 .unwrap_or_else(|| panic!("no {name} in {info}"))
3800 .trim()
3801 .parse()
3802 .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
3803 };
3804 let limit = field("mem_limit:");
3805 assert_eq!(field("mem_budget:"), limit / 4, "{info}");
3806 if limit != 0 {
3809 let host = field("total_system_memory:");
3810 let cgroup = field("mem_cgroup_limit:");
3811 assert!(
3812 limit == host || limit == cgroup,
3813 "the limit came from neither number: {info}"
3814 );
3815 }
3816 }
3817
3818 #[test]
3827 fn a_command_counts_what_it_did_separately_from_what_it_refused() {
3828 let mut f = Fixture::new();
3829 f.run(&[b"SET", b"k", b"v"]);
3830 f.run(&[b"SET", b"k", b"w"]);
3831 f.run(&[b"LPUSH", b"k", b"x"]);
3833 f.run(&[b"LPUSH", b"k"]);
3835
3836 let stats = f.run(&[b"INFO", b"commandstats"]);
3837 assert!(
3838 stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
3839 "{stats}"
3840 );
3841 assert!(
3842 stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
3843 "{stats}"
3844 );
3845 assert!(
3846 !stats.contains("cmdstat_zadd"),
3847 "a command nobody has sent has no row: {stats}"
3848 );
3849 }
3850
3851 #[test]
3855 fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3856 let mut f = Fixture::new();
3857 for i in 0..3_000u32 {
3858 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3859 }
3860 for i in 0..1_000u32 {
3861 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3862 }
3863 assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3864 f.advance(100);
3865 assert_eq!(
3866 f.run(&[b"DBSIZE"]),
3867 ":4000\r\n",
3868 "DBSIZE counts records and nothing has read past the dead ones yet"
3869 );
3870
3871 let mut spent = 0;
3873 for _ in 0..2_000 {
3874 spent += f.server.expire_step(4096);
3875 if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3876 break;
3877 }
3878 }
3879 assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3880 assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3881 for i in 0..1_000u32 {
3882 assert_eq!(
3883 f.run(&[b"GET", format!("k{i}").as_bytes()]),
3884 "$1\r\nv\r\n",
3885 "it took a key that had no deadline"
3886 );
3887 }
3888 }
3889
3890 #[test]
3891 fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3892 let mut f = Fixture::new();
3893 for i in 0..2_000u32 {
3894 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3895 }
3896 assert_eq!(f.server.expire_step(4096), 0);
3897 f.run(&[b"SELECT", b"3"]);
3899 f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3900 f.advance(100);
3901 for _ in 0..64 {
3902 f.server.expire_step(4096);
3903 }
3904 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3905 f.run(&[b"SELECT", b"0"]);
3906 assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3907 assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3908 }
3909
3910 #[test]
3913 fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3914 let mut f = Fixture::new();
3915 for i in 0..500u32 {
3916 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3917 }
3918 f.advance(100);
3919 let at = f.server.striped(0).now_ms();
3920 f.server.set_clock_ms(at);
3921 assert!(f.server.expire_slice(8) > 0, "the first one works");
3925 for _ in 0..1_000 {
3926 assert_eq!(
3927 f.server.expire_slice(8),
3928 0,
3929 "the millisecond has not moved and neither should this"
3930 );
3931 }
3932 assert!(
3933 f.server.striped(0).expires() > 400,
3934 "there is plenty left to take"
3935 );
3936 f.server.set_clock_ms(at + 1);
3937 assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3938 }
3939
3940 #[test]
3943 fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3944 let mut f = Fixture::new();
3945 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3946 assert!(
3947 f.run(&[b"INFO", b"keyspace"])
3948 .contains("db0:keys=3,expires=0"),
3949 "none of them has one yet"
3950 );
3951 f.run(&[b"EXPIRE", b"a", b"1000"]);
3952 f.run(&[b"EXPIRE", b"b", b"1000"]);
3953 let two = f.run(&[b"INFO", b"keyspace"]);
3954 assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3955 f.run(&[b"PERSIST", b"a"]);
3956 f.run(&[b"DEL", b"b"]);
3957 let none = f.run(&[b"INFO", b"keyspace"]);
3958 assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3959
3960 f.run(&[b"SELECT", b"1"]);
3962 f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3963 let both = f.run(&[b"INFO", b"keyspace"]);
3964 assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3965 assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3966 }
3967
3968 #[cfg(unix)]
3969 #[test]
3970 fn info_cpu_reports_processor_time_that_was_really_measured() {
3971 let mut f = Fixture::new();
3972 let cpu = f.run(&[b"INFO", b"cpu"]);
3973 assert!(cpu.contains("# CPU"), "{cpu}");
3974 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3976 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3977 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3978 assert!(!cpu.contains("redis_version"), "{cpu}");
3979
3980 let before = used_cpu_user(&cpu);
3984 let mut n = 0u64;
3985 let mut rounds = 0;
3986 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3987 for i in 0..1_000_000u64 {
3988 n = n.wrapping_add(i.wrapping_mul(i));
3989 }
3990 rounds += 1;
3991 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
3995 }
3996 }
3997
3998 #[cfg(unix)]
4000 fn used_cpu_user(info: &str) -> f64 {
4001 info.lines()
4002 .find_map(|l| l.strip_prefix("used_cpu_user:"))
4003 .expect("no used_cpu_user in the reply")
4004 .trim()
4005 .parse()
4006 .expect("used_cpu_user is not a number")
4007 }
4008
4009 #[test]
4015 fn a_command_that_fails_leaves_nothing_half_written() {
4016 let mut f = Fixture::new();
4017 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4018 assert_eq!(reply, "-ERR offset is out of range\r\n");
4019 assert!(!reply.contains(':'), "no integer went out in front of it");
4020 }
4021
4022 #[test]
4023 fn quit_answers_first_and_closes_after() {
4024 let mut f = Fixture::new();
4025 let (flow, reply) = f.flow(&[b"QUIT"]);
4026 assert_eq!(reply, "+OK\r\n");
4027 assert_eq!(flow, Flow::Close);
4028 }
4029
4030 #[test]
4037 fn shutdown_writes_nothing_and_sets_the_flag() {
4038 let mut f = Fixture::new();
4039 assert!(!f.server.stopping(), "nobody has asked yet");
4040
4041 let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4042 assert_eq!(reply, "");
4043 assert_eq!(flow, Flow::Close);
4044 assert!(f.server.stopping());
4045 }
4046
4047 #[test]
4055 fn shutdown_takes_the_flags_redis_takes() {
4056 for flags in [
4057 &[b"NOSAVE".as_slice()][..],
4058 &[b"SAVE"],
4059 &[b"NOW"],
4060 &[b"FORCE"],
4061 &[b"nosave"],
4062 &[b"NOW", b"NOW"],
4063 &[b"SAVE", b"SAVE"],
4064 &[b"NOSAVE", b"NOW", b"FORCE"],
4065 ] {
4066 let mut f = Fixture::new();
4067 let mut parts = vec![b"SHUTDOWN".as_slice()];
4068 parts.extend_from_slice(flags);
4069 let (flow, reply) = f.flow(&parts);
4070 assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4071 assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4072 assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4073 }
4074
4075 for flags in [
4076 &[b"BOGUS".as_slice()][..],
4077 &[b"SAVE", b"NOSAVE"],
4078 &[b"NOSAVE", b"SAVE"],
4079 &[b"ABORT", b"NOW"],
4080 &[b"NOSAVE", b"ABORT"],
4081 &[b"NOW", b"FORCE", b"ABORT"],
4082 ] {
4083 let mut f = Fixture::new();
4084 let mut parts = vec![b"SHUTDOWN".as_slice()];
4085 parts.extend_from_slice(flags);
4086 assert_eq!(
4087 f.run(&parts),
4088 "-ERR syntax error\r\n",
4089 "SHUTDOWN {flags:?} was accepted"
4090 );
4091 assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4092 }
4093 }
4094
4095 #[test]
4103 fn shutdown_abort_never_has_anything_to_abort() {
4104 let mut f = Fixture::new();
4105 for parts in [
4106 &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4107 &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4108 ] {
4109 assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4110 assert!(!f.server.stopping(), "an abort stopped the server");
4111 }
4112 }
4113
4114 struct Backups {
4122 f: Fixture,
4123 dir: PathBuf,
4124 }
4125
4126 impl Backups {
4127 fn new(name: &str) -> Backups {
4128 let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4129 let _ = std::fs::remove_dir_all(&dir);
4130 std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4131 let mut f = Fixture::new();
4132 f.server.set_dir(dir.clone());
4133 Backups { f, dir }
4134 }
4135
4136 fn run(&mut self, parts: &[&[u8]]) -> String {
4137 self.f.run(parts)
4138 }
4139
4140 fn files(&self) -> Vec<String> {
4142 let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4143 Ok(entries) => entries
4144 .filter_map(|e| e.ok())
4145 .map(|e| e.file_name().to_string_lossy().into_owned())
4146 .collect(),
4147 Err(_) => Vec::new(),
4148 };
4149 names.sort();
4150 names
4151 }
4152
4153 fn read(&self, name: &str) -> Vec<u8> {
4154 std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4155 }
4156 }
4157
4158 impl Drop for Backups {
4159 fn drop(&mut self) {
4160 let _ = std::fs::remove_dir_all(&self.dir);
4161 }
4162 }
4163
4164 #[test]
4167 fn backup_walks_the_states_the_reference_walks() {
4168 let mut b = Backups::new("states");
4169 let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4170
4171 assert!(status(&mut b).contains("idle"));
4172 assert!(b.files().is_empty(), "an idle server has written a backup");
4173
4174 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4175 assert!(status(&mut b).contains("incrementing"));
4176 assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4177
4178 assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4179 assert!(status(&mut b).contains("sealed"));
4180 assert_eq!(
4181 b.files(),
4182 [
4183 "appendonly.aof.1.base.rdb",
4184 "appendonly.aof.1.incr.aof",
4185 "appendonly.aof.manifest",
4186 ]
4187 );
4188
4189 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4190 assert!(status(&mut b).contains("idle"));
4191 assert!(b.files().is_empty(), "cleanup left something behind");
4192 }
4193
4194 #[test]
4196 fn backup_refuses_the_moves_the_reference_refuses() {
4197 let mut b = Backups::new("refusals");
4198
4199 assert_eq!(
4200 b.run(&[b"BACKUP", b"SEAL"]),
4201 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4202 );
4203 assert_eq!(
4204 b.run(&[b"BACKUP", b"ABORT"]),
4205 "-ERR No backup in progress\r\n"
4206 );
4207 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4210
4211 b.run(&[b"BACKUP", b"START"]);
4212 assert_eq!(
4213 b.run(&[b"BACKUP", b"START"]),
4214 "-ERR A backup is already in progress, ABORT it first\r\n"
4215 );
4216 assert_eq!(
4217 b.run(&[b"BACKUP", b"CLEANUP"]),
4218 "-ERR Backup is in progress\r\n"
4219 );
4220
4221 b.run(&[b"BACKUP", b"SEAL"]);
4222 assert_eq!(
4223 b.run(&[b"BACKUP", b"START"]),
4224 "-ERR A sealed backup exists, CLEANUP it first\r\n"
4225 );
4226 assert_eq!(
4227 b.run(&[b"BACKUP", b"SEAL"]),
4228 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4229 );
4230 assert_eq!(
4231 b.run(&[b"BACKUP", b"ABORT"]),
4232 "-ERR No backup in progress\r\n"
4233 );
4234 }
4235
4236 #[test]
4243 fn backup_abort_removes_the_file_and_says_who_did_it() {
4244 let mut b = Backups::new("abort");
4245 b.run(&[b"BACKUP", b"START"]);
4246 assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4247
4248 let status = b.run(&[b"BACKUP", b"STATUS"]);
4249 assert!(status.contains("failed"), "{status}");
4250 assert!(status.contains("aborted by user"), "{status}");
4251 assert!(b.files().is_empty(), "abort left the base file behind");
4252 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4253
4254 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4256 assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4257 let status = b.run(&[b"BACKUP", b"STATUS"]);
4258 assert!(status.contains("incrementing"), "{status}");
4259 assert!(!status.contains("aborted"), "the old error was kept");
4260 }
4261
4262 #[test]
4264 fn backup_list_names_the_files_that_are_pinned_so_far() {
4265 let mut b = Backups::new("list");
4266 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4267
4268 b.run(&[b"BACKUP", b"START"]);
4269 let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4270 let base = base.to_string_lossy().into_owned();
4271 assert_eq!(
4272 b.run(&[b"BACKUP", b"LIST"]),
4273 format!("*1\r\n${}\r\n{base}\r\n", base.len())
4274 );
4275
4276 b.run(&[b"BACKUP", b"SEAL"]);
4277 let listed = b.run(&[b"BACKUP", b"LIST"]);
4278 assert!(listed.starts_with("*3\r\n"), "{listed}");
4279 let names: Vec<&str> = listed
4282 .lines()
4283 .filter(|l| l.starts_with('/') || l.contains(":\\"))
4284 .collect();
4285 assert_eq!(names.len(), 3, "{listed}");
4286 assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4287 assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4288 assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4289 }
4290
4291 #[test]
4299 fn a_backup_holds_the_dataset_as_it_was_at_start() {
4300 let mut b = Backups::new("contents");
4301 b.run(&[b"SET", b"bk", b"v1"]);
4302 b.run(&[b"BACKUP", b"START"]);
4303 b.run(&[b"SET", b"bk", b"v2"]);
4304 b.run(&[b"BACKUP", b"SEAL"]);
4305
4306 let base = b.read("appendonly.aof.1.base.rdb");
4307 assert!(base.starts_with(b"REDIS"), "not an RDB file");
4308 assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4309 assert!(
4310 !base.windows(2).any(|w| w == b"v2"),
4311 "the base file moved on after START"
4312 );
4313 let at = base
4318 .windows(8)
4319 .position(|w| w == b"aof-base")
4320 .expect("no aof-base aux field");
4321 assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4322
4323 assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4324 assert_eq!(
4325 String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4326 "file appendonly.aof.1.base.rdb seq 1 type b\n\
4327 file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4328 );
4329 }
4330
4331 #[test]
4334 fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4335 let mut b = Backups::new("status");
4336 b.f.server.set_clock_ms(1_700_000_000_000);
4337
4338 assert_eq!(
4339 b.run(&[b"BACKUP", b"STATUS"]),
4340 "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4341 $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4342 );
4343
4344 b.f.out = Out::new(Proto::Resp3);
4345 b.run(&[b"BACKUP", b"START"]);
4346 assert_eq!(
4347 b.run(&[b"BACKUP", b"STATUS"]),
4348 "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4349 $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4350 );
4351
4352 b.run(&[b"BACKUP", b"SEAL"]);
4353 let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4354 assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4355 }
4356
4357 #[test]
4360 fn a_sealed_backup_is_swept_away_after_the_timeout() {
4361 let mut b = Backups::new("ttl");
4362 b.f.server.set_clock_ms(1_000_000);
4363 assert_eq!(
4364 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4365 "+OK\r\n"
4366 );
4367 b.run(&[b"BACKUP", b"START"]);
4368 b.run(&[b"BACKUP", b"SEAL"]);
4369
4370 b.f.server.set_clock_ms(1_000_000 + 59_000);
4372 b.f.server.backup_expire();
4373 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4374 assert_eq!(b.files().len(), 3);
4375
4376 b.f.server.set_clock_ms(1_000_000 + 60_000);
4377 b.f.server.backup_expire();
4378 let status = b.run(&[b"BACKUP", b"STATUS"]);
4379 assert!(status.contains("idle"), "{status}");
4380 assert!(b.files().is_empty(), "the timeout left the files behind");
4381
4382 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4384 b.run(&[b"BACKUP", b"START"]);
4385 b.run(&[b"BACKUP", b"SEAL"]);
4386 b.f.server.set_clock_ms(9_000_000_000);
4387 b.f.server.backup_expire();
4388 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4389 }
4390
4391 #[test]
4394 fn the_backup_settings_behave_the_way_the_reference_does() {
4395 let mut b = Backups::new("config");
4396 let dir = b.dir.to_string_lossy().into_owned();
4397
4398 assert_eq!(
4399 b.run(&[b"CONFIG", b"GET", b"dir"]),
4400 format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4401 );
4402 assert_eq!(
4403 b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4404 "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4405 );
4406 assert_eq!(
4407 b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4408 "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4409 );
4410
4411 assert_eq!(
4414 b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4415 "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4416 );
4417 assert_eq!(
4418 b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4419 "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4420 );
4421 assert!(
4422 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4423 .contains("argument couldn't be parsed into an integer")
4424 );
4425 assert!(
4426 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4427 .contains("argument must be between 0 and 9223372036854775807 inclusive")
4428 );
4429 }
4430
4431 #[test]
4433 fn backup_help_is_the_text_the_reference_sends() {
4434 let mut f = Fixture::new();
4435 let help = f.run(&[b"BACKUP", b"HELP"]);
4436 assert!(help.starts_with("*17\r\n"), "{help}");
4437 assert!(
4438 help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4439 );
4440 assert!(help.contains("+ Start a new backup into the configured 'backupdirname'.\r\n"));
4441 assert!(help.contains("+ Freeze the current backup (BASE + INCR + manifest).\r\n"));
4442 assert!(help.contains("+ Return this help.\r\n+HELP\r\n+ Print this help.\r\n"));
4443 }
4444
4445 #[test]
4453 fn backup_refuses_what_it_cannot_read() {
4454 let mut f = Fixture::new();
4455 assert_eq!(
4456 f.run(&[b"BACKUP"]),
4457 "-ERR wrong number of arguments for 'backup' command\r\n"
4458 );
4459 assert_eq!(
4460 f.run(&[b"BACKUP", b"START", b"x"]),
4461 "-ERR wrong number of arguments for 'backup' command\r\n"
4462 );
4463 assert_eq!(
4464 f.run(&[b"BACKUP", b"NOPE"]),
4465 "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4466 );
4467 }
4468
4469 #[test]
4470 fn the_command_counter_counts_every_command_including_the_bad_ones() {
4471 let mut f = Fixture::new();
4472 f.run(&[b"PING"]);
4473 f.run(&[b"NOPE"]);
4474 f.run(&[b"GET"]);
4475 assert_eq!(f.server.stats.commands, 3);
4476 }
4477
4478 #[test]
4479 fn a_set_goes_from_bytes_to_bytes() {
4480 let mut f = Fixture::new();
4481 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4482 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4483 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4484 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4485 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4486 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4487 assert_eq!(
4488 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4489 "*3\r\n:1\r\n:0\r\n:1\r\n"
4490 );
4491 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4492 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4493 }
4494
4495 #[test]
4496 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4497 let mut f = Fixture::new();
4498 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4499 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4500 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4501 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4502 assert_eq!(
4503 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4504 "*2\r\n:0\r\n:0\r\n"
4505 );
4506 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4507 }
4508
4509 #[test]
4510 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
4511 let mut f = Fixture::new();
4515 f.run(&[b"SADD", b"s", b"one"]);
4516 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
4517
4518 f.run(&[b"HELLO", b"3"]);
4519 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
4520 }
4521
4522 #[test]
4523 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
4524 let mut f = Fixture::new();
4527 f.run(&[b"SADD", b"s", b"42"]);
4528 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
4529 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
4530 assert_eq!(
4531 f.run(&[b"SISMEMBER", b"s", b"042"]),
4532 ":0\r\n",
4533 "the member is the bytes and not the number they parse to"
4534 );
4535 }
4536
4537 #[test]
4538 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
4539 let mut f = Fixture::new();
4540 f.run(&[b"SET", b"str", b"v"]);
4541 f.run(&[b"SADD", b"set", b"a"]);
4542
4543 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4544 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
4545 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
4546 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
4547 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
4548 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
4549 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
4550 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
4551 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
4552
4553 assert_eq!(
4556 f.run(&[b"MGET", b"str", b"set", b"nope"]),
4557 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
4558 );
4559 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
4561 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
4562 }
4563
4564 #[test]
4565 fn a_wrongtype_leaves_nothing_half_written() {
4566 let mut f = Fixture::new();
4570 f.run(&[b"SET", b"k", b"v"]);
4571 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
4572 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
4573 assert!(!reply.contains('*'), "an array header went out in front");
4574 }
4575
4576 #[test]
4577 fn emptying_a_set_takes_the_key_with_it() {
4578 let mut f = Fixture::new();
4579 f.run(&[b"SADD", b"s", b"a", b"b"]);
4580 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4581 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
4582 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4583 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
4584 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4585 }
4586
4587 fn split_scan(reply: &str) -> (String, Vec<String>) {
4593 let mut lines = reply.split("\r\n");
4594 assert_eq!(lines.next(), Some("*2"), "got {reply}");
4595 lines.next().expect("the cursor header");
4596 let cursor = lines.next().expect("the cursor").to_owned();
4597 let header = lines.next().expect("the member header");
4598 let n: usize = header[1..].parse().expect("a member count");
4599 let mut members = Vec::with_capacity(n);
4600 for _ in 0..n {
4601 lines.next().expect("a member header");
4602 members.push(lines.next().expect("a member").to_owned());
4603 }
4604 (cursor, members)
4605 }
4606
4607 #[test]
4608 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
4609 let mut f = Fixture::new();
4610 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
4611
4612 let one = f.run(&[b"SPOP", b"s"]);
4613 assert!(
4614 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
4615 "got {one}"
4616 );
4617 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4618
4619 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
4621 assert!(rest.starts_with("*3\r\n"), "got {rest}");
4622 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4623 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
4625 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
4626 }
4627
4628 #[test]
4629 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
4630 let mut f = Fixture::new();
4635 f.run(&[b"HELLO", b"3"]);
4636 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
4637
4638 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
4639 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
4641
4642 f.run(&[b"SADD", b"one", b"z"]);
4646 assert_eq!(
4647 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
4648 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
4649 );
4650 }
4651
4652 #[test]
4653 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
4654 let mut f = Fixture::new();
4655 f.run(&[b"SADD", b"s", b"only"]);
4656 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4657 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4658 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
4659
4660 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
4661 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
4664 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
4665 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
4667 }
4668
4669 #[test]
4670 fn a_pop_count_that_is_not_a_positive_number_says_so() {
4671 let mut f = Fixture::new();
4672 f.run(&[b"SADD", b"s", b"a"]);
4673 let bad = "-ERR value is out of range, must be positive\r\n";
4674 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
4675 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
4676 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
4677 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
4679 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
4680 }
4681
4682 #[test]
4683 fn a_scan_walks_a_set_of_any_size_exactly_once() {
4684 let mut f = Fixture::new();
4685 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
4686 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
4687 .into_iter()
4688 .chain(members.iter().map(Vec::as_slice))
4689 .collect();
4690 f.run(&args);
4691
4692 let mut seen = Vec::new();
4693 let mut cursor = "0".to_owned();
4694 loop {
4695 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
4696 let (next, got) = split_scan(&reply);
4697 seen.extend(got);
4698 cursor = next;
4699 if cursor == "0" {
4700 break;
4701 }
4702 }
4703 seen.sort();
4704 seen.dedup();
4705 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
4706
4707 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
4710 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
4711 assert_eq!(cursor, "0");
4712 assert_eq!(got.len(), 3);
4713 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
4715 }
4716
4717 #[test]
4718 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
4719 let mut f = Fixture::new();
4720 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
4721
4722 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
4723 let mut got = got;
4724 got.sort();
4725 assert_eq!(got, ["aa", "ab"]);
4726
4727 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
4730 let mut got = got;
4731 got.sort();
4732 assert_eq!(got, ["12", "13"]);
4733
4734 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
4735 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
4736 assert_eq!(
4737 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
4738 "-ERR syntax error\r\n"
4739 );
4740 assert_eq!(
4743 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
4744 "-ERR syntax error\r\n"
4745 );
4746 }
4747
4748 #[test]
4749 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
4750 let mut f = Fixture::new();
4751 f.run(&[b"SADD", b"src", b"a", b"b"]);
4752 f.run(&[b"SADD", b"dst", b"c"]);
4753
4754 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
4755 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
4756 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
4757 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
4759 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
4760
4761 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
4764 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
4765 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
4766 }
4767
4768 #[test]
4769 fn moving_checks_the_types_in_the_order_redis_checks_them() {
4770 let mut f = Fixture::new();
4774 f.run(&[b"SET", b"str", b"v"]);
4775 f.run(&[b"SADD", b"set", b"a"]);
4776
4777 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4778 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
4779 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
4780 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
4781 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
4782 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
4783 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
4784 assert_eq!(
4785 f.run(&[b"SISMEMBER", b"set", b"a"]),
4786 ":1\r\n",
4787 "and none of that moved anything"
4788 );
4789 }
4790
4791 #[test]
4792 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4793 let mut f = Fixture::new();
4796 f.run(&[b"SADD", b"s", b"a"]);
4797 for bad in [
4798 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
4799 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
4800 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
4801 ] {
4802 let reply = f.run(bad);
4803 assert!(reply.starts_with("-ERR"), "got {reply}");
4804 assert!(!reply.contains('*'), "an array header went out in front");
4805 }
4806 }
4807
4808 #[test]
4809 fn a_hash_writes_reads_and_deletes_its_fields() {
4810 let mut f = Fixture::new();
4811 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
4812 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
4813 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4814 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
4815 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
4816 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
4817 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
4818 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
4819 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
4820 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
4821
4822 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
4825
4826 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
4827 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
4828 assert_eq!(
4829 f.run(&[b"EXISTS", b"h"]),
4830 ":0\r\n",
4831 "and losing the last field lost the key"
4832 );
4833 }
4834
4835 #[test]
4836 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
4837 let mut f = Fixture::new();
4838 f.run(&[b"HSET", b"h", b"a", b"1"]);
4839 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
4840 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
4841 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
4842 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
4843 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
4844
4845 f.run(&[b"HELLO", b"3"]);
4846 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
4847 assert_eq!(
4848 f.run(&[b"HGETALL", b"nokey"]),
4849 "%0\r\n",
4850 "a missing key is the empty hash and never a nil"
4851 );
4852 assert_eq!(
4853 f.run(&[b"HKEYS", b"h"]),
4854 "*1\r\n$1\r\na\r\n",
4855 "and the two that answer one side stay arrays"
4856 );
4857 }
4858
4859 #[test]
4860 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
4861 let mut f = Fixture::new();
4862 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
4863 assert_eq!(
4864 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
4865 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
4866 "the reply is positional, so b is a nil and not a gap"
4867 );
4868 assert_eq!(
4869 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
4870 "*2\r\n$-1\r\n$-1\r\n",
4871 "and a missing key is all nils rather than an empty array"
4872 );
4873
4874 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
4875 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
4876 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4877 }
4878
4879 #[test]
4880 fn a_hash_counts_up_and_says_so_when_it_cannot() {
4881 let mut f = Fixture::new();
4882 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
4883 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
4884 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
4885 assert_eq!(
4886 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
4887 "$4\r\n10.5\r\n",
4888 "a bulk string and not a double, on both protocols"
4889 );
4890
4891 f.run(&[b"HSET", b"h", b"s", b"words"]);
4892 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
4893 assert!(
4894 bad.starts_with("-ERR hash value is not an integer"),
4895 "{bad}"
4896 );
4897 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
4898 assert!(
4899 bad.starts_with("-ERR value is not an integer"),
4900 "a bad argument is not yet a hash value, {bad}"
4901 );
4902 assert_eq!(
4903 f.run(&[b"HGET", b"h", b"s"]),
4904 "$5\r\nwords\r\n",
4905 "and neither of them wrote anything"
4906 );
4907 }
4908
4909 #[test]
4910 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
4911 let mut f = Fixture::new();
4912 for i in 0..500 {
4913 let field = format!("field-{i}");
4914 let value = format!("value-{i}");
4915 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
4916 }
4917
4918 let mut seen: Vec<String> = Vec::new();
4919 let mut cursor = "0".to_owned();
4920 loop {
4921 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
4922 let (next, items) = scan_reply(&reply);
4923 assert_eq!(items.len() % 2, 0, "a pair went out half written");
4924 for pair in items.chunks(2) {
4925 assert_eq!(
4926 pair[0].strip_prefix("field-"),
4927 pair[1].strip_prefix("value-"),
4928 "a field came back with someone else's value"
4929 );
4930 seen.push(pair[0].clone());
4931 }
4932 cursor = next;
4933 if cursor == "0" {
4934 break;
4935 }
4936 }
4937 seen.sort();
4938 seen.dedup();
4939 assert_eq!(seen.len(), 500, "every field once and only once");
4940
4941 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
4942 assert!(
4943 items.iter().all(|s| s.starts_with("field-")),
4944 "NOVALUES still sent the values"
4945 );
4946
4947 let (_, one) = scan_reply(&f.run(&[
4948 b"HSCAN",
4949 b"h",
4950 b"0",
4951 b"MATCH",
4952 b"field-499",
4953 b"COUNT",
4954 b"1000",
4955 ]));
4956 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
4957 }
4958
4959 #[test]
4960 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
4961 let mut f = Fixture::new();
4962 f.run(&[b"HSET", b"h", b"a", b"1"]);
4963 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
4964 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
4965 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
4966 assert_eq!(
4967 f.run(&[b"HRANDFIELD", b"h", b"3"]),
4968 "*1\r\n$1\r\na\r\n",
4969 "a positive count is capped at the size of the hash"
4970 );
4971 assert_eq!(
4972 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
4973 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
4974 "and a negative one repeats itself"
4975 );
4976 assert_eq!(
4977 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4978 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4979 "flat on RESP2"
4980 );
4981
4982 f.run(&[b"HELLO", b"3"]);
4983 assert_eq!(
4984 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4985 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4986 "and nested on RESP3, but still an array and never a map"
4987 );
4988 }
4989
4990 #[test]
4991 fn every_hash_command_says_wrongtype_and_writes_nothing() {
4992 let mut f = Fixture::new();
4993 f.run(&[b"SET", b"str", b"v"]);
4994 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4995
4996 for cmd in [
4997 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
4998 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
4999 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5000 &[b"HGET".as_slice(), b"str", b"f"][..],
5001 &[b"HMGET".as_slice(), b"str", b"f"][..],
5002 &[b"HDEL".as_slice(), b"str", b"f"][..],
5003 &[b"HLEN".as_slice(), b"str"][..],
5004 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5005 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5006 &[b"HGETALL".as_slice(), b"str"][..],
5007 &[b"HKEYS".as_slice(), b"str"][..],
5008 &[b"HVALS".as_slice(), b"str"][..],
5009 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5010 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5011 &[b"HRANDFIELD".as_slice(), b"str"][..],
5012 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5013 &[b"HSCAN".as_slice(), b"str", b"0"][..],
5014 ] {
5015 let reply = f.run(cmd);
5016 assert_eq!(reply, wrong, "{:?}", cmd[0]);
5017 }
5018 assert_eq!(
5019 f.run(&[b"GET", b"str"]),
5020 "$1\r\nv\r\n",
5021 "and none of them touched the value"
5022 );
5023 }
5024
5025 #[test]
5026 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5027 let mut f = Fixture::new();
5028 f.run(&[b"HSET", b"h", b"f", b"v"]);
5029 for bad in [
5030 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5031 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5032 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5033 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5034 ] {
5035 let reply = f.run(bad);
5036 assert!(reply.starts_with("-ERR"), "got {reply}");
5037 assert!(!reply.contains('*'), "an array header went out in front");
5038 }
5039 }
5040
5041 #[test]
5042 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5043 let mut f = Fixture::new();
5044 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5045 assert_eq!(
5046 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5047 "*1\r\n:1\r\n"
5048 );
5049 assert_eq!(
5050 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5051 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5052 "one answer per field, and the two sentinels are TTL's own"
5053 );
5054
5055 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5058 assert!((99_000..=100_000).contains(&ms), "got {ms}");
5059 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5060 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5061 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5062 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5063
5064 assert_eq!(
5065 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5066 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5067 "one for the deadline taken off, and it does not say what it was"
5068 );
5069 assert_eq!(
5070 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5071 "*1\r\n:-1\r\n"
5072 );
5073 assert_eq!(
5074 f.run(&[b"HGET", b"h", b"a"]),
5075 "$1\r\n1\r\n",
5076 "and the field is still there with the value it had"
5077 );
5078 }
5079
5080 #[test]
5081 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5082 let mut f = Fixture::new();
5083 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5084 assert_eq!(
5085 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5086 "*1\r\n:2\r\n",
5087 "two, and not one, because nothing was stored"
5088 );
5089 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5090 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5091
5092 assert_eq!(
5093 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5094 "*1\r\n:2\r\n"
5095 );
5096 assert_eq!(
5097 f.run(&[b"EXISTS", b"h"]),
5098 ":0\r\n",
5099 "and the last field going took the key with it"
5100 );
5101
5102 f.run(&[b"HSET", b"h", b"a", b"1"]);
5105 assert_eq!(
5106 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5107 "*1\r\n:2\r\n"
5108 );
5109 }
5110
5111 #[test]
5112 fn a_field_is_gone_once_its_moment_passes() {
5113 let mut f = Fixture::new();
5114 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5115 assert_eq!(
5116 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5117 "*1\r\n:1\r\n"
5118 );
5119 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5120
5121 f.server.advance_clock_ms(60);
5125 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5126 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5127 assert_eq!(
5128 f.run(&[b"HGETALL", b"h"]),
5129 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5130 "and the walks do not hand back a field that has expired"
5131 );
5132 }
5133
5134 #[test]
5135 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5136 let mut f = Fixture::new();
5137 for cmd in [
5138 &[
5139 b"HEXPIRE".as_slice(),
5140 b"nokey",
5141 b"100",
5142 b"FIELDS",
5143 b"2",
5144 b"a",
5145 b"b",
5146 ][..],
5147 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5148 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5149 &[
5150 b"HEXPIRETIME".as_slice(),
5151 b"nokey",
5152 b"FIELDS",
5153 b"2",
5154 b"a",
5155 b"b",
5156 ][..],
5157 &[
5158 b"HPERSIST".as_slice(),
5159 b"nokey",
5160 b"FIELDS",
5161 b"2",
5162 b"a",
5163 b"b",
5164 ][..],
5165 ] {
5166 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5167 }
5168 }
5169
5170 #[test]
5171 fn writing_a_field_clears_the_deadline_that_was_on_it() {
5172 let mut f = Fixture::new();
5173 f.run(&[b"HSET", b"h", b"a", b"1"]);
5174 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5175 f.run(&[b"HSET", b"h", b"a", b"2"]);
5176 assert_eq!(
5177 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5178 "*1\r\n:-1\r\n",
5179 "Redis has done this since 7.4, and it is why HGETEX exists"
5180 );
5181 }
5182
5183 #[test]
5184 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5185 let mut f = Fixture::new();
5186 f.run(&[b"HSET", b"h", b"a", b"1"]);
5187 assert_eq!(
5188 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5189 "*1\r\n:0\r\n",
5190 "XX on a field with no deadline changes nothing"
5191 );
5192 assert_eq!(
5193 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5194 "*1\r\n:1\r\n"
5195 );
5196 assert_eq!(
5197 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5198 "*1\r\n:0\r\n",
5199 "and NX will not move one that is already there"
5200 );
5201 assert_eq!(
5202 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5203 "*1\r\n:0\r\n"
5204 );
5205 assert_eq!(
5206 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5207 "*1\r\n:1\r\n"
5208 );
5209 assert_eq!(
5210 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5211 "*1\r\n:1\r\n"
5212 );
5213 assert_eq!(
5214 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5215 "*1\r\n:50\r\n"
5216 );
5217 }
5218
5219 #[test]
5220 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5221 let mut f = Fixture::new();
5222 f.run(&[b"HSET", b"h", b"a", b"1"]);
5223 for (bad, want) in [
5224 (
5225 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5226 "-ERR invalid expire time, must be >= 0",
5227 ),
5228 (
5229 &[
5230 b"HEXPIRE".as_slice(),
5231 b"h",
5232 b"9999999999999999",
5233 b"FIELDS",
5234 b"1",
5235 b"a",
5236 ][..],
5237 "-ERR invalid expire time in 'hexpire' command",
5238 ),
5239 (
5240 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5241 "-ERR wrong number of arguments for 'hexpire' command",
5242 ),
5243 (
5244 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5245 "-ERR Parameter `numFields` should be greater than 0",
5246 ),
5247 (
5248 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5249 "-ERR wrong number of arguments",
5250 ),
5251 (
5252 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5253 "-ERR wrong number of arguments",
5254 ),
5255 ] {
5256 let reply = f.run(bad);
5257 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5258 assert!(!reply.contains('*'), "an array header went out in front");
5259 }
5260 assert_eq!(
5261 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5262 "*1\r\n:-1\r\n",
5263 "and not one of them put a deadline on anything"
5264 );
5265 }
5266
5267 #[test]
5268 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5269 let mut f = Fixture::new();
5270 f.run(&[b"SET", b"str", b"v"]);
5271 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5272
5273 for cmd in [
5274 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5275 &[
5276 b"HPEXPIRE".as_slice(),
5277 b"str",
5278 b"100",
5279 b"FIELDS",
5280 b"1",
5281 b"f",
5282 ][..],
5283 &[
5284 b"HEXPIREAT".as_slice(),
5285 b"str",
5286 b"9999999999",
5287 b"FIELDS",
5288 b"1",
5289 b"f",
5290 ][..],
5291 &[
5292 b"HPEXPIREAT".as_slice(),
5293 b"str",
5294 b"9999999999999",
5295 b"FIELDS",
5296 b"1",
5297 b"f",
5298 ][..],
5299 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5300 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5301 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5302 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5303 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5304 ] {
5305 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5306 }
5307 assert_eq!(
5308 f.run(&[b"GET", b"str"]),
5309 "$1\r\nv\r\n",
5310 "and none of them touched the value"
5311 );
5312 }
5313
5314 #[test]
5315 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5316 let mut f = Fixture::new();
5317 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5318 assert_eq!(
5319 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5320 "*2\r\n$1\r\n1\r\n$-1\r\n",
5321 "positional, so the field that was not there is a nil in its place"
5322 );
5323 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5324 assert_eq!(
5325 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5326 "*1\r\n$-1\r\n"
5327 );
5328 assert_eq!(
5329 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5330 "*1\r\n$1\r\n2\r\n"
5331 );
5332 assert_eq!(
5333 f.run(&[b"EXISTS", b"h"]),
5334 ":0\r\n",
5335 "and the last field took the key"
5336 );
5337 }
5338
5339 #[test]
5340 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5341 let mut f = Fixture::new();
5342 f.run(&[b"HSET", b"h", b"a", b"1"]);
5343 assert_eq!(
5344 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5345 "*1\r\n$1\r\n1\r\n"
5346 );
5347 assert_eq!(
5348 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5349 "*1\r\n:-1\r\n",
5350 "no option means leave it alone, which is the one place this is not GETEX"
5351 );
5352
5353 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5354 assert_eq!(
5355 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5356 "*1\r\n:100\r\n"
5357 );
5358 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5359 assert_eq!(
5360 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5361 "*1\r\n:100\r\n",
5362 "and a plain read really does leave it alone"
5363 );
5364 assert_eq!(
5365 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5366 "*1\r\n$1\r\n1\r\n"
5367 );
5368 assert_eq!(
5369 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5370 "*1\r\n:-1\r\n"
5371 );
5372
5373 assert_eq!(
5374 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5375 "*1\r\n$1\r\n1\r\n",
5376 "the value goes out before the deadline that has already gone is applied"
5377 );
5378 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5379 assert_eq!(
5380 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5381 "*1\r\n$-1\r\n"
5382 );
5383 }
5384
5385 #[test]
5386 fn hsetex_writes_all_of_it_or_none_of_it() {
5387 let mut f = Fixture::new();
5388 assert_eq!(
5389 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5390 ":1\r\n"
5391 );
5392 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5393 assert_eq!(
5394 f.run(&[
5395 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5396 ]),
5397 ":0\r\n",
5398 "FNX wants every field named to be missing"
5399 );
5400 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5401 assert_eq!(
5402 f.run(&[b"HEXISTS", b"h", b"new"]),
5403 ":0\r\n",
5404 "and none of the list was written"
5405 );
5406 assert_eq!(
5407 f.run(&[
5408 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5409 ]),
5410 ":0\r\n",
5411 "and FXX wants every one of them to be there"
5412 );
5413 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5414 assert_eq!(
5415 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5416 ":1\r\n"
5417 );
5418 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5419
5420 assert_eq!(
5421 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5422 ":0\r\n"
5423 );
5424 assert_eq!(
5425 f.run(&[b"EXISTS", b"gone"]),
5426 ":0\r\n",
5427 "a key with no fields cannot meet FXX and is not created trying"
5428 );
5429 }
5430
5431 #[test]
5432 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5433 let mut f = Fixture::new();
5434 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5435 assert_eq!(
5436 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5437 "*1\r\n:100\r\n"
5438 );
5439
5440 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5441 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5442 assert_eq!(
5443 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5444 "*1\r\n:100\r\n",
5445 "KEEPTTL put back what the write cleared"
5446 );
5447
5448 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5449 assert_eq!(
5450 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5451 "*1\r\n:-1\r\n",
5452 "and without it a write clears the deadline the way HSET does"
5453 );
5454
5455 assert_eq!(
5458 f.run(&[
5459 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5460 ]),
5461 ":1\r\n"
5462 );
5463 assert_eq!(
5464 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5465 "*1\r\n:100\r\n"
5466 );
5467
5468 assert_eq!(
5469 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5470 ":1\r\n",
5471 "written, and not the separate code the HEXPIRE family has for this"
5472 );
5473 assert_eq!(
5474 f.run(&[b"EXISTS", b"h"]),
5475 ":0\r\n",
5476 "and storing it and then removing it emptied the hash"
5477 );
5478 }
5479
5480 #[test]
5481 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5482 let mut f = Fixture::new();
5483 f.run(&[b"HSET", b"h", b"a", b"1"]);
5484 for (bad, want) in [
5485 (
5487 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5488 "-ERR Number of fields must be a positive integer",
5489 ),
5490 (
5491 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5492 "-ERR The `numfields` parameter must match the number of arguments",
5493 ),
5494 (
5495 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5496 "-ERR Mandatory argument FIELDS is missing or not at the right position",
5497 ),
5498 (
5500 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5501 "-ERR invalid number of fields",
5502 ),
5503 (
5504 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5505 "-ERR wrong number of arguments",
5506 ),
5507 (
5508 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5509 "-ERR unknown argument: FIELD",
5510 ),
5511 (
5512 &[
5513 b"HGETEX".as_slice(),
5514 b"h",
5515 b"KEEPTTL",
5516 b"FIELDS",
5517 b"1",
5518 b"a",
5519 ][..],
5520 "-ERR unknown argument: KEEPTTL",
5521 ),
5522 (
5523 &[
5524 b"HGETEX".as_slice(),
5525 b"h",
5526 b"EX",
5527 b"100",
5528 b"PERSIST",
5529 b"FIELDS",
5530 b"1",
5531 b"a",
5532 ][..],
5533 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
5534 ),
5535 (
5536 &[
5537 b"HSETEX".as_slice(),
5538 b"h",
5539 b"EX",
5540 b"1",
5541 b"KEEPTTL",
5542 b"FIELDS",
5543 b"1",
5544 b"a",
5545 b"1",
5546 ][..],
5547 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
5548 ),
5549 (
5550 &[
5551 b"HSETEX".as_slice(),
5552 b"h",
5553 b"FNX",
5554 b"FXX",
5555 b"FIELDS",
5556 b"1",
5557 b"a",
5558 b"1",
5559 ][..],
5560 "-ERR Only one of FXX or FNX arguments can be specified",
5561 ),
5562 (
5563 &[
5564 b"HSETEX".as_slice(),
5565 b"h",
5566 b"FIELDS",
5567 b"2",
5568 b"a",
5569 b"1",
5570 b"b",
5571 ][..],
5572 "-ERR wrong number of arguments",
5573 ),
5574 (
5575 &[
5576 b"HGETEX".as_slice(),
5577 b"h",
5578 b"EX",
5579 b"-1",
5580 b"FIELDS",
5581 b"1",
5582 b"a",
5583 ][..],
5584 "-ERR invalid expire time, must be >= 0",
5585 ),
5586 (
5587 &[
5588 b"HGETEX".as_slice(),
5589 b"h",
5590 b"PXAT",
5591 b"99999999999999",
5592 b"FIELDS",
5593 b"1",
5594 b"a",
5595 ][..],
5596 "-ERR invalid expire time in 'hgetex' command",
5597 ),
5598 (
5599 &[
5600 b"HSETEX".as_slice(),
5601 b"h",
5602 b"EX",
5603 b"abc",
5604 b"FIELDS",
5605 b"1",
5606 b"a",
5607 b"1",
5608 ][..],
5609 "-ERR value is not an integer or out of range",
5610 ),
5611 ] {
5612 let reply = f.run(bad);
5613 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5614 assert!(!reply.contains('*'), "an array header went out in front");
5615 }
5616 assert_eq!(
5617 f.run(&[b"HGET", b"h", b"a"]),
5618 "$1\r\n1\r\n",
5619 "and not one of them wrote anything"
5620 );
5621 assert_eq!(
5622 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5623 "*1\r\n:-1\r\n"
5624 );
5625 }
5626
5627 #[test]
5628 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
5629 let mut f = Fixture::new();
5630 f.run(&[b"SET", b"str", b"v"]);
5631 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5632 for cmd in [
5633 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5634 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5635 &[
5636 b"HGETEX".as_slice(),
5637 b"str",
5638 b"EX",
5639 b"100",
5640 b"FIELDS",
5641 b"1",
5642 b"f",
5643 ][..],
5644 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
5645 ] {
5646 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5647 }
5648 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5649 }
5650
5651 #[test]
5659 fn himport_writes_declared_values_into_sorted_fields() {
5660 let mut f = Fixture::new();
5661 assert_eq!(
5662 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
5663 "+OK\r\n"
5664 );
5665 assert_eq!(
5666 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
5667 "+OK\r\n"
5668 );
5669 assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
5670 assert_eq!(
5671 f.run(&[b"HGETALL", b"k"]),
5672 bulks(&["a", "3", "b", "1", "aa", "2"])
5673 );
5674 }
5675
5676 #[test]
5679 fn himport_set_replaces_the_whole_key() {
5680 let mut f = Fixture::new();
5681 f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
5682 f.run(&[b"EXPIRE", b"k", b"100"]);
5683 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5684 assert_eq!(
5685 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5686 "+OK\r\n"
5687 );
5688 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5689 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
5690 }
5691
5692 #[test]
5695 fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
5696 let mut f = Fixture::new();
5697 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
5698 f.run(&[b"SELECT", b"1"]);
5699 assert_eq!(
5700 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5701 "+OK\r\n"
5702 );
5703 f.run(&[b"SELECT", b"0"]);
5704 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
5705 assert_eq!(
5706 f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
5707 "-ERR no such fieldset\r\n"
5708 );
5709 }
5710
5711 #[test]
5718 fn himport_complains_in_the_order_a_real_server_does() {
5719 let mut f = Fixture::new();
5720 f.run(&[b"SET", b"str", b"v"]);
5721 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5722 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5723 assert_eq!(
5724 f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
5725 wrong,
5726 "the type beats a missing fieldset"
5727 );
5728 assert_eq!(
5729 f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
5730 wrong,
5731 "and it beats a value count that does not fit"
5732 );
5733 assert_eq!(
5734 f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
5735 "-ERR no such fieldset\r\n"
5736 );
5737 for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
5739 let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
5740 line.extend_from_slice(values);
5741 assert_eq!(
5742 f.run(&line),
5743 "-ERR value count does not match fieldset field count\r\n",
5744 "{} values into two fields",
5745 values.len()
5746 );
5747 }
5748 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5749 }
5750
5751 #[test]
5753 fn himport_checks_each_subcommand_count_under_its_own_name() {
5754 let mut f = Fixture::new();
5755 assert_eq!(
5756 f.run(&[b"HIMPORT"]),
5757 "-ERR wrong number of arguments for 'himport' command\r\n"
5758 );
5759 for (rest, name) in [
5760 (&["PREPARE"][..], "prepare"),
5761 (&["PREPARE", "fs"][..], "prepare"),
5762 (&["SET"][..], "set"),
5763 (&["SET", "k"][..], "set"),
5764 (&["SET", "k", "fs"][..], "set"),
5765 (&["DISCARD"][..], "discard"),
5766 (&["DISCARD", "a", "b"][..], "discard"),
5767 (&["DISCARDALL", "x"][..], "discardall"),
5768 ] {
5769 let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
5770 line.extend(rest.iter().map(|a| a.as_bytes()));
5771 assert_eq!(
5772 f.run(&line),
5773 format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
5774 "HIMPORT {}",
5775 rest.join(" ")
5776 );
5777 }
5778 assert_eq!(
5779 f.run(&[b"HIMPORT", b"NOPE", b"x"]),
5780 "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
5781 );
5782 }
5783
5784 #[test]
5787 fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
5788 let mut f = Fixture::new();
5789 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5790 assert_eq!(
5791 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
5792 "-ERR duplicate field name in fieldset\r\n"
5793 );
5794 assert_eq!(
5795 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5796 "+OK\r\n"
5797 );
5798 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5799 }
5800
5801 #[test]
5804 fn himport_prepare_replaces_and_the_discards_count() {
5805 let mut f = Fixture::new();
5806 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5807 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
5808 assert_eq!(
5809 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5810 "+OK\r\n"
5811 );
5812 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
5813
5814 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
5815 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
5816 f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
5817 f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
5818 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
5819 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
5820 }
5821
5822 fn int(reply: &str) -> i64 {
5828 let body = reply
5829 .strip_prefix(':')
5830 .and_then(|s| s.strip_suffix("\r\n"))
5831 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
5832 body.parse().expect("an integer")
5833 }
5834
5835 fn int_reply(reply: &str) -> i64 {
5836 let body = reply
5837 .strip_prefix("*1\r\n:")
5838 .and_then(|s| s.strip_suffix("\r\n"))
5839 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
5840 body.parse().expect("an integer")
5841 }
5842
5843 fn scan_reply(reply: &str) -> (String, Vec<String>) {
5845 let mut lines = reply.split("\r\n");
5846 assert_eq!(lines.next(), Some("*2"), "got {reply}");
5847 lines.next().expect("the cursor header");
5848 let cursor = lines.next().expect("a cursor").to_owned();
5849 let header = lines.next().expect("an item count");
5850 let n: usize = header[1..].parse().expect("a count");
5851 let mut items = Vec::with_capacity(n);
5852 for _ in 0..n {
5853 lines.next().expect("an item header");
5854 items.push(lines.next().expect("an item").to_owned());
5855 }
5856 (cursor, items)
5857 }
5858
5859 fn sorted(reply: &str) -> Vec<String> {
5862 let mut lines = reply.split("\r\n");
5863 let header = lines.next().expect("a header");
5864 assert!(
5865 header.starts_with('*') || header.starts_with('~'),
5866 "got {reply}"
5867 );
5868 let n: usize = header[1..].parse().expect("a member count");
5869 let mut got = Vec::with_capacity(n);
5870 for _ in 0..n {
5871 lines.next().expect("a member header");
5872 got.push(lines.next().expect("a member").to_owned());
5873 }
5874 got.sort();
5875 got
5876 }
5877
5878 #[test]
5879 fn the_algebra_answers_what_the_sets_share_and_do_not() {
5880 let mut f = Fixture::new();
5881 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5882 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5883 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
5884
5885 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
5886 assert_eq!(
5887 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
5888 ["1", "2", "3", "4", "5"]
5889 );
5890 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
5891 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
5892
5893 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
5896 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
5897 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
5898 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
5899 }
5900
5901 #[test]
5902 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
5903 let mut f = Fixture::new();
5904 f.run(&[b"SADD", b"a", b"x"]);
5905 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
5906 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
5907 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
5908
5909 f.run(&[b"HELLO", b"3"]);
5910 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
5911 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
5912 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
5913 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
5914 }
5915
5916 #[test]
5917 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
5918 let mut f = Fixture::new();
5919 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5920 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5921
5922 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
5923 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
5924 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
5925 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
5926 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
5927 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
5928
5929 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
5932 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5933 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
5934 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
5935
5936 f.run(&[b"SET", b"str", b"v"]);
5939 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
5940 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
5941 }
5942
5943 #[test]
5944 fn sintercard_counts_without_building_and_stops_at_a_limit() {
5945 let mut f = Fixture::new();
5946 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5947 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
5948
5949 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
5950 assert_eq!(
5951 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5952 ":2\r\n"
5953 );
5954 assert_eq!(
5955 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5956 ":3\r\n",
5957 "a limit of zero is no limit"
5958 );
5959 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
5960 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
5961
5962 assert_eq!(
5964 f.run(&[b"SINTERCARD", b"0", b"a"]),
5965 "-ERR numkeys should be greater than 0\r\n"
5966 );
5967 assert_eq!(
5968 f.run(&[b"SINTERCARD", b"abc", b"a"]),
5969 "-ERR numkeys should be greater than 0\r\n"
5970 );
5971 assert_eq!(
5972 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
5973 "-ERR Number of keys can't be greater than number of args\r\n"
5974 );
5975 assert_eq!(
5976 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
5977 "-ERR LIMIT can't be negative\r\n"
5978 );
5979 assert_eq!(
5980 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
5981 "-ERR syntax error\r\n"
5982 );
5983 f.run(&[b"SADD", b"LIMIT", b"2"]);
5985 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
5986 }
5987
5988 #[test]
5991 fn sunioncard_and_sdiffcard_count_without_building() {
5992 let mut f = Fixture::new();
5993 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5994 f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
5995
5996 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
5997 assert_eq!(
5998 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5999 ":2\r\n"
6000 );
6001 assert_eq!(
6002 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6003 ":6\r\n",
6004 "a limit of zero is no limit"
6005 );
6006 assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6007 assert_eq!(
6008 f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6009 ":4\r\n",
6010 "a missing key adds nothing to a union"
6011 );
6012
6013 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6014 assert_eq!(
6015 f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6016 ":1\r\n"
6017 );
6018 assert_eq!(
6019 f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6020 ":2\r\n",
6021 "a difference is not symmetric"
6022 );
6023 assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6024 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6025 assert_eq!(
6026 f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6027 ":0\r\n",
6028 "nothing taken away from nothing"
6029 );
6030
6031 for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6034 assert_eq!(
6035 f.run(&[name, b"0", b"a"]),
6036 "-ERR numkeys should be greater than 0\r\n"
6037 );
6038 assert_eq!(
6039 f.run(&[name, b"abc", b"a"]),
6040 "-ERR numkeys should be greater than 0\r\n"
6041 );
6042 assert_eq!(
6043 f.run(&[name, b"-1", b"a"]),
6044 "-ERR numkeys should be greater than 0\r\n"
6045 );
6046 assert_eq!(
6047 f.run(&[name, b"3", b"a", b"b"]),
6048 "-ERR Number of keys can't be greater than number of args\r\n"
6049 );
6050 assert_eq!(
6051 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6052 "-ERR LIMIT can't be negative\r\n"
6053 );
6054 assert_eq!(
6055 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6056 "-ERR LIMIT can't be negative\r\n",
6057 "a LIMIT that is not a number gets the negative message too"
6058 );
6059 assert_eq!(
6060 f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6061 "-ERR syntax error\r\n"
6062 );
6063 assert_eq!(
6064 f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6065 "-ERR syntax error\r\n"
6066 );
6067 assert_eq!(
6068 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6069 "-ERR syntax error\r\n"
6070 );
6071 }
6072
6073 f.run(&[b"SADD", b"LIMIT", b"2"]);
6075 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6076 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6077 }
6078
6079 #[test]
6080 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6081 let mut f = Fixture::new();
6082 f.run(&[b"SADD", b"a", b"1"]);
6083 f.run(&[b"SADD", b"d", b"old"]);
6084 f.run(&[b"SET", b"str", b"v"]);
6085
6086 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6087 for bad in [
6088 &[b"SINTER".as_slice(), b"a", b"str"][..],
6089 &[b"SUNION".as_slice(), b"str"][..],
6090 &[b"SDIFF".as_slice(), b"a", b"str"][..],
6091 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6092 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6093 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6094 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6095 ] {
6096 let reply = f.run(bad);
6097 assert_eq!(reply, wrong, "for {:?}", bad[0]);
6098 }
6099 assert_eq!(
6100 f.run(&[b"SMEMBERS", b"d"]),
6101 "*1\r\n$3\r\nold\r\n",
6102 "and the destination was left alone every time"
6103 );
6104 }
6105
6106 #[test]
6109 fn churning_sets_does_not_grow_the_server() {
6110 let mut f = Fixture::new();
6111 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6112 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6113 .chain(std::iter::once(&b"s"[..]))
6114 .chain(members.iter().map(Vec::as_slice))
6115 .collect();
6116
6117 f.run(&args);
6118 f.run(&[b"DEL", b"s"]);
6119 f.server.compact_step();
6120 let after_first = f.server.memory_bytes();
6121
6122 for _ in 0..200 {
6123 f.run(&args);
6124 f.run(&[b"DEL", b"s"]);
6125 f.server.compact_step();
6126 }
6127 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6128 assert!(
6129 f.server.memory_bytes() <= after_first * 2,
6130 "held {} after two hundred passes against {after_first} after one",
6131 f.server.memory_bytes()
6132 );
6133 }
6134
6135 #[test]
6143 fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6144 let mut f = Fixture::new();
6145 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6146 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6147 assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6148 assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6149 assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6150 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6151
6152 assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6154 assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6155 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6156
6157 f.run(&[b"SET", b"num", b"12345"]);
6158 assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6159 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6160 assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6161 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6162 assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6163 }
6164
6165 #[test]
6170 fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6171 let mut f = Fixture::new();
6172 f.run(&[b"SET", b"mykey", b"foobar"]);
6173 assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6174 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6175 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6176 assert_eq!(
6177 f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6178 ":6\r\n"
6179 );
6180 assert_eq!(
6181 f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6182 ":25\r\n"
6183 );
6184 assert_eq!(
6185 f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6186 ":17\r\n"
6187 );
6188 assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6189
6190 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6193
6194 assert_eq!(
6196 f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6197 "-ERR syntax error\r\n"
6198 );
6199 assert_eq!(
6200 f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6201 "-ERR syntax error\r\n"
6202 );
6203 }
6204
6205 #[test]
6211 fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6212 let mut f = Fixture::new();
6213 f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6214 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6215 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6216 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6217 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6218 assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6219
6220 f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6221 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6222 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6223 assert_eq!(
6224 f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6225 ":8\r\n"
6226 );
6227
6228 assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6231 assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6232 }
6233
6234 #[test]
6236 fn the_eight_combinations_write_what_a_real_server_writes() {
6237 let mut f = Fixture::new();
6238 f.run(&[b"SET", b"a", b"abc"]);
6239 f.run(&[b"SET", b"b", b"abd"]);
6240 let cases: &[(&[u8], &str)] = &[
6241 (b"AND", "ab`"),
6242 (b"OR", "abg"),
6243 (b"XOR", "\u{0}\u{0}\u{7}"),
6244 (b"DIFF", "\u{0}\u{0}\u{3}"),
6245 (b"DIFF1", "\u{0}\u{0}\u{4}"),
6246 (b"ANDOR", "ab`"),
6247 (b"ONE", "\u{0}\u{0}\u{7}"),
6248 ];
6249 for (op, want) in cases {
6250 assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6251 assert_eq!(
6252 f.run(&[b"GET", b"d"]),
6253 format!("$3\r\n{want}\r\n"),
6254 "{op:?}"
6255 );
6256 }
6257 assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6259 assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6260
6261 assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6264 assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6265
6266 f.run(&[b"SET", b"dest", b"x"]);
6269 assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6270 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6271 }
6272
6273 #[test]
6275 fn bitop_names_the_operation_in_its_own_complaints() {
6276 let mut f = Fixture::new();
6277 f.run(&[b"SET", b"a", b"abc"]);
6278 assert_eq!(
6279 f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6280 "-ERR syntax error\r\n"
6281 );
6282 assert_eq!(
6283 f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6284 "-ERR BITOP NOT must be called with a single source key.\r\n"
6285 );
6286 for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6287 assert_eq!(
6288 f.run(&[b"BITOP", op, b"d", b"a"]),
6289 format!(
6290 "-ERR BITOP {} must be called with at least two source keys.\r\n",
6291 String::from_utf8_lossy(op)
6292 )
6293 );
6294 }
6295 f.run(&[b"LPUSH", b"l", b"x"]);
6296 assert_eq!(
6297 f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6298 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6299 );
6300 }
6301
6302 #[test]
6304 fn bitfield_reads_and_writes_packed_fields() {
6305 let mut f = Fixture::new();
6306 assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6307 assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6308
6309 assert_eq!(
6310 f.run(&[
6311 b"BITFIELD",
6312 b"bf",
6313 b"INCRBY",
6314 b"u2",
6315 b"100",
6316 b"1",
6317 b"GET",
6318 b"u4",
6319 b"0"
6320 ]),
6321 "*2\r\n:1\r\n:0\r\n"
6322 );
6323 assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6326
6327 assert_eq!(
6329 f.run(&[
6330 b"BITFIELD",
6331 b"bf",
6332 b"SET",
6333 b"u8",
6334 b"#0",
6335 b"255",
6336 b"GET",
6337 b"u8",
6338 b"#0"
6339 ]),
6340 "*2\r\n:0\r\n:255\r\n"
6341 );
6342
6343 assert_eq!(
6344 f.run(&[
6345 b"BITFIELD",
6346 b"bf",
6347 b"OVERFLOW",
6348 b"SAT",
6349 b"INCRBY",
6350 b"i8",
6351 b"0",
6352 b"120",
6353 b"INCRBY",
6354 b"i8",
6355 b"0",
6356 b"120"
6357 ]),
6358 "*2\r\n:119\r\n:127\r\n"
6359 );
6360 assert_eq!(
6361 f.run(&[
6362 b"BITFIELD",
6363 b"bf2",
6364 b"OVERFLOW",
6365 b"FAIL",
6366 b"INCRBY",
6367 b"u2",
6368 b"0",
6369 b"5"
6370 ]),
6371 "*1\r\n$-1\r\n"
6372 );
6373 assert_eq!(
6374 f.run(&[
6375 b"BITFIELD",
6376 b"bf3",
6377 b"OVERFLOW",
6378 b"WRAP",
6379 b"INCRBY",
6380 b"u2",
6381 b"0",
6382 b"5"
6383 ]),
6384 "*1\r\n:1\r\n"
6385 );
6386 assert_eq!(
6387 f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6388 "*1\r\n:4611686018427387904\r\n"
6389 );
6390 }
6391
6392 #[test]
6398 fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6399 let mut f = Fixture::new();
6400 let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6401 assert_eq!(
6402 f.run(&[
6403 b"BITFIELD",
6404 b"bad",
6405 b"SET",
6406 b"u8",
6407 b"0",
6408 b"1",
6409 b"GET",
6410 b"u99",
6411 b"0"
6412 ]),
6413 bad_type
6414 );
6415 assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6416 assert_eq!(
6417 f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6418 bad_type
6419 );
6420 assert_eq!(
6421 f.run(&[b"BITFIELD", b"bad", b"GET"]),
6422 "-ERR syntax error\r\n"
6423 );
6424 assert_eq!(
6425 f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6426 "-ERR syntax error\r\n"
6427 );
6428 assert_eq!(
6429 f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6430 "-ERR syntax error\r\n"
6431 );
6432 assert_eq!(
6433 f.run(&[
6434 b"BITFIELD",
6435 b"bad",
6436 b"OVERFLOW",
6437 b"NOPE",
6438 b"GET",
6439 b"u8",
6440 b"0"
6441 ]),
6442 "-ERR Invalid OVERFLOW type specified\r\n"
6443 );
6444 assert_eq!(
6445 f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6446 "-ERR value is not an integer or out of range\r\n"
6447 );
6448 for at in [&b"#-1"[..], b"abc"] {
6449 assert_eq!(
6450 f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6451 "-ERR bit offset is not an integer or out of range\r\n"
6452 );
6453 }
6454 }
6455
6456 #[test]
6458 fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6459 let mut f = Fixture::new();
6460 f.run(&[b"SET", b"n", b"123"]);
6461 assert_eq!(
6462 f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6463 "*1\r\n:49\r\n"
6464 );
6465 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6467
6468 assert_eq!(
6470 f.run(&[
6471 b"BITFIELD_RO",
6472 b"n",
6473 b"OVERFLOW",
6474 b"SAT",
6475 b"GET",
6476 b"u8",
6477 b"0"
6478 ]),
6479 "*1\r\n:49\r\n"
6480 );
6481 for sub in [&b"SET"[..], b"INCRBY"] {
6482 assert_eq!(
6483 f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6484 "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6485 );
6486 }
6487
6488 assert_eq!(
6489 f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6490 "*1\r\n:0\r\n"
6491 );
6492 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6493 }
6494
6495 #[test]
6497 fn an_offset_off_the_end_of_the_world_is_refused() {
6498 let mut f = Fixture::new();
6499 let bad = "-ERR bit offset is not an integer or out of range\r\n";
6500 for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6501 assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6502 assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6503 }
6504 for arg in [&b"2"[..], b"-1"] {
6505 assert_eq!(
6506 f.run(&[b"BITPOS", b"k", arg]),
6507 "-ERR The bit argument must be 1 or 0.\r\n"
6508 );
6509 }
6510 assert_eq!(
6511 f.run(&[b"BITPOS", b"k", b"abc"]),
6512 "-ERR value is not an integer or out of range\r\n"
6513 );
6514 assert_eq!(
6515 f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
6516 "-ERR value is not an integer or out of range\r\n"
6517 );
6518 let bad_bit = "-ERR bit is not an integer or out of range\r\n";
6519 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
6520 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
6521 }
6522
6523 #[test]
6525 fn every_bitmap_command_says_wrongtype() {
6526 let mut f = Fixture::new();
6527 f.run(&[b"LPUSH", b"l", b"x"]);
6528 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6529 let cases: &[&[&[u8]]] = &[
6530 &[b"SETBIT", b"l", b"0", b"1"],
6531 &[b"GETBIT", b"l", b"0"],
6532 &[b"BITCOUNT", b"l"],
6533 &[b"BITPOS", b"l", b"1"],
6534 &[b"BITOP", b"AND", b"d", b"l"],
6535 &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
6536 &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
6537 ];
6538 for case in cases {
6539 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
6540 }
6541 }
6542
6543 #[test]
6546 fn a_sketch_is_added_to_and_counted() {
6547 let mut f = Fixture::new();
6548 assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
6550 assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
6551 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
6552 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
6553 assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
6556 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
6557
6558 assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
6559 assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
6560 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6561 }
6562
6563 #[test]
6564 fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
6565 let mut f = Fixture::new();
6566 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6567 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";
6569 let mut reply = b"$27\r\n".to_vec();
6570 reply.extend_from_slice(want);
6571 reply.extend_from_slice(b"\r\n");
6572 assert_eq!(f.raw(&[b"GET", b"h"]), reply);
6573 }
6574
6575 #[test]
6576 fn counting_several_keys_counts_their_union() {
6577 let mut f = Fixture::new();
6578 f.run(&[b"PFADD", b"a", b"x", b"y"]);
6579 f.run(&[b"PFADD", b"b", b"y", b"z"]);
6580 assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
6581 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
6582 assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
6585 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
6586 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6587 }
6588
6589 #[test]
6590 fn a_merge_keeps_what_the_destination_had() {
6591 let mut f = Fixture::new();
6592 f.run(&[b"PFADD", b"a", b"x", b"y"]);
6593 f.run(&[b"PFADD", b"b", b"z"]);
6594 assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
6595 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
6596 f.run(&[b"PFADD", b"c", b"w"]);
6598 assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
6599 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
6600 assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
6603 assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
6604 }
6605
6606 #[test]
6607 fn the_debug_forms_answer_four_different_shapes() {
6608 let mut f = Fixture::new();
6609 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6610 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
6611 assert_eq!(
6612 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6613 "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
6614 );
6615 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
6616 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
6617 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
6618 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
6619 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6620 assert_eq!(
6622 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6623 "-ERR HLL encoding is not sparse\r\n"
6624 );
6625
6626 let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
6628 assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
6629 assert_eq!(reply.matches(":0\r\n").count(), 16381);
6630 assert_eq!(reply.matches(":1\r\n").count(), 2);
6631 assert_eq!(reply.matches(":2\r\n").count(), 1);
6632
6633 assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
6634 }
6635
6636 #[test]
6637 fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
6638 let mut f = Fixture::new();
6639 f.run(&[b"SET", b"plain", b"not a sketch"]);
6640 let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
6641 assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
6642 assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
6643 assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
6644 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
6645
6646 f.run(&[b"RPUSH", b"l", b"x"]);
6649 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6650 assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
6651 assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
6652 assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
6653 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6654 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
6655 }
6656
6657 #[test]
6658 fn pfdebug_has_its_own_complaints() {
6659 let mut f = Fixture::new();
6660 f.run(&[b"PFADD", b"h", b"a"]);
6661 assert_eq!(
6664 f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
6665 "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
6666 );
6667 let gone = "-ERR The specified key does not exist\r\n";
6669 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
6670 assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
6671 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
6672 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
6673 assert_eq!(
6674 f.run(&[b"PFDEBUG"]),
6675 "-ERR wrong number of arguments for 'pfdebug' command\r\n"
6676 );
6677 assert_eq!(
6678 f.run(&[b"PFSELFTEST", b"x"]),
6679 "-ERR wrong number of arguments for 'pfselftest' command\r\n"
6680 );
6681 }
6682
6683 #[test]
6684 fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
6685 let mut f = Fixture::new();
6686 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6687 let reply = f.raw(&[b"GET", b"h"]);
6690 let short = reply[5..reply.len() - 3].to_vec();
6691 f.run(&[b"SET", b"h", &short]);
6692 assert_eq!(
6693 f.run(&[b"PFCOUNT", b"h"]),
6694 "-INVALIDOBJ Corrupted HLL object detected\r\n"
6695 );
6696 }
6697
6698 #[test]
6699 fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
6700 let mut f = Fixture::new();
6701 f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
6704 for i in 0..10_000u32 {
6705 let ele = format!("e{i}");
6706 f.run(&[b"PFADD", b"big", ele.as_bytes()]);
6707 }
6708 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
6709 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
6710
6711 for key in [&b"small"[..], b"big"] {
6712 let mut copy = key.to_vec();
6713 copy.push(b'2');
6714 let bytes = payload(&f.raw(&[b"DUMP", key]));
6715 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
6716 assert_eq!(f.raw(&[b"GET", ©]), f.raw(&[b"GET", key]));
6719 assert_eq!(
6720 f.run(&[b"PFDEBUG", b"ENCODING", ©]),
6721 f.run(&[b"PFDEBUG", b"ENCODING", key])
6722 );
6723 assert_eq!(f.run(&[b"PFCOUNT", ©]), f.run(&[b"PFCOUNT", key]));
6724 }
6725 assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
6726 assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
6727 }
6728
6729 fn bulk(s: &str) -> String {
6733 format!("${}\r\n{s}\r\n", s.len())
6734 }
6735
6736 fn bulks(parts: &[&str]) -> String {
6739 let mut s = format!("*{}\r\n", parts.len());
6740 for p in parts {
6741 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
6742 }
6743 s
6744 }
6745
6746 #[test]
6747 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
6748 let mut f = Fixture::new();
6749 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
6753 assert_eq!(
6754 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6755 bulks(&["c", "b", "a"])
6756 );
6757 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
6758 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
6759 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
6760 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
6761 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
6762 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
6763 }
6764
6765 #[test]
6766 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
6767 let mut f = Fixture::new();
6768 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
6769 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
6770 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6771 f.run(&[b"RPUSH", b"k", b"a"]);
6772 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
6773 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
6774 assert_eq!(
6775 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6776 bulks(&["z", "a", "y"])
6777 );
6778 }
6779
6780 #[test]
6783 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
6784 let mut f = Fixture::new();
6785 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
6786 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
6787 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
6788 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
6789 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6790 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
6793 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
6794 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
6796 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6797 }
6798
6799 #[test]
6800 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
6801 let mut f = Fixture::new();
6802 f.run(&[b"RPUSH", b"k", b"a"]);
6803 let range = "-ERR value is out of range, must be positive\r\n";
6804 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
6805 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
6806 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
6807 assert_eq!(
6810 f.run(&[b"LPOP", b"k", b"1", b"2"]),
6811 "-ERR wrong number of arguments for 'lpop' command\r\n"
6812 );
6813 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
6814 }
6815
6816 #[test]
6817 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
6818 let mut f = Fixture::new();
6819 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6820 assert_eq!(
6821 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6822 bulks(&["a", "b", "c"])
6823 );
6824 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
6825 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
6826 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
6827 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
6828 assert_eq!(
6829 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
6830 bulks(&["a", "b", "c"])
6831 );
6832 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
6835 assert_eq!(
6836 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
6837 "-ERR value is not an integer or out of range\r\n"
6838 );
6839 }
6840
6841 #[test]
6842 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
6843 let mut f = Fixture::new();
6844 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6845 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
6846 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
6847 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
6848 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
6849 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
6850 assert_eq!(
6851 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6852 bulks(&["a", "b", "z"])
6853 );
6854 assert_eq!(
6857 f.run(&[b"LSET", b"k", b"99", b"z"]),
6858 "-ERR index out of range\r\n"
6859 );
6860 assert_eq!(
6861 f.run(&[b"LSET", b"nope", b"0", b"z"]),
6862 "-ERR no such key\r\n"
6863 );
6864 }
6865
6866 #[test]
6867 fn linsert_says_three_things_with_one_signed_number() {
6868 let mut f = Fixture::new();
6869 assert_eq!(
6872 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
6873 ":0\r\n"
6874 );
6875 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6876 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
6877 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
6878 assert_eq!(
6879 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6880 bulks(&["X", "a", "b", "Y"])
6881 );
6882 assert_eq!(
6883 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
6884 ":-1\r\n"
6885 );
6886 assert_eq!(
6887 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
6888 "-ERR syntax error\r\n"
6889 );
6890 }
6891
6892 #[test]
6893 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
6894 let mut f = Fixture::new();
6895 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
6896 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
6897 assert_eq!(
6898 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6899 bulks(&["b", "c", "a"])
6900 );
6901 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
6902 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6903 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
6904 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
6905 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6906 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
6907 }
6908
6909 #[test]
6910 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
6911 let mut f = Fixture::new();
6912 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
6913 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
6914 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6915 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
6918 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6919 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
6920 }
6921
6922 #[test]
6923 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
6924 let mut f = Fixture::new();
6925 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
6926 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
6927 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
6928 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
6929 assert_eq!(
6930 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
6931 "*2\r\n:0\r\n:3\r\n"
6932 );
6933 assert_eq!(
6934 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
6935 "*3\r\n:6\r\n:3\r\n:0\r\n"
6936 );
6937 assert_eq!(
6940 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
6941 "*1\r\n:0\r\n"
6942 );
6943 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
6946 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
6947 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
6948 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
6949 }
6950
6951 #[test]
6952 fn lpos_words_its_three_mistakes_the_way_redis_does() {
6953 let mut f = Fixture::new();
6954 f.run(&[b"RPUSH", b"p", b"a"]);
6955 assert_eq!(
6958 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
6959 "-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"
6960 );
6961 assert_eq!(
6962 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
6963 "-ERR COUNT can't be negative\r\n"
6964 );
6965 assert_eq!(
6966 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
6967 "-ERR MAXLEN can't be negative\r\n"
6968 );
6969 assert_eq!(
6970 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
6971 "-ERR syntax error\r\n"
6972 );
6973 assert_eq!(
6974 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
6975 "-ERR syntax error\r\n"
6976 );
6977 }
6978
6979 #[test]
6980 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
6981 let mut f = Fixture::new();
6982 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6983 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
6984 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6985 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
6986 assert_eq!(
6987 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
6988 "$1\r\na\r\n"
6989 );
6990 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
6991 f.run(&[b"DEL", b"r"]);
6994 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
6995 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
6996 assert_eq!(
6997 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
6998 bulks(&["3", "1", "2"])
6999 );
7000 assert_eq!(
7001 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7002 "$-1\r\n"
7003 );
7004 assert_eq!(
7005 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7006 "-ERR syntax error\r\n"
7007 );
7008 }
7009
7010 #[test]
7011 fn a_move_checks_the_destination_before_it_takes_anything() {
7012 let mut f = Fixture::new();
7013 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7014 f.run(&[b"SET", b"str", b"v"]);
7015 assert_eq!(
7016 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7017 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7018 );
7019 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7021 }
7022
7023 #[test]
7024 fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7025 for (from, to, order, want) in [
7029 ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7030 ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7031 ("LEFT", "LEFT", "OBO", ["b", "a"]),
7032 ("LEFT", "LEFT", "BULK", ["a", "b"]),
7033 ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7034 ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7035 ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7036 ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7037 ] {
7038 let mut f = Fixture::new();
7039 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7040 let how = format!("{from} {to} {order}");
7041 let reply = f.run(&[
7042 b"LMOVEM",
7043 b"s",
7044 b"d",
7045 from.as_bytes(),
7046 to.as_bytes(),
7047 b"COUNT",
7048 b"2",
7049 order.as_bytes(),
7050 ]);
7051 assert_eq!(reply, bulks(&want), "the reply for {how}");
7052 assert_eq!(
7053 f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7054 bulks(&want),
7055 "the destination for {how}"
7056 );
7057 }
7058 }
7059
7060 #[test]
7061 fn a_block_move_of_one_needs_no_count_at_all() {
7062 let mut f = Fixture::new();
7063 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7064 assert_eq!(
7065 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7066 bulks(&["a"])
7067 );
7068 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7069 assert_eq!(
7072 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7073 "-ERR syntax error\r\n"
7074 );
7075 assert_eq!(
7076 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7077 "-ERR syntax error\r\n"
7078 );
7079 }
7080
7081 #[test]
7082 fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7083 let mut f = Fixture::new();
7084 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7085 assert_eq!(
7089 f.run(&[
7090 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7091 ]),
7092 "*-1\r\n"
7093 );
7094 assert_eq!(
7095 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7096 bulks(&["a", "b", "c"])
7097 );
7098 assert_eq!(
7100 f.run(&[
7101 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7102 ]),
7103 bulks(&["a", "b", "c"])
7104 );
7105 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7106 assert_eq!(
7107 f.run(&[
7108 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7109 ]),
7110 "*-1\r\n"
7111 );
7112 }
7113
7114 #[test]
7115 fn a_block_move_onto_itself_rotates_by_the_count() {
7116 let mut f = Fixture::new();
7117 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7118 assert_eq!(
7119 f.run(&[
7120 b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7121 ]),
7122 bulks(&["a", "b"])
7123 );
7124 assert_eq!(
7125 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7126 bulks(&["c", "a", "b"])
7127 );
7128 }
7129
7130 #[test]
7131 fn a_block_move_reads_the_count_before_the_ordering_word() {
7132 let mut f = Fixture::new();
7133 f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7134 f.run(&[b"SET", b"str", b"v"]);
7135 let count = "-ERR count should be greater than 0\r\n";
7136 assert_eq!(
7137 f.run(&[
7138 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7139 ]),
7140 count
7141 );
7142 assert_eq!(
7143 f.run(&[
7144 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7145 ]),
7146 count
7147 );
7148 assert_eq!(
7149 f.run(&[
7150 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7151 ]),
7152 "-ERR syntax error\r\n"
7153 );
7154 assert_eq!(
7155 f.run(&[
7156 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7157 ]),
7158 "-ERR syntax error\r\n"
7159 );
7160 assert_eq!(
7163 f.run(&[
7164 b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7165 ]),
7166 count
7167 );
7168 assert_eq!(
7169 f.run(&[
7170 b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7171 ]),
7172 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7173 );
7174 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7175 }
7176
7177 #[test]
7178 fn lmpop_answers_from_the_first_key_that_has_anything() {
7179 let mut f = Fixture::new();
7180 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7181 assert_eq!(
7184 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7185 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7186 );
7187 assert_eq!(
7188 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7189 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7190 );
7191 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7192 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7195 }
7196
7197 #[test]
7198 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7199 let mut f = Fixture::new();
7200 f.run(&[b"RPUSH", b"k", b"a"]);
7201 assert_eq!(
7202 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7203 "-ERR numkeys should be greater than 0\r\n"
7204 );
7205 assert_eq!(
7206 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7207 "-ERR numkeys should be greater than 0\r\n"
7208 );
7209 assert_eq!(
7210 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7211 "-ERR count should be greater than 0\r\n"
7212 );
7213 assert_eq!(
7216 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7217 "-ERR syntax error\r\n"
7218 );
7219 assert_eq!(
7220 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7221 "-ERR syntax error\r\n"
7222 );
7223 assert_eq!(
7224 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7225 "-ERR syntax error\r\n"
7226 );
7227 assert_eq!(
7228 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7229 "-ERR syntax error\r\n"
7230 );
7231 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7232 }
7233
7234 #[test]
7235 fn every_list_command_says_wrongtype_and_writes_nothing() {
7236 let mut f = Fixture::new();
7237 f.run(&[b"SET", b"str", b"v"]);
7238 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7239 for cmd in [
7240 &[b"LPUSH".as_slice(), b"str", b"a"][..],
7241 &[b"RPUSH", b"str", b"a"],
7242 &[b"LPUSHX", b"str", b"a"],
7243 &[b"RPUSHX", b"str", b"a"],
7244 &[b"LPOP", b"str"],
7245 &[b"LPOP", b"str", b"2"],
7246 &[b"RPOP", b"str"],
7247 &[b"LLEN", b"str"],
7248 &[b"LRANGE", b"str", b"0", b"-1"],
7249 &[b"LINDEX", b"str", b"0"],
7250 &[b"LSET", b"str", b"0", b"a"],
7251 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7252 &[b"LREM", b"str", b"0", b"a"],
7253 &[b"LTRIM", b"str", b"0", b"-1"],
7254 &[b"LPOS", b"str", b"a"],
7255 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7256 &[b"RPOPLPUSH", b"str", b"d"],
7257 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7258 &[b"LMPOP", b"1", b"str", b"LEFT"],
7259 ] {
7260 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7261 }
7262 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7263 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7264 }
7265
7266 #[test]
7270 fn a_timeout_has_three_ways_of_being_wrong() {
7271 let mut f = Fixture::new();
7272 let not_float = "-ERR timeout is not a float or out of range\r\n";
7273 let range = "-ERR timeout is out of range\r\n";
7274 for (bad, want) in [
7275 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7276 (&[b"BLPOP", b"k", b"nan"], not_float),
7277 (&[b"BLPOP", b"k", b""], not_float),
7278 (&[b"BLPOP", b"k", b" 1"], not_float),
7281 (&[b"BLPOP", b"k", b"1 "], not_float),
7282 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7283 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7284 (&[b"BLPOP", b"k", b"1e400"], range),
7287 (&[b"BLPOP", b"k", b"inf"], range),
7288 (&[b"BLPOP", b"k", b"9999999999999999"], range),
7289 (&[b"BRPOP", b"k", b"abc"], not_float),
7290 (
7291 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7292 not_float,
7293 ),
7294 (
7295 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7296 "-ERR timeout is negative\r\n",
7297 ),
7298 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7299 ] {
7300 assert_eq!(f.run(bad), want, "for {bad:?}");
7301 }
7302 }
7303
7304 #[test]
7307 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7308 let mut f = Fixture::new();
7309 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7310 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7311 assert_eq!(flow, Flow::Block, "for {timeout:?}");
7312 assert!(out.is_empty(), "for {timeout:?}");
7313 }
7314 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7318 assert_eq!(flow, Flow::Block);
7319 assert!(out.is_empty());
7320 }
7321
7322 #[test]
7323 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7324 let mut f = Fixture::new();
7325 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7326
7327 assert_eq!(
7330 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7331 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7332 );
7333 assert_eq!(
7334 f.run(&[b"BRPOP", b"L", b"0"]),
7335 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7336 );
7337 assert_eq!(
7338 f.run(&[
7339 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7340 ]),
7341 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7342 );
7343 assert_eq!(
7344 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7345 "$1\r\nd\r\n"
7346 );
7347 assert_eq!(
7348 f.run(&[b"EXISTS", b"L"]),
7349 ":0\r\n",
7350 "and the key went with it"
7351 );
7352 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7353 f.run(&[b"RPUSH", b"D", b"x"]);
7356 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7357 assert_eq!(
7358 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7359 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7360 );
7361 }
7362
7363 #[test]
7364 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7365 let mut f = Fixture::new();
7366 f.run(&[b"RPUSH", b"k", b"a"]);
7367 for (bad, want) in [
7368 (
7369 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7370 "-ERR numkeys should be greater than 0\r\n",
7371 ),
7372 (
7373 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7374 "-ERR numkeys should be greater than 0\r\n",
7375 ),
7376 (
7379 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7380 "-ERR syntax error\r\n",
7381 ),
7382 (
7383 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7384 "-ERR syntax error\r\n",
7385 ),
7386 (
7387 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7388 "-ERR syntax error\r\n",
7389 ),
7390 (
7391 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7392 "-ERR syntax error\r\n",
7393 ),
7394 (
7397 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7398 "-ERR count should be greater than 0\r\n",
7399 ),
7400 (
7401 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7402 "-ERR count should be greater than 0\r\n",
7403 ),
7404 ] {
7405 assert_eq!(f.run(bad), want, "for {bad:?}");
7406 }
7407 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7408 }
7409
7410 #[test]
7411 fn a_blocking_move_reads_its_directions_before_its_timeout() {
7412 let mut f = Fixture::new();
7413 assert_eq!(
7416 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7417 "-ERR syntax error\r\n"
7418 );
7419 assert_eq!(
7420 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7421 "-ERR syntax error\r\n"
7422 );
7423 }
7424
7425 #[test]
7429 fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7430 let mut f = Fixture::new();
7431 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7432 assert_eq!(
7433 f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7434 (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7435 );
7436 assert_eq!(
7437 f.run(&[
7438 b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7439 ]),
7440 bulks(&["e", "d"])
7441 );
7442 assert_eq!(
7443 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7444 bulks(&["a", "e", "d"])
7445 );
7446 assert_eq!(
7448 f.run(&[
7449 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7450 ]),
7451 bulks(&["b", "c"])
7452 );
7453 assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7454 }
7455
7456 #[test]
7460 fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7461 let mut f = Fixture::new();
7462 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7463 assert_eq!(
7465 f.flow(&[
7466 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7467 ]),
7468 (Flow::Continue, bulks(&["a", "b"]))
7469 );
7470
7471 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7472 assert_eq!(
7475 f.flow(&[
7476 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7477 ])
7478 .0,
7479 Flow::Block
7480 );
7481 assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7482 }
7483
7484 #[test]
7485 fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7486 let mut f = Fixture::new();
7487 let syntax = "-ERR syntax error\r\n";
7488 assert_eq!(
7490 f.run(&[
7491 b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7492 ]),
7493 syntax
7494 );
7495 assert_eq!(
7497 f.run(&[
7498 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7499 ]),
7500 "-ERR timeout is not a float or out of range\r\n"
7501 );
7502 assert_eq!(
7503 f.run(&[
7504 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7505 ]),
7506 "-ERR timeout is negative\r\n"
7507 );
7508 assert_eq!(
7510 f.run(&[
7511 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
7512 ]),
7513 "-ERR count should be greater than 0\r\n"
7514 );
7515 assert_eq!(
7516 f.run(&[
7517 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
7518 ]),
7519 syntax
7520 );
7521 assert_eq!(
7524 f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
7525 syntax
7526 );
7527 assert_eq!(
7528 f.run(&[
7529 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
7530 ]),
7531 syntax
7532 );
7533 }
7534
7535 #[test]
7538 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
7539 let mut f = Fixture::new();
7540 f.run(&[b"SET", b"S", b"v"]);
7541 f.run(&[b"RPUSH", b"D", b"x"]);
7542 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7543
7544 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
7545 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
7548 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
7549 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
7550 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
7551 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
7554 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
7555 assert_eq!(
7556 f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
7557 wrong
7558 );
7559 assert_eq!(
7560 f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
7561 wrong
7562 );
7563
7564 assert_eq!(
7568 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7569 .0,
7570 Flow::Block
7571 );
7572 assert_eq!(
7576 f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7577 .0,
7578 Flow::Block
7579 );
7580 f.run(&[b"RPUSH", b"E", b"1", b"2"]);
7581 assert_eq!(
7582 f.flow(&[
7583 b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
7584 ])
7585 .0,
7586 Flow::Block
7587 );
7588 }
7589
7590 #[test]
7594 fn churning_lists_does_not_grow_the_server() {
7595 let mut f = Fixture::new();
7596 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
7597 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
7598 .into_iter()
7599 .chain(vals.iter().map(Vec::as_slice))
7600 .collect();
7601
7602 f.run(&args);
7603 f.run(&[b"DEL", b"k"]);
7604 f.server.compact_step();
7605 let after_first = f.server.memory_bytes();
7606
7607 for _ in 0..200 {
7608 f.run(&args);
7609 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
7610 f.server.compact_step();
7611 }
7612 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
7613 assert!(
7614 f.server.memory_bytes() <= after_first * 2,
7615 "held {} after two hundred passes against {after_first} after one",
7616 f.server.memory_bytes()
7617 );
7618 }
7619
7620 #[test]
7623 fn a_sorted_set_takes_scores_and_gives_them_back() {
7624 let mut f = Fixture::new();
7625 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
7626 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
7627 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
7628 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
7629 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
7630 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
7631 assert_eq!(
7632 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
7633 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
7634 );
7635 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
7636 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
7637 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
7639 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7640 }
7641
7642 #[test]
7643 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
7644 let mut f = Fixture::new();
7645 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
7646 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
7647 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
7648 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
7649
7650 f.out = Out::new(Proto::Resp3);
7651 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
7652 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
7653 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
7654 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
7655 }
7656
7657 #[test]
7658 fn the_zadd_options_gate_what_gets_written() {
7659 let mut f = Fixture::new();
7660 f.run(&[b"ZADD", b"z", b"5", b"a"]);
7661 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
7663 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
7664 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
7665 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
7666 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
7668 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
7669 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
7670 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
7672 assert_eq!(
7673 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
7674 ":2\r\n"
7675 );
7676 }
7677
7678 #[test]
7679 fn zadd_incr_answers_a_score_or_nothing_at_all() {
7680 let mut f = Fixture::new();
7681 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
7682 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
7683 assert_eq!(
7686 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
7687 "$-1\r\n"
7688 );
7689 assert_eq!(
7690 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
7691 "$-1\r\n"
7692 );
7693 assert_eq!(
7694 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
7695 "$-1\r\n"
7696 );
7697 assert_eq!(
7698 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
7699 "$1\r\n8\r\n"
7700 );
7701 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
7702 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
7703 }
7704
7705 #[test]
7706 fn the_two_infinities_will_not_be_added_together() {
7707 let mut f = Fixture::new();
7708 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
7709 let nan = "-ERR resulting score is not a number (NaN)\r\n";
7710 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
7711 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
7712 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
7713 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
7715 }
7716
7717 #[test]
7718 fn zadd_says_its_mistakes_the_way_redis_says_them() {
7719 let mut f = Fixture::new();
7720 assert_eq!(
7723 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
7724 "-ERR syntax error\r\n"
7725 );
7726 assert_eq!(
7727 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
7728 "-ERR XX and NX options at the same time are not compatible\r\n"
7729 );
7730 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
7731 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
7732 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
7733 assert_eq!(
7734 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
7735 "-ERR INCR option supports a single increment-element pair\r\n"
7736 );
7737 assert_eq!(
7739 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
7740 "-ERR syntax error\r\n"
7741 );
7742 assert_eq!(
7744 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
7745 "-ERR value is not a valid float\r\n"
7746 );
7747 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7748 }
7749
7750 #[test]
7751 fn a_rank_says_where_a_member_sits_from_either_end() {
7752 let mut f = Fixture::new();
7753 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7754 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
7755 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
7756 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
7757 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
7758 assert_eq!(
7760 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
7761 "*2\r\n:1\r\n$1\r\n2\r\n"
7762 );
7763 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
7764 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
7765 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
7766 assert_eq!(
7769 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
7770 "-ERR syntax error\r\n"
7771 );
7772 assert_eq!(
7773 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
7774 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
7775 );
7776 }
7777
7778 #[test]
7779 fn the_two_counts_read_their_two_kinds_of_bound() {
7780 let mut f = Fixture::new();
7781 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7782 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
7783 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
7784 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
7785 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
7786 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
7787 assert_eq!(
7788 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
7789 "-ERR min or max is not a float\r\n"
7790 );
7791
7792 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
7793 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
7794 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
7795 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
7796 assert_eq!(
7799 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
7800 "-ERR min or max not valid string range item\r\n"
7801 );
7802 }
7803
7804 #[test]
7810 fn one_range_command_selects_by_rank_or_score_or_name() {
7811 let mut f = Fixture::new();
7812 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7813 assert_eq!(
7814 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7815 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7816 );
7817 assert_eq!(
7818 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
7819 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7820 );
7821 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
7822 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
7823 assert_eq!(
7826 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
7827 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7828 );
7829 assert_eq!(
7830 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
7831 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7832 );
7833 assert_eq!(
7836 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
7837 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7838 );
7839 assert_eq!(
7840 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
7841 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7842 );
7843 assert_eq!(
7844 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
7845 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7846 );
7847 }
7848
7849 #[test]
7852 fn the_older_range_spellings_name_their_high_end_first() {
7853 let mut f = Fixture::new();
7854 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7855 assert_eq!(
7856 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
7857 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7858 );
7859 assert_eq!(
7860 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
7861 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7862 );
7863 assert_eq!(
7864 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
7865 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7866 );
7867 assert_eq!(
7868 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
7869 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7870 );
7871 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
7875 assert_eq!(
7876 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
7877 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
7878 );
7879 assert_eq!(
7880 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
7881 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
7882 );
7883 for cmd in [
7886 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
7887 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
7888 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
7889 ] {
7890 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
7891 }
7892 }
7893
7894 #[test]
7897 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
7898 let mut f = Fixture::new();
7899 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7900 assert_eq!(
7901 f.run(&[
7902 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
7903 ]),
7904 "*1\r\n$1\r\nb\r\n"
7905 );
7906 assert_eq!(
7908 f.run(&[
7909 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
7910 ]),
7911 "*0\r\n"
7912 );
7913 assert_eq!(
7914 f.run(&[
7915 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
7916 ]),
7917 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7918 );
7919 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";
7921 assert_eq!(
7922 f.run(&[
7923 b"ZRANGEBYSCORE",
7924 b"z",
7925 b"1",
7926 b"3",
7927 b"WITHSCORES",
7928 b"LIMIT",
7929 b"0",
7930 b"2"
7931 ]),
7932 both
7933 );
7934 assert_eq!(
7935 f.run(&[
7936 b"ZRANGEBYSCORE",
7937 b"z",
7938 b"1",
7939 b"3",
7940 b"LIMIT",
7941 b"0",
7942 b"2",
7943 b"WITHSCORES"
7944 ]),
7945 both
7946 );
7947 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
7950 assert_eq!(
7951 f.run(&[
7952 b"ZREVRANGE",
7953 b"z",
7954 b"0",
7955 b"-1",
7956 b"WITHSCORES",
7957 b"LIMIT",
7958 b"0",
7959 b"1"
7960 ]),
7961 needs_by
7962 );
7963 assert_eq!(
7964 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
7965 needs_by
7966 );
7967 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
7968 assert_eq!(
7969 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
7970 not_bylex
7971 );
7972 assert_eq!(
7973 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
7974 not_bylex
7975 );
7976 for cmd in [
7979 &[
7980 b"ZRANGE".as_slice(),
7981 b"z",
7982 b"0",
7983 b"-1",
7984 b"BYSCORE",
7985 b"BYLEX",
7986 ][..],
7987 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
7988 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
7989 ] {
7990 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
7991 }
7992 assert_eq!(
7993 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
7994 "-ERR min or max is not a float\r\n"
7995 );
7996 assert_eq!(
7997 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
7998 "-ERR min or max not valid string range item\r\n"
7999 );
8000 assert_eq!(
8001 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8002 "-ERR value is not an integer or out of range\r\n"
8003 );
8004 }
8005
8006 #[test]
8009 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8010 let mut f = Fixture::new();
8011 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8012 assert_eq!(
8013 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8014 "*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"
8015 );
8016 f.out = Out::new(Proto::Resp3);
8017 assert_eq!(
8018 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8019 "*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"
8020 );
8021 assert_eq!(
8022 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8023 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8024 );
8025 }
8026
8027 #[test]
8029 fn a_range_store_writes_the_window_into_another_key() {
8030 let mut f = Fixture::new();
8031 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8032 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8033 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8036 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8037 assert_eq!(
8038 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8039 ":2\r\n"
8040 );
8041 assert_eq!(
8042 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8043 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8044 );
8045 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8048 assert_eq!(
8049 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8050 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8051 );
8052 assert_eq!(
8055 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8056 "-ERR syntax error\r\n"
8057 );
8058 }
8059
8060 #[test]
8063 fn the_three_removals_share_their_window_with_the_reads() {
8064 let mut f = Fixture::new();
8065 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8066 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8067 assert_eq!(
8068 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8069 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8070 );
8071 assert_eq!(
8072 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8073 ":1\r\n"
8074 );
8075 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8076 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8078 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8079 assert_eq!(
8080 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8081 ":0\r\n"
8082 );
8083 assert_eq!(
8084 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8085 "-ERR value is not an integer or out of range\r\n"
8086 );
8087 }
8088
8089 #[test]
8091 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8092 let mut f = Fixture::new();
8093 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8094 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8095 assert_eq!(
8096 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8097 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8098 );
8099 assert_eq!(
8102 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8103 "*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"
8104 );
8105 assert_eq!(
8106 f.run(&[
8107 b"ZUNION",
8108 b"2",
8109 b"z",
8110 b"y",
8111 b"WEIGHTS",
8112 b"2",
8113 b"3",
8114 b"WITHSCORES"
8115 ]),
8116 "*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"
8117 );
8118 assert_eq!(
8119 f.run(&[
8120 b"ZUNION",
8121 b"2",
8122 b"z",
8123 b"y",
8124 b"AGGREGATE",
8125 b"MIN",
8126 b"WITHSCORES"
8127 ]),
8128 "*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"
8129 );
8130 assert_eq!(
8131 f.run(&[
8132 b"ZUNION",
8133 b"2",
8134 b"z",
8135 b"y",
8136 b"AGGREGATE",
8137 b"MAX",
8138 b"WITHSCORES"
8139 ]),
8140 "*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"
8141 );
8142 assert_eq!(
8143 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8144 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8145 );
8146 assert_eq!(
8147 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8148 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8149 );
8150 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8151 f.run(&[b"SADD", b"p", b"a", b"d"]);
8154 assert_eq!(
8155 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8156 "*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"
8157 );
8158 for cmd in [
8161 &[
8162 b"ZDIFF".as_slice(),
8163 b"2",
8164 b"z",
8165 b"y",
8166 b"WEIGHTS",
8167 b"1",
8168 b"1",
8169 ][..],
8170 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8171 ] {
8172 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8173 }
8174 }
8175
8176 #[test]
8178 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8179 let mut f = Fixture::new();
8180 f.run(&[b"ZADD", b"z", b"1", b"a"]);
8181 f.run(&[b"ZADD", b"y", b"2", b"b"]);
8182 assert_eq!(
8184 f.run(&[b"ZUNION", b"0", b"z"]),
8185 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8186 );
8187 assert_eq!(
8188 f.run(&[b"ZUNION", b"-1", b"z"]),
8189 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8190 );
8191 assert_eq!(
8192 f.run(&[b"ZINTERCARD", b"0", b"z"]),
8193 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8194 );
8195 assert_eq!(
8198 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8199 "-ERR syntax error\r\n"
8200 );
8201 assert_eq!(
8202 f.run(&[b"ZUNION", b"x", b"z"]),
8203 "-ERR value is not an integer or out of range\r\n"
8204 );
8205 assert_eq!(
8208 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8209 "-ERR syntax error\r\n"
8210 );
8211 assert_eq!(
8212 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8213 "-ERR weight value is not a float\r\n"
8214 );
8215 assert_eq!(
8216 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8217 "-ERR syntax error\r\n"
8218 );
8219 }
8220
8221 #[test]
8223 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8224 let mut f = Fixture::new();
8225 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8226 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8227 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8228 assert_eq!(
8229 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8230 "*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"
8231 );
8232 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8233 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8234 assert_eq!(
8237 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8238 ":0\r\n"
8239 );
8240 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8241 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8243 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8244 for cmd in [
8245 &[
8246 b"ZUNIONSTORE".as_slice(),
8247 b"d",
8248 b"2",
8249 b"z",
8250 b"y",
8251 b"WITHSCORES",
8252 ][..],
8253 &[
8254 b"ZDIFFSTORE",
8255 b"d",
8256 b"2",
8257 b"z",
8258 b"y",
8259 b"WEIGHTS",
8260 b"1",
8261 b"1",
8262 ],
8263 ] {
8264 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8265 }
8266 }
8267
8268 #[test]
8270 fn intercard_counts_and_stops_at_its_limit() {
8271 let mut f = Fixture::new();
8272 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8273 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8274 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8275 assert_eq!(
8277 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8278 ":2\r\n"
8279 );
8280 assert_eq!(
8281 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8282 ":1\r\n"
8283 );
8284 let bad = "-ERR LIMIT can't be negative\r\n";
8287 assert_eq!(
8288 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8289 bad
8290 );
8291 assert_eq!(
8292 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8293 bad
8294 );
8295 for cmd in [
8296 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8297 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8298 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8299 ] {
8300 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8301 }
8302 }
8303
8304 #[test]
8306 fn a_draw_answers_one_member_or_an_array_of_them() {
8307 let mut f = Fixture::new();
8308 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8309 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8312 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8313 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8314 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8315 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8318 assert!(all.starts_with("*3\r\n"), "{all}");
8319 for m in ["a", "b", "c"] {
8320 assert!(all.contains(m), "{all}");
8321 }
8322 assert!(
8325 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8326 "five draws with replacement"
8327 );
8328 assert!(
8329 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8330 .starts_with("*4\r\n"),
8331 "two pairs, flat on RESP2"
8332 );
8333 f.out = Out::new(Proto::Resp3);
8334 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8335 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8336 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8337 f.out = Out::new(Proto::Resp2);
8338 assert_eq!(
8339 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8340 "-ERR syntax error\r\n"
8341 );
8342 assert_eq!(
8343 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8344 "-ERR value is not an integer or out of range\r\n"
8345 );
8346 }
8347
8348 #[test]
8350 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8351 let mut f = Fixture::new();
8352 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8353 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";
8354 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8355 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8356 assert_eq!(
8357 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8358 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8359 );
8360 assert_eq!(
8361 f.run(&[b"ZSCAN", b"nokey", b"0"]),
8362 "*2\r\n$1\r\n0\r\n*0\r\n"
8363 );
8364 f.out = Out::new(Proto::Resp3);
8367 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8368 f.out = Out::new(Proto::Resp2);
8369 assert_eq!(
8370 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8371 "-ERR NOVALUES option can only be used in HSCAN\r\n"
8372 );
8373 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8374 assert_eq!(
8375 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8376 "-ERR syntax error\r\n"
8377 );
8378 }
8379
8380 #[test]
8382 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8383 let mut f = Fixture::new();
8384 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8385 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8387 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8388 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8389 assert_eq!(
8391 f.run(&[b"ZPOPMIN", b"z", b"2"]),
8392 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8393 );
8394 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8397 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8398 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8399 assert_eq!(
8401 f.run(&[b"ZPOPMIN", b"z", b"9"]),
8402 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8403 );
8404 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8405
8406 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8407 f.out = Out::new(Proto::Resp3);
8408 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8409 assert_eq!(
8410 f.run(&[b"ZPOPMIN", b"z", b"1"]),
8411 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8412 );
8413 f.out = Out::new(Proto::Resp2);
8414 let bad = "-ERR value is out of range, must be positive\r\n";
8417 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8418 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8419 assert_eq!(
8420 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8421 "-ERR syntax error\r\n"
8422 );
8423 }
8424
8425 #[test]
8427 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8428 let mut f = Fixture::new();
8429 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8430 assert_eq!(
8431 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8432 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8433 );
8434 assert_eq!(
8437 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8438 "*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"
8439 );
8440 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8442 f.out = Out::new(Proto::Resp3);
8443 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8444 f.out = Out::new(Proto::Resp2);
8445 let numkeys = "-ERR numkeys should be greater than 0\r\n";
8446 for bad in [
8447 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8448 &[b"ZMPOP", b"-1", b"z", b"MIN"],
8449 &[b"ZMPOP", b"x", b"z", b"MIN"],
8450 ] {
8451 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8452 }
8453 let count = "-ERR count should be greater than 0\r\n";
8454 for bad in [
8455 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8456 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8457 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8458 ] {
8459 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8460 }
8461 let syntax = "-ERR syntax error\r\n";
8462 for bad in [
8463 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8466 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8467 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8468 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8469 ] {
8470 assert_eq!(f.run(bad), syntax, "{bad:?}");
8471 }
8472 }
8473
8474 #[test]
8477 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8478 let mut f = Fixture::new();
8479 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8480 assert_eq!(
8481 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8482 (
8483 Flow::Continue,
8484 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8485 )
8486 );
8487 assert_eq!(
8488 f.run(&[b"BZPOPMAX", b"z", b"0"]),
8489 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8490 );
8491 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8492 assert_eq!(
8493 f.run(&[
8494 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8495 ]),
8496 "*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"
8497 );
8498 f.out = Out::new(Proto::Resp3);
8499 assert_eq!(
8500 f.run(&[b"BZPOPMIN", b"z", b"0"]),
8501 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8502 );
8503 f.out = Out::new(Proto::Resp2);
8504 assert_eq!(
8506 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8507 (Flow::Block, String::new())
8508 );
8509 assert_eq!(
8510 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
8511 (Flow::Block, String::new())
8512 );
8513 assert_eq!(
8516 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
8517 "-ERR timeout is not a float or out of range\r\n"
8518 );
8519 assert_eq!(
8520 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
8521 "-ERR numkeys should be greater than 0\r\n"
8522 );
8523 assert_eq!(
8524 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
8525 "-ERR timeout is negative\r\n"
8526 );
8527 }
8528
8529 #[test]
8533 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
8534 let mut f = Fixture::new();
8535 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
8536 assert_eq!(f.server.waiters().len(), 1);
8537 f.run(&[b"SET", b"z", b"v"]);
8540 let mut out = Out::new(Proto::Resp2);
8541 assert!(!f.server.serve_waiter(0, 0, &mut out));
8542 assert!(out.as_slice().is_empty());
8543 f.run(&[b"DEL", b"z"]);
8544 f.run(&[b"ZADD", b"z", b"5", b"m"]);
8545 assert!(f.server.serve_waiter(0, 0, &mut out));
8546 assert_eq!(
8547 core::str::from_utf8(out.as_slice()).expect("ascii"),
8548 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
8549 );
8550 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8553 }
8554
8555 #[test]
8556 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
8557 let mut f = Fixture::new();
8558 f.run(&[b"SET", b"s", b"v"]);
8559 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8560 for cmd in [
8561 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
8562 &[b"ZINCRBY", b"s", b"1", b"a"],
8563 &[b"ZCARD", b"s"],
8564 &[b"ZSCORE", b"s", b"a"],
8565 &[b"ZMSCORE", b"s", b"a"],
8566 &[b"ZREM", b"s", b"a"],
8567 &[b"ZRANK", b"s", b"a"],
8568 &[b"ZREVRANK", b"s", b"a"],
8569 &[b"ZCOUNT", b"s", b"1", b"2"],
8570 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
8571 &[b"ZRANGE", b"s", b"0", b"-1"],
8572 &[b"ZREVRANGE", b"s", b"0", b"-1"],
8573 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
8574 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
8575 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
8576 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
8577 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
8578 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
8579 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
8580 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
8581 &[b"ZUNION", b"1", b"s"],
8582 &[b"ZINTER", b"1", b"s"],
8583 &[b"ZDIFF", b"1", b"s"],
8584 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
8585 &[b"ZINTERSTORE", b"d", b"1", b"s"],
8586 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
8587 &[b"ZINTERCARD", b"1", b"s"],
8588 &[b"ZRANDMEMBER", b"s"],
8589 &[b"ZSCAN", b"s", b"0"],
8590 &[b"ZPOPMIN", b"s"],
8591 &[b"ZPOPMAX", b"s", b"2"],
8592 &[b"ZMPOP", b"1", b"s", b"MIN"],
8593 &[b"BZPOPMIN", b"s", b"0"],
8594 &[b"BZPOPMAX", b"s", b"0"],
8595 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
8596 ] {
8597 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
8598 }
8599 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
8600 }
8601
8602 #[test]
8606 fn churning_sorted_sets_does_not_grow_the_server() {
8607 let mut f = Fixture::new();
8608 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
8609 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
8610 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
8611 for i in 0..200 {
8612 args.push(&scores[i]);
8613 args.push(&members[i]);
8614 }
8615
8616 f.run(&args);
8617 f.run(&[b"DEL", b"z"]);
8618 f.server.compact_step();
8619 let after_first = f.server.memory_bytes();
8620
8621 for _ in 0..200 {
8622 f.run(&args);
8623 f.run(&[b"DEL", b"z"]);
8624 f.server.compact_step();
8625 }
8626 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8627 assert!(
8628 f.server.memory_bytes() <= after_first * 2,
8629 "held {} after two hundred passes against {after_first} after one",
8630 f.server.memory_bytes()
8631 );
8632 }
8633
8634 fn sicily(f: &mut Fixture) {
8642 f.run(&[
8643 b"GEOADD",
8644 b"Sicily",
8645 b"13.361389",
8646 b"38.115556",
8647 b"Palermo",
8648 b"15.087269",
8649 b"37.502669",
8650 b"Catania",
8651 ]);
8652 f.run(&[
8653 b"GEOADD",
8654 b"Sicily",
8655 b"13.583333",
8656 b"37.316667",
8657 b"Agrigento",
8658 ]);
8659 }
8660
8661 #[test]
8662 fn places_go_in_as_scores_and_come_back_as_positions() {
8663 let mut f = Fixture::new();
8664 assert_eq!(
8665 f.run(&[
8666 b"GEOADD",
8667 b"Sicily",
8668 b"13.361389",
8669 b"38.115556",
8670 b"Palermo",
8671 b"15.087269",
8672 b"37.502669",
8673 b"Catania"
8674 ]),
8675 ":2\r\n"
8676 );
8677 assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
8681 assert_eq!(
8682 f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
8683 "$16\r\n3479099956230698\r\n"
8684 );
8685 assert_eq!(
8686 f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
8687 "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
8688 );
8689 assert_eq!(
8690 f.run(&[
8691 b"GEOHASH",
8692 b"Sicily",
8693 b"Palermo",
8694 b"Catania",
8695 b"NonExisting"
8696 ]),
8697 "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
8698 );
8699 assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
8703 assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
8704 }
8705
8706 #[test]
8707 fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
8708 let mut f = Fixture::new();
8709 sicily(&mut f);
8710 assert_eq!(
8711 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
8712 "$11\r\n166274.1516\r\n"
8713 );
8714 assert_eq!(
8715 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
8716 "$8\r\n166.2742\r\n"
8717 );
8718 assert_eq!(
8719 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
8720 "$8\r\n103.3182\r\n"
8721 );
8722 assert_eq!(
8726 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
8727 "$-1\r\n"
8728 );
8729 assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
8730 assert_eq!(
8731 f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
8732 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
8733 );
8734 assert_eq!(
8735 f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
8736 "-ERR syntax error\r\n"
8737 );
8738 }
8739
8740 #[test]
8741 fn a_search_finds_what_is_inside_it_nearest_first() {
8742 let mut f = Fixture::new();
8743 sicily(&mut f);
8744 let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
8745 assert_eq!(
8746 f.run(&[
8747 b"GEOSEARCH",
8748 b"Sicily",
8749 b"FROMLONLAT",
8750 b"15",
8751 b"37",
8752 b"BYRADIUS",
8753 b"200",
8754 b"km",
8755 b"ASC"
8756 ]),
8757 all
8758 );
8759 assert_eq!(
8762 f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
8763 all
8764 );
8765 assert_eq!(
8766 f.run(&[
8767 b"GEORADIUS_RO",
8768 b"Sicily",
8769 b"15",
8770 b"37",
8771 b"200",
8772 b"km",
8773 b"ASC"
8774 ]),
8775 all
8776 );
8777 assert_eq!(
8780 f.run(&[
8781 b"GEORADIUS",
8782 b"Sicily",
8783 b"15",
8784 b"37",
8785 b"200",
8786 b"km",
8787 b"DESC",
8788 b"COUNT",
8789 b"1"
8790 ]),
8791 "*1\r\n$7\r\nPalermo\r\n"
8792 );
8793 assert_eq!(
8794 f.run(&[
8795 b"GEORADIUS",
8796 b"Sicily",
8797 b"15",
8798 b"37",
8799 b"200",
8800 b"km",
8801 b"COUNT",
8802 b"1"
8803 ]),
8804 "*1\r\n$7\r\nCatania\r\n"
8805 );
8806 let empty = "*0\r\n";
8809 assert_eq!(
8810 f.run(&[
8811 b"GEOSEARCH",
8812 b"Sicily",
8813 b"FROMLONLAT",
8814 b"15",
8815 b"37",
8816 b"BYRADIUS",
8817 b"1",
8818 b"km"
8819 ]),
8820 empty
8821 );
8822 assert_eq!(
8823 f.run(&[
8824 b"GEOSEARCH",
8825 b"nokey",
8826 b"FROMLONLAT",
8827 b"15",
8828 b"37",
8829 b"BYRADIUS",
8830 b"1",
8831 b"km"
8832 ]),
8833 empty
8834 );
8835 assert_eq!(
8836 f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
8837 empty
8838 );
8839 }
8840
8841 #[test]
8842 fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
8843 let mut f = Fixture::new();
8844 sicily(&mut f);
8845 assert_eq!(
8846 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
8847 "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
8848 );
8849 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";
8852 assert_eq!(
8853 f.run(&[
8854 b"GEORADIUSBYMEMBER_RO",
8855 b"Sicily",
8856 b"Agrigento",
8857 b"100",
8858 b"km",
8859 b"WITHDIST"
8860 ]),
8861 with_dist
8862 );
8863 assert_eq!(
8864 f.run(&[
8865 b"GEOSEARCH",
8866 b"Sicily",
8867 b"FROMMEMBER",
8868 b"Agrigento",
8869 b"BYRADIUS",
8870 b"100",
8871 b"km",
8872 b"ASC",
8873 b"WITHDIST"
8874 ]),
8875 with_dist
8876 );
8877 assert_eq!(
8878 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
8879 "-ERR could not decode requested zset member\r\n"
8880 );
8881 }
8882
8883 #[test]
8884 fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
8885 let mut f = Fixture::new();
8886 sicily(&mut f);
8887 assert_eq!(
8891 f.run(&[
8892 b"GEOSEARCH",
8893 b"Sicily",
8894 b"FROMLONLAT",
8895 b"15",
8896 b"37",
8897 b"BYBOX",
8898 b"400",
8899 b"400",
8900 b"km",
8901 b"ASC",
8902 b"WITHCOORD",
8903 b"WITHDIST",
8904 b"WITHHASH"
8905 ]),
8906 "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
8907 $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
8908 *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
8909 $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
8910 *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
8911 $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
8912 );
8913 }
8914
8915 #[test]
8916 fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
8917 let mut f = Fixture::new();
8918 sicily(&mut f);
8919 let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
8920 $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
8921 $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
8922 assert_eq!(
8923 f.run(&[
8924 b"GEOSEARCHSTORE",
8925 b"dst",
8926 b"Sicily",
8927 b"FROMLONLAT",
8928 b"15",
8929 b"37",
8930 b"BYRADIUS",
8931 b"200",
8932 b"km",
8933 b"ASC"
8934 ]),
8935 ":3\r\n"
8936 );
8937 assert_eq!(
8938 f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
8939 hashes
8940 );
8941 assert_eq!(
8944 f.run(&[
8945 b"GEORADIUS",
8946 b"Sicily",
8947 b"15",
8948 b"37",
8949 b"200",
8950 b"km",
8951 b"STORE",
8952 b"dst3"
8953 ]),
8954 ":3\r\n"
8955 );
8956 assert_eq!(
8957 f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
8958 hashes
8959 );
8960 assert_eq!(
8969 f.run(&[
8970 b"GEOSEARCHSTORE",
8971 b"dst2",
8972 b"Sicily",
8973 b"FROMLONLAT",
8974 b"15",
8975 b"37",
8976 b"BYRADIUS",
8977 b"200",
8978 b"km",
8979 b"ASC",
8980 b"STOREDIST"
8981 ]),
8982 ":3\r\n"
8983 );
8984 for (member, want) in [
8985 ("Catania", 56.441_257_870_158_19),
8986 ("Agrigento", 130.423_487_067_147_14),
8987 ("Palermo", 190.442_429_847_757_92),
8988 ] {
8989 let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
8990 let got: f64 = reply
8991 .trim_start_matches(|c: char| c != '\n')
8992 .trim()
8993 .parse()
8994 .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
8995 assert!(
8996 (got - want).abs() < 1e-9,
8997 "{member} scored {got} not {want}"
8998 );
8999 }
9000 assert_eq!(
9003 f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9004 "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9005 );
9006 assert_eq!(
9010 f.run(&[
9011 b"GEOSEARCHSTORE",
9012 b"dst",
9013 b"nokey",
9014 b"FROMLONLAT",
9015 b"15",
9016 b"37",
9017 b"BYRADIUS",
9018 b"200",
9019 b"km"
9020 ]),
9021 ":0\r\n"
9022 );
9023 assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9024 }
9025
9026 #[test]
9027 fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9028 let mut f = Fixture::new();
9029 sicily(&mut f);
9030 assert_eq!(
9033 f.run(&[
9034 b"GEOADD",
9035 b"Sicily",
9036 b"XX",
9037 b"CH",
9038 b"13.361389",
9039 b"38.115556",
9040 b"Palermo"
9041 ]),
9042 ":0\r\n"
9043 );
9044 assert_eq!(
9045 f.run(&[
9046 b"GEOADD",
9047 b"Sicily",
9048 b"NX",
9049 b"13.361389",
9050 b"38.9",
9051 b"Palermo"
9052 ]),
9053 ":0\r\n"
9054 );
9055 assert_eq!(
9056 f.run(&[
9057 b"GEOADD",
9058 b"Sicily",
9059 b"CH",
9060 b"13.361389",
9061 b"38.9",
9062 b"Palermo"
9063 ]),
9064 ":1\r\n"
9065 );
9066 assert_eq!(
9069 f.run(&[
9070 b"GEOADD",
9071 b"new",
9072 b"13.361389",
9073 b"38.115556",
9074 b"here",
9075 b"181",
9076 b"38",
9077 b"there"
9078 ]),
9079 "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9080 );
9081 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9082 assert_eq!(
9083 f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9084 "-ERR value is not a valid float\r\n"
9085 );
9086 assert_eq!(
9089 f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9090 "-ERR syntax error\r\n"
9091 );
9092 assert_eq!(
9093 f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9094 "-ERR syntax error\r\n"
9095 );
9096 assert_eq!(
9097 f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9098 "-ERR syntax error\r\n"
9099 );
9100 assert_eq!(
9101 f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9102 "-ERR wrong number of arguments for 'geoadd' command\r\n"
9103 );
9104 }
9105
9106 #[test]
9109 fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9110 let mut f = Fixture::new();
9111 sicily(&mut f);
9112 let cases: &[(&[&[u8]], &str)] = &[
9113 (
9114 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9115 "-ERR need numeric radius\r\n",
9116 ),
9117 (
9118 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9119 "-ERR radius cannot be negative\r\n",
9120 ),
9121 (
9122 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9123 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9124 ),
9125 (
9126 &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9127 "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9128 ),
9129 (
9130 &[
9131 b"GEOSEARCH",
9132 b"Sicily",
9133 b"FROMLONLAT",
9134 b"15",
9135 b"37",
9136 b"BYBOX",
9137 b"x",
9138 b"1",
9139 b"km",
9140 ],
9141 "-ERR need numeric width\r\n",
9142 ),
9143 (
9144 &[
9145 b"GEOSEARCH",
9146 b"Sicily",
9147 b"FROMLONLAT",
9148 b"15",
9149 b"37",
9150 b"BYBOX",
9151 b"1",
9152 b"y",
9153 b"km",
9154 ],
9155 "-ERR need numeric height\r\n",
9156 ),
9157 (
9158 &[
9159 b"GEOSEARCH",
9160 b"Sicily",
9161 b"FROMLONLAT",
9162 b"15",
9163 b"37",
9164 b"BYBOX",
9165 b"-1",
9166 b"1",
9167 b"km",
9168 ],
9169 "-ERR height or width cannot be negative\r\n",
9170 ),
9171 (
9172 &[
9173 b"GEOSEARCH",
9174 b"Sicily",
9175 b"FROMLONLAT",
9176 b"15",
9177 b"37",
9178 b"BYRADIUS",
9179 b"1",
9180 b"km",
9181 b"ANY",
9182 ],
9183 "-ERR the ANY argument requires COUNT argument\r\n",
9184 ),
9185 (
9186 &[
9187 b"GEOSEARCH",
9188 b"Sicily",
9189 b"FROMLONLAT",
9190 b"15",
9191 b"37",
9192 b"BYRADIUS",
9193 b"1",
9194 b"km",
9195 b"COUNT",
9196 b"0",
9197 ],
9198 "-ERR COUNT must be > 0\r\n",
9199 ),
9200 (
9201 &[
9202 b"GEOSEARCH",
9203 b"Sicily",
9204 b"BYRADIUS",
9205 b"1",
9206 b"km",
9207 b"BYBOX",
9208 b"1",
9209 b"1",
9210 b"km",
9211 ],
9212 "-ERR syntax error\r\n",
9213 ),
9214 (
9215 &[
9216 b"GEOSEARCH",
9217 b"Sicily",
9218 b"FROMMEMBER",
9219 b"Palermo",
9220 b"FROMLONLAT",
9221 b"1",
9222 b"2",
9223 b"BYRADIUS",
9224 b"1",
9225 b"km",
9226 ],
9227 "-ERR syntax error\r\n",
9228 ),
9229 (
9232 &[
9233 b"geosearch",
9234 b"Sicily",
9235 b"BYRADIUS",
9236 b"1",
9237 b"km",
9238 b"ASC",
9239 b"WITHDIST",
9240 ],
9241 "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9242 ),
9243 (
9244 &[
9245 b"GEOSEARCH",
9246 b"Sicily",
9247 b"FROMLONLAT",
9248 b"15",
9249 b"37",
9250 b"ASC",
9251 b"WITHDIST",
9252 ],
9253 "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9254 ),
9255 (
9258 &[
9259 b"GEOSEARCHSTORE",
9260 b"d",
9261 b"Sicily",
9262 b"FROMLONLAT",
9263 b"15",
9264 b"37",
9265 b"BYRADIUS",
9266 b"1",
9267 b"km",
9268 b"WITHCOORD",
9269 ],
9270 "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9271 ),
9272 (
9273 &[
9274 b"GEORADIUS",
9275 b"Sicily",
9276 b"15",
9277 b"37",
9278 b"1",
9279 b"km",
9280 b"WITHDIST",
9281 b"STORE",
9282 b"d",
9283 ],
9284 "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9285 ),
9286 (
9289 &[
9290 b"GEORADIUS_RO",
9291 b"Sicily",
9292 b"15",
9293 b"37",
9294 b"1",
9295 b"km",
9296 b"STORE",
9297 b"d",
9298 ],
9299 "-ERR syntax error\r\n",
9300 ),
9301 (
9302 &[
9303 b"GEOSEARCH",
9304 b"Sicily",
9305 b"FROMLONLAT",
9306 b"15",
9307 b"37",
9308 b"BYRADIUS",
9309 b"1",
9310 b"km",
9311 b"STOREDIST",
9312 ],
9313 "-ERR syntax error\r\n",
9314 ),
9315 ];
9316 for (parts, want) in cases {
9317 assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9318 }
9319 }
9320
9321 #[test]
9324 fn every_geo_command_says_wrongtype() {
9325 let mut f = Fixture::new();
9326 f.run(&[b"SET", b"s", b"v"]);
9327 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9328 let cases: &[&[&[u8]]] = &[
9329 &[b"GEOADD", b"s", b"13", b"38", b"m"],
9330 &[b"GEOPOS", b"s", b"m"],
9331 &[b"GEOHASH", b"s", b"m"],
9332 &[b"GEODIST", b"s", b"a", b"b"],
9333 &[
9334 b"GEOSEARCH",
9335 b"s",
9336 b"FROMLONLAT",
9337 b"15",
9338 b"37",
9339 b"BYRADIUS",
9340 b"1",
9341 b"km",
9342 ],
9343 &[
9344 b"GEOSEARCHSTORE",
9345 b"d",
9346 b"s",
9347 b"FROMLONLAT",
9348 b"15",
9349 b"37",
9350 b"BYRADIUS",
9351 b"1",
9352 b"km",
9353 ],
9354 &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9355 &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9356 &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9357 &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9358 ];
9359 for case in cases {
9360 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9361 }
9362 assert_eq!(
9365 f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9366 wrong
9367 );
9368 }
9369
9370 #[test]
9373 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9374 let mut f = Fixture::new();
9375 assert_eq!(
9378 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9379 ":3\r\n"
9380 );
9381 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9382 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9383 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9384 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9386 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9387 assert_eq!(
9388 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9389 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9390 );
9391 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9393 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9394 }
9395
9396 #[test]
9399 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9400 let mut f = Fixture::new();
9401 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9402 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9403 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9404 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9405 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9406 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9408 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9409 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9410
9411 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9415 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9416 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9417 assert_eq!(
9420 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9421 "-ERR array index overflow\r\n"
9422 );
9423 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9424 }
9425
9426 #[test]
9429 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9430 let mut f = Fixture::new();
9431 f.run(&[b"ARSET", b"a", b"1", b"x"]);
9432 assert_eq!(
9433 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9434 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9435 );
9436 assert_eq!(
9439 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9440 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9441 );
9442 assert_eq!(
9444 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9445 "*2\r\n$-1\r\n$-1\r\n"
9446 );
9447 assert_eq!(
9451 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9452 "-ERR range exceeds maximum of 1000000 items\r\n"
9453 );
9454 }
9455
9456 #[test]
9459 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9460 let mut f = Fixture::new();
9461 assert_eq!(
9462 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9463 "-ERR invalid array index\r\n"
9464 );
9465 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9466 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9467 assert_eq!(
9468 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9469 "-ERR invalid array index\r\n"
9470 );
9471 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9472 assert_eq!(
9475 f.run(&[b"ARGET", b"a", b"-1"]),
9476 "-ERR invalid array index\r\n"
9477 );
9478 assert_eq!(
9481 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9482 "-ERR wrong number of arguments for 'armset' command\r\n"
9483 );
9484 assert_eq!(
9485 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9486 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9487 );
9488 }
9489
9490 #[test]
9491 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9492 let mut f = Fixture::new();
9493 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9494 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9495 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9496 assert_eq!(
9499 f.run(&[
9500 b"ARDELRANGE",
9501 b"a",
9502 b"100",
9503 b"200",
9504 b"0",
9505 b"18446744073709551614"
9506 ]),
9507 ":2\r\n"
9508 );
9509 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9510 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
9511 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
9512 }
9513
9514 #[test]
9517 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
9518 let mut f = Fixture::new();
9519 let long = vec![b'v'; 200];
9520 f.run(&[
9521 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
9522 b"short", b"5", &long, b"6", b"-0",
9523 ]);
9524 assert_eq!(
9528 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
9529 format!(
9530 "*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",
9531 String::from_utf8_lossy(&long)
9532 )
9533 );
9534 }
9535
9536 #[test]
9537 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
9538 let mut f = Fixture::new();
9539 f.run(&[b"ARSET", b"a", b"0", b"x"]);
9540 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
9541 assert_eq!(
9542 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
9543 "$12\r\nsliced-array\r\n"
9544 );
9545 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
9547 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
9548 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
9549 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
9550 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
9551 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
9552 }
9553
9554 #[test]
9555 fn every_array_command_refuses_a_key_holding_something_else() {
9556 let mut f = Fixture::new();
9557 f.run(&[b"SET", b"s", b"v"]);
9558 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9559 for cmd in [
9560 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
9561 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
9562 &[b"ARGET".as_ref(), b"s", b"0"][..],
9563 &[b"ARMGET".as_ref(), b"s", b"0"][..],
9564 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
9565 &[b"ARLEN".as_ref(), b"s"][..],
9566 &[b"ARCOUNT".as_ref(), b"s"][..],
9567 &[b"ARDEL".as_ref(), b"s", b"0"][..],
9568 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
9569 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
9570 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
9571 &[b"ARNEXT".as_ref(), b"s"][..],
9572 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
9573 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
9574 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
9575 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
9576 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
9577 &[b"ARINFO".as_ref(), b"s"][..],
9578 ] {
9579 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
9580 }
9581 }
9582
9583 #[test]
9587 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
9588 let mut f = Fixture::new();
9589 f.run(&[b"SET", b"s", b"v"]);
9590 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9591 let bad = "-ERR invalid array index\r\n";
9592 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
9593 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
9594 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
9595 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
9596 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
9597 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
9598 f.run(&[b"ARSET", b"a", b"0", b"x"]);
9600 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
9601 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
9602 }
9603
9604 #[test]
9605 fn an_append_follows_a_cursor_the_client_can_move() {
9606 let mut f = Fixture::new();
9607 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
9608 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
9609 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
9610 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
9611 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
9612
9613 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
9616 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
9617 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
9618 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
9619 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
9620 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
9621 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
9622
9623 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
9626 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
9627 assert_eq!(
9628 f.run(&[b"ARINSERT", b"a", b"x"]),
9629 "-ERR insert index overflow\r\n"
9630 );
9631 assert_eq!(
9632 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
9633 "-ERR invalid array index\r\n"
9634 );
9635 }
9636
9637 #[test]
9638 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
9639 let mut f = Fixture::new();
9640 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
9641 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
9642 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
9643 assert_eq!(
9644 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
9645 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
9646 );
9647 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
9650 assert_eq!(
9651 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
9652 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
9653 );
9654 assert_eq!(
9657 f.run(&[b"ARRING", b"r", b"0", b"x"]),
9658 "-ERR size must be positive\r\n"
9659 );
9660 assert_eq!(
9661 f.run(&[b"ARRING", b"r", b"big", b"x"]),
9662 "-ERR invalid size\r\n"
9663 );
9664 }
9665
9666 #[test]
9667 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
9668 let mut f = Fixture::new();
9669 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
9670 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
9671 assert_eq!(
9672 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
9673 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
9674 );
9675 assert_eq!(
9676 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
9677 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
9678 );
9679 assert_eq!(
9680 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
9681 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
9682 "more than there is gets what there is"
9683 );
9684 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
9687 assert_eq!(
9688 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
9689 "-ERR syntax error\r\n"
9690 );
9691 assert_eq!(
9692 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
9693 "-ERR invalid COUNT\r\n"
9694 );
9695
9696 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
9699 assert_eq!(
9700 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
9701 "*2\r\n$-1\r\n$1\r\nz\r\n"
9702 );
9703 }
9704
9705 #[test]
9706 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
9707 let mut f = Fixture::new();
9708 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
9709 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
9710 assert_eq!(
9713 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
9714 "*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"
9715 );
9716 assert_eq!(
9717 f.run(&[
9718 b"ARSCAN",
9719 b"a",
9720 b"18446744073709551614",
9721 b"0",
9722 b"LIMIT",
9723 b"1"
9724 ]),
9725 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
9726 );
9727 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
9728 assert_eq!(
9729 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
9730 "-ERR LIMIT must be positive\r\n"
9731 );
9732 assert_eq!(
9733 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
9734 "-ERR syntax error\r\n"
9735 );
9736 assert_eq!(
9737 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
9738 "-ERR wrong number of arguments for 'arscan' command\r\n"
9739 );
9740 }
9741
9742 #[test]
9743 fn a_grep_answers_the_indexes_whose_elements_match() {
9744 let mut f = Fixture::new();
9745 assert_eq!(
9746 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
9747 "*0\r\n"
9748 );
9749 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
9750
9751 assert_eq!(
9754 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
9755 "*3\r\n:0\r\n:1\r\n:2\r\n"
9756 );
9757 assert_eq!(
9758 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
9759 "*3\r\n:2\r\n:1\r\n:0\r\n"
9760 );
9761 assert_eq!(
9762 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
9763 "*2\r\n:1\r\n:2\r\n"
9764 );
9765
9766 assert_eq!(
9769 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
9770 "*1\r\n:0\r\n"
9771 );
9772 assert_eq!(
9773 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
9774 "*2\r\n:0\r\n:3\r\n"
9775 );
9776 assert_eq!(
9777 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
9778 "*1\r\n:2\r\n"
9779 );
9780 assert_eq!(
9781 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
9782 "*2\r\n:1\r\n:2\r\n"
9783 );
9784
9785 let both: &[&[u8]] = &[
9788 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
9789 ];
9790 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
9791 assert_eq!(
9792 f.run(&[
9793 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
9794 ]),
9795 "*0\r\n"
9796 );
9797 assert_eq!(
9798 f.run(&[
9799 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
9800 ]),
9801 "*2\r\n:0\r\n:1\r\n"
9802 );
9803
9804 assert_eq!(
9807 f.run(&[
9808 b"ARGREP",
9809 b"a",
9810 b"-",
9811 b"+",
9812 b"MATCH",
9813 b"a",
9814 b"WITHVALUES",
9815 b"LIMIT",
9816 b"2"
9817 ]),
9818 "*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"
9819 );
9820 assert_eq!(
9821 f.run(&[
9822 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
9823 ]),
9824 "*1\r\n:3\r\n"
9825 );
9826 }
9827
9828 #[test]
9830 fn a_grep_reports_a_broken_command_the_way_redis_does() {
9831 let mut f = Fixture::new();
9832 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
9833 let syntax = "-ERR syntax error\r\n";
9834
9835 assert_eq!(
9838 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
9839 "-ERR invalid array index\r\n"
9840 );
9841 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
9842 assert_eq!(
9844 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
9845 syntax
9846 );
9847 assert_eq!(
9848 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
9849 syntax
9850 );
9851 assert_eq!(
9852 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
9853 syntax,
9854 "a command with no predicate in it at all"
9855 );
9856 assert_eq!(
9857 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
9858 "-ERR LIMIT must be positive\r\n"
9859 );
9860 assert_eq!(
9861 f.run(&[
9862 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
9863 ]),
9864 "-ERR value is not an integer or out of range\r\n"
9865 );
9866 assert_eq!(
9867 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
9868 "-ERR regular expression is empty\r\n"
9869 );
9870 assert_eq!(
9871 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
9872 "-ERR invalid regular expression: Missing ')'\r\n"
9873 );
9874 assert_eq!(
9875 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
9876 "-ERR regular expression backreferences are not supported\r\n"
9877 );
9878 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
9881 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
9882 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
9883 }
9884
9885 #[test]
9886 fn an_op_reduces_a_range_to_one_number() {
9887 let mut f = Fixture::new();
9888 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
9889 assert_eq!(
9890 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
9891 "$4\r\n-0.5\r\n"
9892 );
9893 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
9894 assert_eq!(
9895 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
9896 "$3\r\n2.5\r\n"
9897 );
9898 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
9899 assert_eq!(
9900 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
9901 ":1\r\n"
9902 );
9903 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
9906 assert_eq!(
9907 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
9908 "$19\r\n0.30000000000000004\r\n"
9909 );
9910 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
9911 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
9912
9913 f.run(&[b"ARSET", b"w", b"0", b"word"]);
9916 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
9917 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
9918 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
9919
9920 assert_eq!(
9921 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
9922 "-ERR unknown operation\r\n"
9923 );
9924 assert_eq!(
9925 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
9926 "-ERR MATCH requires a value argument\r\n"
9927 );
9928 assert_eq!(
9929 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
9930 "-ERR wrong number of arguments for 'arop' command\r\n"
9931 );
9932 }
9933
9934 #[test]
9935 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
9936 let mut f = Fixture::new();
9937 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
9938 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
9939 let short = f.run(&[b"ARINFO", b"a"]);
9940 assert!(
9941 short.starts_with("*14\r\n"),
9942 "seven pairs on RESP2: {short}"
9943 );
9944 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
9945 assert!(
9946 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
9947 "{short}"
9948 );
9949 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
9950 let full = f.run(&[b"ARINFO", b"a", b"full"]);
9951 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
9952 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
9955 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
9956 assert!(
9957 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
9958 "{full}"
9959 );
9960 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
9961
9962 let mut g = Fixture::new();
9964 g.run(&[b"HELLO", b"3"]);
9965 g.run(&[b"ARINSERT", b"a", b"x"]);
9966 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
9967 assert!(map.starts_with("%12\r\n"), "{map}");
9968 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
9969 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
9970 }
9971
9972 #[test]
9973 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
9974 let mut f = Fixture::new();
9975 for (score, want) in [
9978 ("3", "3"),
9979 ("3.5", "3.5"),
9980 ("0.3", "0.3"),
9981 ("1e30", "1e+30"),
9982 ("1e19", "1e+19"),
9983 ("1e-7", "1e-7"),
9984 ("0.000001", "0.000001"),
9985 ("4611686018427387904", "4611686018427387904"),
9986 ("-0", "-0"),
9987 ] {
9988 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
9989 assert_eq!(
9990 f.run(&[b"ZSCORE", b"z", b"m"]),
9991 format!("${}\r\n{want}\r\n", want.len()),
9992 "score {score}"
9993 );
9994 }
9995
9996 let mut g = Fixture::new();
9999 g.run(&[b"HELLO", b"3"]);
10000 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10001 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10002 assert_eq!(
10007 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10008 "$31\r\n1000000000000000000000000000000\r\n"
10009 );
10010 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10011 assert_eq!(
10012 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10013 "$20\r\n10000000000000000000\r\n"
10014 );
10015 }
10016
10017 #[test]
10020 fn a_node_comes_back_with_the_fields_it_went_in_with() {
10021 let mut f = Fixture::new();
10022 assert_eq!(
10023 f.run(&[
10024 b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10025 ]),
10026 ":1\r\n"
10027 );
10028 assert_eq!(
10034 f.run(&[b"G.NGET", b"social", b"ada"]),
10035 "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10036 );
10037 assert_eq!(
10040 f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10041 ":0\r\n"
10042 );
10043 assert_eq!(
10044 f.run(&[b"G.NGET", b"social", b"ada"]),
10045 "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10046 );
10047 assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10050 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10051 assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10052 assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10053
10054 assert_eq!(
10057 f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10058 "-ERR syntax error\r\n"
10059 );
10060 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10061
10062 let mut g = Fixture::new();
10064 g.run(&[b"HELLO", b"3"]);
10065 g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10066 assert_eq!(
10067 g.run(&[b"G.NGET", b"social", b"ada"]),
10068 "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10069 );
10070 }
10071
10072 #[test]
10073 fn an_edge_creates_the_ends_it_needs() {
10074 let mut f = Fixture::new();
10075 assert_eq!(
10076 f.run(&[
10077 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10078 ]),
10079 ":1\r\n"
10080 );
10081 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10083 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10084 assert_eq!(
10085 f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10086 "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10087 );
10088 assert_eq!(
10089 f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10090 "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10091 );
10092 assert_eq!(
10095 f.run(&[
10096 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10097 ]),
10098 ":0\r\n"
10099 );
10100 assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10101 assert_eq!(
10103 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10104 ":1\r\n"
10105 );
10106 assert_eq!(
10107 f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10108 ":1\r\n"
10109 );
10110
10111 assert_eq!(
10112 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10113 ":1\r\n"
10114 );
10115 assert_eq!(
10116 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10117 ":0\r\n"
10118 );
10119 assert_eq!(
10122 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10123 ":0\r\n"
10124 );
10125 assert_eq!(
10126 f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10127 ":0\r\n"
10128 );
10129 assert_eq!(
10130 f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10131 ":0\r\n"
10132 );
10133 }
10134
10135 #[test]
10138 fn a_hop_answers_a_cursor_and_a_page() {
10139 let mut f = Fixture::new();
10140 for i in 0..25u32 {
10141 let dst = format!("n{i}");
10142 f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10143 }
10144 let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10146 assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10147
10148 let mut seen = 0;
10149 let mut cursor = String::from("0");
10150 loop {
10151 let page = f.run(&[
10152 b"G.OUT",
10153 b"social",
10154 b"hub",
10155 b"FOLLOWS",
10156 b"COUNT",
10157 b"7",
10158 b"CURSOR",
10159 cursor.as_bytes(),
10160 ]);
10161 let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10162 cursor = head
10163 .rsplit("\r\n")
10164 .next()
10165 .expect("the cursor line")
10166 .to_string();
10167 seen += rest
10168 .split_once("\r\n")
10169 .expect("the page length")
10170 .0
10171 .parse::<usize>()
10172 .expect("a length");
10173 if cursor == "0" {
10174 break;
10175 }
10176 }
10177 assert_eq!(seen, 25, "every neighbour once across the pages");
10178
10179 assert_eq!(
10182 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10183 "*2\r\n$1\r\n0\r\n*0\r\n"
10184 );
10185 assert_eq!(
10186 f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10187 "*2\r\n$1\r\n0\r\n*0\r\n"
10188 );
10189 assert_eq!(
10190 f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10191 "*2\r\n$1\r\n0\r\n*0\r\n"
10192 );
10193 assert_eq!(
10194 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10195 "-ERR COUNT must be a positive integer\r\n"
10196 );
10197 assert_eq!(
10198 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10199 "-ERR syntax error\r\n"
10200 );
10201 }
10202
10203 #[test]
10204 fn a_degree_counts_one_way_or_both() {
10205 let mut f = Fixture::new();
10206 f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10207 f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10208 f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10209 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10210 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10211 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10212 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10213 assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10214 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10215 assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10216 assert_eq!(
10217 f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10218 "-ERR syntax error\r\n"
10219 );
10220 }
10221
10222 #[test]
10225 fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10226 let mut f = Fixture::new();
10227 for (src, dst) in [
10228 ("ada", "grace"),
10229 ("ada", "alan"),
10230 ("grace", "edsger"),
10231 ("alan", "edsger"),
10232 ("edsger", "barbara"),
10233 ] {
10234 f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10235 }
10236 assert_eq!(
10239 f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10240 "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10241 );
10242 assert_eq!(
10243 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10244 "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10245 );
10246 let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10247 assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10248 assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10249 assert_eq!(
10251 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10252 "*1\r\n$5\r\ngrace\r\n"
10253 );
10254 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10256 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10257 assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10258 assert_eq!(
10259 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10260 "-ERR DEPTH must be a positive integer\r\n"
10261 );
10262 assert_eq!(
10263 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10264 "-ERR syntax error\r\n"
10265 );
10266 }
10267
10268 #[test]
10271 fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10272 let mut f = Fixture::new();
10273 for i in 0..6u32 {
10276 let src = format!("n{i}");
10277 let dst = format!("n{}", i + 1);
10278 f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10279 }
10280 assert_eq!(
10281 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10282 "*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"
10283 );
10284 f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10285 assert_eq!(
10286 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10287 "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10288 );
10289 assert_eq!(
10292 f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10293 "*1\r\n$2\r\nn2\r\n"
10294 );
10295 assert_eq!(
10296 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10297 "*0\r\n"
10298 );
10299 assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10301 f.run(&[b"G.NADD", b"road", b"island"]);
10304 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10305 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10306 assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10307 assert_eq!(
10308 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10309 "-ERR syntax error\r\n"
10310 );
10311 }
10312
10313 #[test]
10317 fn the_keyspace_sees_a_graph_key_like_any_other() {
10318 let mut f = Fixture::new();
10319 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10320 assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10321 assert_eq!(
10322 f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10323 "$9\r\nadjacency\r\n"
10324 );
10325 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10326 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10327 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10328 let held = f.server.memory_bytes();
10332 for i in 0..200u32 {
10333 let dst = format!("n{i}");
10334 f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10335 }
10336 assert!(
10337 f.server.memory_bytes() > held,
10338 "two hundred edges cost something: {held} then {}",
10339 f.server.memory_bytes()
10340 );
10341 f.run(&[b"DEL", b"big"]);
10342
10343 assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10346 assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10347 assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10348 assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10349 assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10350 f.run(&[b"SELECT", b"1"]);
10351 assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10352
10353 assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10354 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10355 f.run(&[b"G.NADD", b"g", b"n"]);
10356 assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10357 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10358 }
10359
10360 #[test]
10363 fn a_graph_cannot_be_copied_or_dumped() {
10364 let mut f = Fixture::new();
10365 f.run(&[b"G.NADD", b"social", b"ada"]);
10366 assert_eq!(
10367 f.run(&[b"COPY", b"social", b"other"]),
10368 "-ERR COPY is not supported for a graph\r\n"
10369 );
10370 assert_eq!(
10371 f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10372 "-ERR COPY is not supported for a graph\r\n"
10373 );
10374 assert_eq!(
10375 f.run(&[b"DUMP", b"social"]),
10376 "-ERR DUMP is not supported for a graph\r\n"
10377 );
10378 assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10380 }
10381
10382 #[test]
10385 fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10386 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10387 let mut f = Fixture::new();
10388 f.run(&[b"G.NADD", b"social", b"ada"]);
10389 assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10390 assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10391 assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10392
10393 f.run(&[b"SET", b"str", b"v"]);
10394 for cmd in [
10395 vec![b"G.NADD".as_ref(), b"str", b"n"],
10396 vec![b"G.NGET".as_ref(), b"str", b"n"],
10397 vec![b"G.NDEL".as_ref(), b"str", b"n"],
10398 vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10399 vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10400 vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10401 vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10402 vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10403 vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10404 vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10405 ] {
10406 assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10407 }
10408 }
10409
10410 #[test]
10413 fn a_graph_goes_when_its_last_node_does() {
10414 let mut f = Fixture::new();
10415 f.run(&[
10416 b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10417 ]);
10418 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10419 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10421 assert_eq!(
10422 f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10423 ":0\r\n"
10424 );
10425 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10426 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10427
10428 assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10429 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10430 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10431 assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10432
10433 f.run(&[b"G.NADD", b"social", b"first"]);
10436 f.run(&[b"G.NADD", b"social", b"second"]);
10437 f.run(&[b"G.NDEL", b"social", b"first"]);
10438 f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10439 assert_eq!(
10440 f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10441 "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10442 );
10443 }
10444
10445 #[test]
10450 fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10451 let mut f = Fixture::new();
10452 let doc = br#"{"a":1,"b":{"c":true}}"#;
10453 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10454 assert_eq!(
10457 f.run(&[b"JSON.GET", b"doc"]),
10458 bulk(r#"{"a":1,"b":{"c":true}}"#)
10459 );
10460 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10461 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10462 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10463 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10466 assert_eq!(
10467 f.run(&[b"JSON.GET", b"doc", b".nope"]),
10468 "-ERR Path does not exist\r\n"
10469 );
10470 assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10471 assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10474 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10475 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10476 assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10477 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10478 }
10479
10480 #[test]
10486 fn the_two_json_errors_that_carry_no_prefix() {
10487 let mut f = Fixture::new();
10488 f.run(&[b"SET", b"plain", b"x"]);
10489 let wrong = "-Existing key has wrong Redis type\r\n";
10490 assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10491 assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10492 assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10493 assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10494 assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10495
10496 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10497 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10501 assert_eq!(
10502 f.run(&[b"JSON.GET", b"doc"]),
10503 bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10504 );
10505 assert_eq!(
10506 f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10507 "-Err wrong static path\r\n"
10508 );
10509 }
10510
10511 #[test]
10513 fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10514 let mut f = Fixture::new();
10515 assert_eq!(
10517 f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
10518 "-ERR new objects must be created at the root\r\n"
10519 );
10520 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
10521 assert_eq!(
10524 f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
10525 "-ERR new objects must be created at the root\r\n"
10526 );
10527 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
10528 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
10529
10530 f.run(&[
10531 b"JSON.SET",
10532 b"doc",
10533 b"$",
10534 br#"{"o":{},"arr":[1,2],"s":"x"}"#,
10535 ]);
10536 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10538 assert_eq!(
10541 f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
10542 "$-1\r\n"
10543 );
10544 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
10545 assert_eq!(
10547 f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
10548 "-ERR array index out of range\r\n"
10549 );
10550 assert_eq!(
10551 f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
10552 "-ERR array index out of range\r\n"
10553 );
10554 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
10555 assert_eq!(
10558 f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
10559 "$-1\r\n"
10560 );
10561 assert_eq!(
10562 f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
10563 "$-1\r\n"
10564 );
10565 assert_eq!(
10566 f.run(&[b"JSON.GET", b"doc"]),
10567 bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
10568 );
10569 assert!(
10573 f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
10574 .starts_with("-this is not the start of a value")
10575 );
10576 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
10577 }
10578
10579 #[test]
10582 fn the_json_commands_that_do_not_answer_text() {
10583 let mut f = Fixture::new();
10584 let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
10585 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10586
10587 assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
10588 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
10589 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
10590 assert_eq!(
10591 f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
10592 format!("*1\r\n{}", bulk("integer"))
10593 );
10594 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
10597 assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
10598
10599 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
10602 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
10603 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
10606 assert_eq!(
10607 f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
10608 "-ERR Path does not exist or not a bool\r\n"
10609 );
10610 assert_eq!(
10611 f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
10612 "-ERR Path does not exist or not a bool\r\n"
10613 );
10614 assert_eq!(
10615 f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
10616 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10617 );
10618
10619 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
10622 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
10623 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
10624 assert_eq!(
10625 f.run(&[b"JSON.GET", b"doc"]),
10626 bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
10627 );
10628
10629 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
10632 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
10633 assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
10638 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
10639 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10640 assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
10641 assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
10642 assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
10643 assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
10644 assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
10645 }
10646
10647 #[test]
10654 fn json_get_lays_out_the_wrapper_it_builds() {
10655 let mut f = Fixture::new();
10656 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
10657
10658 assert_eq!(
10659 f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
10660 bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
10661 );
10662 assert_eq!(
10664 f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
10665 bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
10666 );
10667 let fmt: &[&[u8]] = &[b"INDENT", b" ", b"NEWLINE", b"\n", b"SPACE", b" "];
10668 let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
10669 one.extend_from_slice(fmt);
10670 one.push(b"$.b");
10671 assert_eq!(
10672 f.run(&one),
10673 bulk("[\n [\n 1,\n {\n \"c\": 2\n }\n ]\n]")
10674 );
10675 let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
10676 two.extend_from_slice(fmt);
10677 two.push(b"$.a");
10678 two.push(b"$.nope");
10679 assert_eq!(
10680 f.run(&two),
10681 bulk("{\n \"$.a\": [\n 1\n ],\n \"$.nope\": []\n}")
10682 );
10683 let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
10686 root.push(b".a");
10687 assert_eq!(f.run(&root), bulk("1"));
10688 }
10689
10690 #[test]
10693 fn json_mget_answers_once_per_key_whatever_is_under_them() {
10694 let mut f = Fixture::new();
10695 f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
10696 f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
10697 f.run(&[b"SET", b"plain", b"x"]);
10698 assert_eq!(
10699 f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
10700 format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
10701 );
10702 assert_eq!(
10705 f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
10706 format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
10707 );
10708 assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
10711 }
10712
10713 #[test]
10721 fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
10722 let mut f = Fixture::new();
10723 let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
10724 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10725
10726 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
10727 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
10728 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
10729 assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
10730 assert_eq!(
10731 f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
10732 format!("*2\r\n{}{}", bulk("x"), bulk("y"))
10733 );
10734 assert_eq!(
10737 f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
10738 "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
10739 );
10740
10741 assert_eq!(
10744 f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
10745 "-ERR Path does not exist\r\n"
10746 );
10747 assert_eq!(
10748 f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
10749 "-ERR Path does not exist\r\n"
10750 );
10751 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
10752 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
10755 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
10758
10759 assert_eq!(
10763 f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
10764 "-ERR Path does not exist or not an array\r\n"
10765 );
10766 assert_eq!(
10767 f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
10768 "-ERR Path does not exist or not an object\r\n"
10769 );
10770 assert_eq!(
10771 f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
10772 "-WRONGTYPE wrong type of path value - expected object\r\n"
10773 );
10774 assert_eq!(
10775 f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
10776 "-WRONGTYPE wrong type of path value - expected string\r\n"
10777 );
10778
10779 assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
10782 assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
10783 assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
10784 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
10785 assert_eq!(
10786 f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
10787 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10788 );
10789 assert_eq!(
10791 f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
10792 "-ERR Path does not exist or not an object\r\n"
10793 );
10794 }
10795
10796 #[test]
10802 fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
10803 let mut f = Fixture::new();
10804 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
10805
10806 assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
10807 assert_eq!(
10808 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
10809 "*1\r\n:6\r\n"
10810 );
10811 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
10812
10813 assert_eq!(
10816 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
10817 ":7\r\n"
10818 );
10819 assert_eq!(
10820 f.run(&[b"JSON.GET", b"doc", b".a"]),
10821 bulk("[1,2,3,4,5,0,6]")
10822 );
10823 assert_eq!(
10824 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
10825 ":8\r\n"
10826 );
10827 assert_eq!(
10829 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
10830 "-ERR index out of bounds\r\n"
10831 );
10832 assert_eq!(
10833 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
10834 "-ERR index out of bounds\r\n"
10835 );
10836
10837 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
10840 assert_eq!(
10841 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
10842 ":3\r\n"
10843 );
10844 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
10845 assert_eq!(
10846 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
10847 ":2\r\n"
10848 );
10849 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
10850 assert_eq!(
10851 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
10852 ":0\r\n"
10853 );
10854 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10855
10856 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
10859 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
10860 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
10861 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
10862 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
10863
10864 for call in [
10867 &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
10868 &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
10869 &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
10870 &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
10871 ] {
10872 for path in [&b".n"[..], &b".nope"[..]] {
10873 let args: Vec<&[u8]> = call
10874 .iter()
10875 .map(|a| if *a == b"PATH" { path } else { *a })
10876 .collect();
10877 assert_eq!(
10878 f.run(&args),
10879 "-ERR Path does not exist or not an array\r\n",
10880 "{} {}",
10881 String::from_utf8_lossy(call[0]),
10882 String::from_utf8_lossy(path)
10883 );
10884 }
10885 }
10886
10887 assert_eq!(
10890 f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
10891 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10892 );
10893 assert_eq!(
10894 f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
10895 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10896 );
10897
10898 assert!(
10903 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
10904 .starts_with("-this is not the start of a value")
10905 );
10906 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10907 }
10908
10909 #[test]
10917 fn json_arrinsert_is_all_or_nothing_across_the_matches() {
10918 let mut f = Fixture::new();
10919 let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
10920 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10921 assert_eq!(
10922 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
10923 "-ERR index out of bounds\r\n"
10924 );
10925 assert_eq!(
10926 f.run(&[b"JSON.GET", b"doc"]),
10927 bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
10928 );
10929 assert_eq!(
10931 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
10932 "*3\r\n:4\r\n:3\r\n:2\r\n"
10933 );
10934 assert_eq!(
10935 f.run(&[b"JSON.GET", b"doc"]),
10936 bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
10937 );
10938 }
10939
10940 #[test]
10948 fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
10949 let mut f = Fixture::new();
10950 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
10951
10952 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
10953 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
10954 assert_eq!(
10955 f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
10956 "*1\r\n:1\r\n"
10957 );
10958
10959 assert_eq!(
10962 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
10963 ":3\r\n"
10964 );
10965 assert_eq!(
10968 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
10969 ":-1\r\n"
10970 );
10971
10972 for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
10975 assert_eq!(
10976 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
10977 ":3\r\n",
10978 "{}",
10979 String::from_utf8_lossy(start)
10980 );
10981 }
10982 assert_eq!(
10983 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
10984 ":0\r\n"
10985 );
10986 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
10989 assert_eq!(
10990 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
10991 ":-1\r\n"
10992 );
10993
10994 f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
10998 assert_eq!(
10999 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11000 ":0\r\n"
11001 );
11002 assert_eq!(
11003 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11004 ":1\r\n"
11005 );
11006 assert_eq!(
11007 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11008 ":-1\r\n"
11009 );
11010
11011 assert_eq!(
11015 f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11016 "-ERR Path does not exist\r\n"
11017 );
11018 assert_eq!(
11019 f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11020 "-WRONGTYPE wrong type of path value - expected array\r\n"
11021 );
11022 assert_eq!(
11023 f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11024 "-ERR Path does not exist\r\n"
11025 );
11026 assert_eq!(
11027 f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11028 "-ERR Path does not exist\r\n"
11029 );
11030 }
11031
11032 #[test]
11035 fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11036 let mut f = Fixture::new();
11037 let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11038 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11039
11040 assert_eq!(
11043 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11044 bulk("9").as_str()
11045 );
11046 assert_eq!(
11048 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11049 bulk("[11]").as_str()
11050 );
11051 assert_eq!(
11054 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11055 bulk("13.0").as_str()
11056 );
11057 assert_eq!(
11058 f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11059 bulk("number").as_str()
11060 );
11061 assert_eq!(
11062 f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11063 bulk("3.0").as_str()
11064 );
11065 assert_eq!(
11066 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11067 bulk("-8").as_str()
11068 );
11069 f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11072 assert_eq!(
11073 f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11074 bulk("1.224744871391589").as_str()
11075 );
11076 assert_eq!(
11077 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11078 "-ERR result is not a number\r\n"
11079 );
11080 f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11084 assert_eq!(
11085 f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11086 "-ERR numeric overflow\r\n"
11087 );
11088 f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11089 assert_eq!(
11090 f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11091 "-ERR numeric overflow\r\n"
11092 );
11093 f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11095 assert_eq!(
11096 f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11097 "-ERR result is not a number\r\n"
11098 );
11099
11100 assert_eq!(
11104 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11105 bulk("[null]").as_str()
11106 );
11107 assert_eq!(
11108 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11109 bulk("[]").as_str()
11110 );
11111 assert_eq!(
11112 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11113 "-ERR Path does not exist or does not contains a number\r\n"
11114 );
11115 assert_eq!(
11116 f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11117 "-ERR Path does not exist or does not contains a number\r\n"
11118 );
11119 assert_eq!(
11122 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11123 "-bad input number\r\n"
11124 );
11125 assert_eq!(
11126 f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11127 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11128 );
11129 assert_eq!(
11130 f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11131 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11132 );
11133 }
11134
11135 #[test]
11138 fn json_strappend_reads_its_shape_off_the_argument_count() {
11139 let mut f = Fixture::new();
11140 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11141
11142 assert_eq!(
11143 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11144 ":3\r\n"
11145 );
11146 assert_eq!(
11147 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11148 "*1\r\n:4\r\n"
11149 );
11150 assert_eq!(
11153 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11154 ":6\r\n"
11155 );
11156 f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11159 assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11160 assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11161
11162 assert_eq!(
11166 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11167 "-WRONGTYPE wrong type of path value - expected string\r\n"
11168 );
11169 assert_eq!(
11170 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11171 "*1\r\n$-1\r\n"
11172 );
11173 assert_eq!(
11174 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11175 "-ERR Path does not exist or not a string\r\n"
11176 );
11177 assert_eq!(
11178 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11179 "*0\r\n"
11180 );
11181 assert_eq!(
11182 f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11183 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11184 );
11185 }
11186
11187 #[test]
11190 fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11191 let mut f = Fixture::new();
11192 let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11195
11196 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11197 assert_eq!(
11198 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11199 ":4\r\n"
11200 );
11201 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11202 assert_eq!(
11203 f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11204 ":2\r\n"
11205 );
11206 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11207 assert_eq!(
11208 f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11209 ":1\r\n"
11210 );
11211 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11212 assert_eq!(
11213 f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11214 bulk("1").as_str()
11215 );
11216 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11217 assert_eq!(
11218 f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11219 bulk("13").as_str()
11220 );
11221 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11222 assert_eq!(
11223 f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11224 ":4\r\n"
11225 );
11226 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11227 assert_eq!(
11228 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11229 bulk("false").as_str()
11230 );
11231 assert_eq!(
11234 f.run(&[b"JSON.GET", b"doc", b".a"]),
11235 bulk("[false,true,false]").as_str()
11236 );
11237
11238 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11241 assert_eq!(
11242 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11243 ":3\r\n"
11244 );
11245 assert_eq!(
11246 f.run(&[b"JSON.GET", b"doc", b".a"]),
11247 bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11248 );
11249 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11252 assert_eq!(
11253 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11254 "-ERR Path does not exist or not an array\r\n"
11255 );
11256 assert_eq!(
11257 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11258 "-ERR Path does not exist or not a bool\r\n"
11259 );
11260 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11264 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11265 assert_eq!(
11266 f.run(&[b"JSON.GET", b"doc", b".a"]),
11267 bulk("[[],[2]]").as_str()
11268 );
11269 }
11270
11271 #[test]
11274 fn a_write_reaches_a_match_that_sits_inside_another_match() {
11275 let mut f = Fixture::new();
11276 let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11277
11278 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11279 assert_eq!(
11280 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11281 "*3\r\n:3\r\n:2\r\n:3\r\n"
11282 );
11283 assert_eq!(
11284 f.run(&[b"JSON.GET", b"doc", b"$"]),
11285 bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11286 );
11287
11288 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11291 assert_eq!(
11292 f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11293 "*3\r\n:1\r\n:1\r\n:1\r\n"
11294 );
11295 assert_eq!(
11296 f.run(&[b"JSON.GET", b"doc", b"$"]),
11297 bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11298 );
11299
11300 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11303 assert_eq!(
11304 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11305 bulk("[null,8,8]").as_str()
11306 );
11307 }
11308
11309 #[test]
11312 fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11313 let mut f = Fixture::new();
11314 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11315
11316 assert_eq!(
11319 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11320 bulk("[null]").as_str()
11321 );
11322 assert_eq!(
11323 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11324 bulk("[null]").as_str()
11325 );
11326 assert_eq!(
11327 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11328 bulk("[]").as_str()
11329 );
11330 assert_eq!(
11331 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11332 "-ERR Path does not exist or does not contains a number\r\n"
11333 );
11334 assert_eq!(
11336 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11337 "-bad input number\r\n"
11338 );
11339
11340 assert_eq!(
11342 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11343 "*1\r\n$-1\r\n"
11344 );
11345 assert_eq!(
11346 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11347 "-ERR Path does not exist or not a string\r\n"
11348 );
11349 assert_eq!(
11350 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11351 "-WRONGTYPE wrong type of path value - expected string\r\n"
11352 );
11353
11354 assert_eq!(
11356 f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11357 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11358 );
11359 assert_eq!(
11360 f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11361 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11362 );
11363 }
11364
11365 #[test]
11368 fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11369 let mut f = Fixture::new();
11370
11371 assert_eq!(
11375 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11376 "+OK\r\n"
11377 );
11378 assert_eq!(
11379 f.run(&[b"JSON.GET", b"doc", b"$"]),
11380 bulk(r#"[{"x":null,"y":1}]"#).as_str()
11381 );
11382
11383 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11386 assert_eq!(
11387 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11388 "+OK\r\n"
11389 );
11390 assert_eq!(
11391 f.run(&[b"JSON.GET", b"doc", b"$"]),
11392 bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11393 );
11394
11395 assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11397 assert_eq!(
11398 f.run(&[b"JSON.GET", b"doc", b"$"]),
11399 bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11400 );
11401
11402 assert_eq!(
11406 f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11407 "+OK\r\n"
11408 );
11409 assert_eq!(
11410 f.run(&[b"JSON.GET", b"doc", b"$"]),
11411 bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11412 );
11413
11414 assert_eq!(
11418 f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11419 "+OK\r\n"
11420 );
11421 assert_eq!(
11422 f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11423 bulk(r#"[{"z":null}]"#).as_str()
11424 );
11425 assert_eq!(
11426 f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11427 "$-1\r\n"
11428 );
11429 assert_eq!(
11430 f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11431 "-Err wrong static path\r\n"
11432 );
11433
11434 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11436 assert_eq!(
11437 f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11438 "+OK\r\n"
11439 );
11440 assert_eq!(
11441 f.run(&[b"JSON.GET", b"doc", b"$"]),
11442 bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11443 );
11444
11445 assert_eq!(
11447 f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11448 "-ERR syntax error\r\n"
11449 );
11450 assert_eq!(
11451 f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11452 "-ERR new objects must be created at the root\r\n"
11453 );
11454 f.run(&[b"SET", b"str", b"x"]);
11455 assert_eq!(
11456 f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11457 "-Existing key has wrong Redis type\r\n"
11458 );
11459 }
11460
11461 #[test]
11464 fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11465 let mut f = Fixture::new();
11466 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11467 assert_eq!(
11468 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11469 "+OK\r\n"
11470 );
11471 assert_eq!(
11475 f.run(&[b"JSON.GET", b"doc", b"$"]),
11476 bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11477 );
11478
11479 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11482 assert_eq!(
11483 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11484 "+OK\r\n"
11485 );
11486 assert_eq!(
11487 f.run(&[b"JSON.GET", b"doc", b"$"]),
11488 bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11489 );
11490 }
11491
11492 #[test]
11495 fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11496 let mut f = Fixture::new();
11497 let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11498 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11499
11500 assert_eq!(
11501 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11502 bulk(r#"["a","c"]"#).as_str()
11503 );
11504 assert_eq!(
11507 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11508 bulk(r#"["a","c"]"#).as_str()
11509 );
11510 assert_eq!(
11512 f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
11513 bulk(r#""a""#).as_str()
11514 );
11515 assert_eq!(
11516 f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
11517 "*1\r\n$6\r\nobject\r\n"
11518 );
11519
11520 assert_eq!(
11524 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
11525 bulk("[9,10]").as_str()
11526 );
11527 assert_eq!(
11528 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
11529 "+OK\r\n"
11530 );
11531 assert_eq!(
11532 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
11533 "-Err wrong static path\r\n"
11534 );
11535 assert_eq!(
11536 f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
11537 ":2\r\n"
11538 );
11539 assert_eq!(
11540 f.run(&[b"JSON.GET", b"doc", b"$"]),
11541 bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
11542 );
11543
11544 assert!(
11547 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
11548 .starts_with("-ERR")
11549 );
11550 assert!(
11551 f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
11552 .starts_with("-ERR")
11553 );
11554 }
11555
11556 #[test]
11559 fn a_filter_takes_the_membership_operators_and_the_methods_too() {
11560 let mut f = Fixture::new();
11561 let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
11562 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11563
11564 for (path, want) in [
11565 (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
11566 (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
11567 (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
11568 (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
11569 (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
11570 (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
11571 (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
11572 (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
11573 (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
11574 (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
11575 (b"$.box[?(@.n~)].t", "[]"),
11576 (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
11577 (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
11578 (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
11579 (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
11580 ] {
11581 assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
11582 }
11583
11584 assert_eq!(
11587 f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
11588 "+OK\r\n"
11589 );
11590 assert_eq!(
11591 f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
11592 bulk(r#"["b"]"#).as_str()
11593 );
11594 }
11595
11596 #[test]
11599 fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
11600 let mut f = Fixture::new();
11601 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
11602 assert_eq!(
11603 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11604 "+OK\r\n"
11605 );
11606 assert_eq!(
11607 f.run(&[b"JSON.GET", b"doc", b"$"]),
11608 bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
11609 );
11610 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
11613 assert_eq!(
11614 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11615 "+OK\r\n"
11616 );
11617 assert_eq!(
11618 f.run(&[b"JSON.GET", b"doc", b"$"]),
11619 bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
11620 );
11621 }
11622
11623 #[test]
11626 fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
11627 let mut f = Fixture::new();
11628 assert_eq!(
11629 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
11630 "+OK\r\n"
11631 );
11632 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
11633 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
11634
11635 assert_eq!(
11637 f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
11638 "+OK\r\n"
11639 );
11640 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
11641
11642 f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
11647 assert_eq!(
11648 f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
11649 "$-1\r\n"
11650 );
11651 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
11652 assert_eq!(
11653 f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
11654 "$-1\r\n"
11655 );
11656 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11657
11658 f.run(&[b"SET", b"str", b"x"]);
11663 assert_eq!(
11664 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
11665 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
11666 );
11667 assert_eq!(
11668 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
11669 "-Existing key has wrong Redis type\r\n"
11670 );
11671 assert_eq!(
11672 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
11673 "-ERR new objects must be created at the root\r\n"
11674 );
11675 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
11676
11677 assert_eq!(
11682 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
11683 "-Err wrong static path\r\n"
11684 );
11685 assert_eq!(
11686 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
11687 "-ERR array index out of range\r\n"
11688 );
11689 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11690
11691 f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
11695 assert_eq!(
11696 f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
11697 "+OK\r\n"
11698 );
11699 assert_eq!(
11700 f.run(&[b"JSON.GET", b"c", b"$"]),
11701 bulk(r#"[{"n":3}]"#).as_str()
11702 );
11703
11704 assert_eq!(
11707 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
11708 "-ERR wrong number of arguments for 'json.mset' command\r\n"
11709 );
11710 }
11711
11712 #[test]
11715 fn json_resp_answers_the_document_as_resp_types() {
11716 let mut f = Fixture::new();
11717 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
11718 assert_eq!(
11719 f.run(&[b"JSON.RESP", b"doc"]),
11720 "*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"
11721 );
11722 assert_eq!(
11724 f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
11725 "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
11726 );
11727
11728 f.run(&[
11729 b"JSON.SET",
11730 b"doc",
11731 b"$",
11732 br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
11733 ]);
11734 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
11735 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
11736 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
11739 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
11740 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
11741
11742 assert_eq!(
11745 f.run(&[b"JSON.RESP", b"doc", b".nope"]),
11746 "-ERR Path does not exist\r\n"
11747 );
11748 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
11749 assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
11750 assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
11751 }
11752
11753 #[test]
11757 fn json_debug_answers_a_byte_count_and_its_own_help() {
11758 let mut f = Fixture::new();
11759 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
11760 let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
11761 assert!(one.starts_with(':'), "{one}");
11762 assert_eq!(
11763 f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
11764 format!("*1\r\n{one}")
11765 );
11766 let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
11767 assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
11768
11769 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
11773 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
11774 assert_eq!(
11775 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
11776 "-ERR Path does not exist\r\n"
11777 );
11778 assert_eq!(
11779 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
11780 "*0\r\n"
11781 );
11782
11783 assert_eq!(
11784 f.run(&[b"JSON.DEBUG", b"HELP"]),
11785 "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
11786 $34\r\nHELP - this message\r\n"
11787 );
11788 assert_eq!(
11789 f.run(&[b"JSON.DEBUG", b"NOPE"]),
11790 "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
11791 );
11792 assert_eq!(
11793 f.run(&[b"JSON.DEBUG", b"MEMORY"]),
11794 "-ERR wrong number of arguments for 'json.debug' command\r\n"
11795 );
11796 }
11797
11798 #[test]
11803 fn the_first_vadd_decides_how_wide_the_set_is() {
11804 let mut f = Fixture::new();
11805 assert_eq!(
11806 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
11807 ":1\r\n"
11808 );
11809 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
11810 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11811 assert_eq!(
11814 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
11815 ":0\r\n"
11816 );
11817 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11818 assert_eq!(
11821 f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
11822 "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
11823 );
11824 assert_eq!(
11827 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
11828 ":1\r\n"
11829 );
11830 assert_eq!(
11831 f.run(&[b"VEMB", b"v", b"nowhere"]),
11832 "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
11833 );
11834 assert_eq!(
11838 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
11839 "-ERR asked quantization mismatch with existing vector set\r\n"
11840 );
11841 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
11844 assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
11845 assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
11846 }
11847
11848 #[test]
11851 fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
11852 let mut f = Fixture::new();
11853 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
11854 assert_eq!(
11860 f.run(&[b"VEMB", b"v", b"a"]),
11861 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
11862 );
11863 f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
11865 assert_eq!(
11866 f.run(&[b"VEMB", b"n", b"a"]),
11867 "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
11868 );
11869 f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
11872 assert_eq!(
11873 f.run(&[b"VEMB", b"b", b"a"]),
11874 "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
11875 );
11876 assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
11877 assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
11878
11879 let mut f = Fixture::new();
11883 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
11884 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
11885 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
11886 assert_eq!(
11887 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
11888 "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
11889 $8\r\nopposite\r\n$1\r\n0\r\n"
11890 );
11891 assert_eq!(
11894 f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
11895 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11896 );
11897 assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
11900 assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
11901 assert_eq!(
11904 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
11905 "*1\r\n$6\r\nacross\r\n"
11906 );
11907 assert_eq!(
11908 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
11909 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11910 );
11911 assert_eq!(
11915 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
11916 "*1\r\n$6\r\nacross\r\n"
11917 );
11918
11919 let mut g = Fixture::new();
11922 g.run(&[b"HELLO", b"3"]);
11923 g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11924 assert_eq!(
11925 g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
11926 "%1\r\n$4\r\neast\r\n,1\r\n"
11927 );
11928 }
11929
11930 #[test]
11932 fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
11933 let mut f = Fixture::new();
11934 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11935 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11936 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
11937 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
11938 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
11941 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
11942 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
11944 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11945 assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
11948 assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
11949 assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
11950 assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
11953 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
11954 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
11955 assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
11956
11957 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11959 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11960 assert_eq!(
11961 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
11962 "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
11963 );
11964 }
11965
11966 #[test]
11969 fn vrem_takes_the_attribute_with_it() {
11970 let mut f = Fixture::new();
11971 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11972 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11973 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
11974 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
11975 assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
11976 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
11979
11980 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11983 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11984 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11985 f.run(&[b"VREM", b"v", b"east"]);
11986 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
11987 assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
11988 }
11989
11990 #[test]
11993 fn vinfo_says_partition_first() {
11994 let mut f = Fixture::new();
11995 f.run(&[
11996 b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
11997 ]);
11998 f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
11999 let info = f.run(&[b"VINFO", b"v"]);
12000 assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12001 assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12004 assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12005 assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12006 assert!(
12009 info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12010 "{info}"
12011 );
12012 let mut f = Fixture::new();
12013 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12014 assert!(
12015 f.run(&[b"VINFO", b"v"])
12016 .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12017 );
12018 assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12019 }
12020
12021 fn named() -> Fixture {
12023 let mut f = Fixture::new();
12024 for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12025 .iter()
12026 .enumerate()
12027 {
12028 let x = (i + 1).to_string();
12029 f.run(&[
12030 b"VADD",
12031 b"r",
12032 b"VALUES",
12033 b"2",
12034 x.as_bytes(),
12035 b"1",
12036 name.as_bytes(),
12037 ]);
12038 }
12039 f
12040 }
12041
12042 #[test]
12045 fn vrange_walks_the_names_and_not_the_vectors() {
12046 let mut f = named();
12047 assert_eq!(
12048 f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12049 "*5\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n$5\r\ngamma\r\n"
12050 );
12051 assert_eq!(
12052 f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12053 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12054 "the high end is a name and not a prefix, so delta is past it"
12055 );
12056 assert_eq!(
12057 f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12058 "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12059 );
12060 assert_eq!(
12061 f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12062 "*1\r\n$4\r\nbeta\r\n"
12063 );
12064 assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12065 f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12068 assert_eq!(
12069 f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12070 "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12071 );
12072 assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12073 }
12074
12075 #[test]
12078 fn a_vrange_count_of_zero_asks_for_nothing() {
12079 let mut f = named();
12080 assert_eq!(
12081 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12082 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12083 );
12084 assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12085 assert!(
12086 f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12087 .starts_with("*5\r\n"),
12088 "a negative count is no limit at all"
12089 );
12090 }
12091
12092 #[test]
12095 fn vrange_says_which_end_it_could_not_read() {
12096 let mut f = named();
12097 assert_eq!(
12098 f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12099 "-ERR invalid start range format\r\n"
12100 );
12101 assert_eq!(
12102 f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12103 "-ERR invalid end range format\r\n",
12104 "the high end is spelled wrong, which is worth saying before the \
12105 low end being on the wrong side"
12106 );
12107 assert_eq!(
12108 f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12109 "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12110 );
12111 assert_eq!(
12114 f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12115 "-ERR invalid start range format\r\n"
12116 );
12117 assert_eq!(
12118 f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12119 "-ERR invalid COUNT value\r\n"
12120 );
12121 assert_eq!(
12122 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12123 "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12124 );
12125 f.run(&[b"SET", b"s", b"x"]);
12126 assert!(
12127 f.run(&[b"VRANGE", b"s", b"-", b"+"])
12128 .starts_with("-WRONGTYPE")
12129 );
12130 }
12131
12132 #[test]
12135 fn reduce_is_refused_and_not_ignored() {
12136 let mut f = Fixture::new();
12137 let reduce = f.run(&[
12138 b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12139 ]);
12140 assert!(
12141 reduce.starts_with("-ERR REDUCE is not supported."),
12142 "{reduce}"
12143 );
12144 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12145 }
12146
12147 #[test]
12150 fn vsim_filter_reads_the_attributes() {
12151 let mut f = Fixture::new();
12152 for (name, x, y, attr) in [
12153 ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12154 ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12155 ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12156 ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12157 ] {
12158 f.run(&[
12159 b"VADD",
12160 b"v",
12161 b"VALUES",
12162 b"2",
12163 x.as_bytes(),
12164 y.as_bytes(),
12165 name.as_bytes(),
12166 b"SETATTR",
12167 attr.as_bytes(),
12168 ]);
12169 }
12170 assert_eq!(
12173 f.run(&[
12174 b"VSIM",
12175 b"v",
12176 b"VALUES",
12177 b"2",
12178 b"9",
12179 b"1",
12180 b"COUNT",
12181 b"2",
12182 b"FILTER",
12183 b".lang == \"en\"",
12184 ]),
12185 "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12186 );
12187 assert_eq!(
12190 f.run(&[
12191 b"VSIM",
12192 b"v",
12193 b"VALUES",
12194 b"2",
12195 b"9",
12196 b"1",
12197 b"FILTER",
12198 b".lang == 'en' and .year > 1980",
12199 ]),
12200 "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12201 );
12202 assert_eq!(
12204 f.run(&[
12205 b"VSIM",
12206 b"v",
12207 b"VALUES",
12208 b"2",
12209 b"9",
12210 b"1",
12211 b"FILTER",
12212 b".lang in ['fr', 'de']",
12213 ]),
12214 "*1\r\n$1\r\nb\r\n"
12215 );
12216 assert_eq!(
12217 f.run(&[
12218 b"VSIM",
12219 b"v",
12220 b"VALUES",
12221 b"2",
12222 b"9",
12223 b"1",
12224 b"FILTER",
12225 b".rating > 3"
12226 ]),
12227 "*0\r\n"
12228 );
12229 assert_eq!(
12232 f.run(&[
12233 b"VSIM",
12234 b"v",
12235 b"VALUES",
12236 b"2",
12237 b"9",
12238 b"1",
12239 b"TRUTH",
12240 b"FILTER",
12241 b".year < 1980",
12242 ]),
12243 "*1\r\n$1\r\nc\r\n"
12244 );
12245 f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12248 assert_eq!(
12249 f.run(&[
12250 b"VSIM",
12251 b"v",
12252 b"VALUES",
12253 b"2",
12254 b"9",
12255 b"1",
12256 b"COUNT",
12257 b"1",
12258 b"FILTER",
12259 b".lang == \"en\"",
12260 ]),
12261 "*1\r\n$1\r\nb\r\n"
12262 );
12263 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12266 assert_eq!(
12267 f.run(&[
12268 b"VSIM",
12269 b"v",
12270 b"VALUES",
12271 b"2",
12272 b"9",
12273 b"1",
12274 b"COUNT",
12275 b"1",
12276 b"FILTER",
12277 b".lang == \"en\"",
12278 ]),
12279 "*1\r\n$1\r\nb\r\n"
12280 );
12281
12282 let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12285 assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12286 assert_eq!(
12287 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12288 "-ERR invalid FILTER expression\r\n"
12289 );
12290 assert_eq!(
12293 f.run(&[
12294 b"VSIM",
12295 b"v",
12296 b"VALUES",
12297 b"2",
12298 b"9",
12299 b"1",
12300 b"COUNT",
12301 b"1",
12302 b"FILTER-EF",
12303 b"500",
12304 b"FILTER",
12305 b".lang == 'en'",
12306 ]),
12307 "*1\r\n$1\r\nb\r\n"
12308 );
12309 assert_eq!(
12310 f.run(&[
12311 b"VSIM",
12312 b"v",
12313 b"VALUES",
12314 b"2",
12315 b"9",
12316 b"1",
12317 b"COUNT",
12318 b"1",
12319 b"FILTER-EF",
12320 b"0"
12321 ]),
12322 "*1\r\n$1\r\nb\r\n"
12323 );
12324 assert_eq!(
12325 f.run(&[
12326 b"VSIM",
12327 b"v",
12328 b"VALUES",
12329 b"2",
12330 b"9",
12331 b"1",
12332 b"FILTER-EF",
12333 b"lots"
12334 ]),
12335 "-ERR EF must be a positive integer\r\n"
12336 );
12337 }
12338
12339 #[test]
12342 fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12343 let mut f = Fixture::new();
12344 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12345 assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12346 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12347 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12348 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12349 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12350 assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12351 assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12352 assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12353 assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12354 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12355
12356 f.run(&[b"SET", b"s", b"1"]);
12358 assert_eq!(
12359 f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12360 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12361 );
12362 assert_eq!(
12363 f.run(&[b"VCARD", b"s"]),
12364 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12365 );
12366 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12367 assert_eq!(
12368 f.run(&[b"GET", b"v"]),
12369 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12370 );
12371 f.run(&[b"G.NADD", b"social", b"ada"]);
12375 assert_eq!(
12376 f.run(&[b"VCARD", b"social"]),
12377 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12378 );
12379 assert_eq!(
12380 f.run(&[b"G.NGET", b"v", b"ada"]),
12381 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12382 );
12383 }
12384
12385 #[test]
12388 fn vrandmember_has_the_two_shapes_srandmember_has() {
12389 let mut f = Fixture::new();
12390 for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12391 let x = (i + 1).to_string();
12392 f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12393 }
12394 let one = f.run(&[b"VRANDMEMBER", b"v"]);
12396 assert!(one.starts_with("$1\r\n"), "{one}");
12397 let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12399 assert!(all.starts_with("*3\r\n"), "{all}");
12400 for name in ["a", "b", "c"] {
12401 assert!(all.contains(name), "{all} is missing {name}");
12402 }
12403 all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12404 assert!(all.starts_with("*2\r\n"), "{all}");
12405 let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12407 assert!(many.starts_with("*5\r\n"), "{many}");
12408 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12410 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12411 }
12412
12413 #[test]
12416 fn vlinks_reports_one_layer_of_partition_neighbours() {
12417 let mut f = Fixture::new();
12418 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12419 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12420 assert_eq!(
12423 f.run(&[b"VLINKS", b"v", b"east"]),
12424 "*1\r\n*1\r\n$5\r\nnorth\r\n"
12425 );
12426 assert_eq!(
12427 f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12428 "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12429 );
12430 assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12431 assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12432 }
12433
12434 #[test]
12437 fn fp32_and_values_are_the_same_vector() {
12438 let mut f = Fixture::new();
12439 let mut blob = Vec::new();
12440 for x in [3.0f32, 4.0] {
12441 blob.extend_from_slice(&x.to_le_bytes());
12442 }
12443 assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12444 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12445 assert_eq!(
12446 f.run(&[b"VEMB", b"v", b"a"]),
12447 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12448 );
12449 assert_eq!(
12455 f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
12456 "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
12457 );
12458 assert_eq!(
12460 f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12461 "-ERR invalid vector specification\r\n"
12462 );
12463 assert_eq!(
12465 f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12466 "-ERR syntax error\r\n"
12467 );
12468 assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12469 }
12470
12471 #[test]
12476 fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12477 let mut f = Fixture::new();
12478 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12479 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12480 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12481 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12482 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12483 assert_eq!(
12486 f.run(&[b"BF.INFO", b"b"]),
12487 "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12488 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12489 +Expansion rate\r\n:2\r\n"
12490 );
12491 assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12492 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12493 assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12496 assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12497 }
12498
12499 #[test]
12505 fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12506 let mut f = Fixture::new();
12507 f.run(&[b"SET", b"s", b"text"]);
12508 assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12509 assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12510 for cmd in [
12511 vec![&b"BF.ADD"[..], b"s", b"x"],
12512 vec![&b"BF.MADD"[..], b"s", b"x"],
12513 vec![&b"BF.CARD"[..], b"s"],
12514 vec![&b"BF.INFO"[..], b"s"],
12515 vec![&b"BF.DEBUG"[..], b"s"],
12516 vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
12517 ] {
12518 let name = String::from_utf8_lossy(cmd[0]).into_owned();
12519 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12520 }
12521 assert_eq!(
12524 f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
12525 "-ERR bad error rate\r\n"
12526 );
12527 assert!(
12528 f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
12529 .starts_with("-WRONGTYPE")
12530 );
12531 }
12532
12533 #[test]
12536 fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
12537 let mut f = Fixture::new();
12538 assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
12539 for i in 0..10u32 {
12540 assert_eq!(
12541 f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
12542 ":1\r\n"
12543 );
12544 }
12545 assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
12546 assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
12547 assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
12548 assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
12551 assert_eq!(
12552 f.run(&[b"BF.DEBUG", b"g"]),
12553 "*3\r\n$7\r\nsize:11\r\n\
12554 $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
12555 $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
12556 );
12557
12558 assert_eq!(
12560 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
12561 "+OK\r\n"
12562 );
12563 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
12564 assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
12565 assert_eq!(
12566 f.run(&[b"BF.ADD", b"n", b"c"]),
12567 "-ERR non scaling filter is full\r\n"
12568 );
12569 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12572 assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
12575 f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
12576 assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
12577 assert_eq!(
12580 f.run(&[
12581 b"BF.RESERVE",
12582 b"q",
12583 b"0.01",
12584 b"2",
12585 b"NONSCALING",
12586 b"EXPANSION",
12587 b"2"
12588 ]),
12589 "-Nonscaling filters cannot expand\r\n"
12590 );
12591 }
12592
12593 #[test]
12596 fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
12597 let mut f = Fixture::new();
12598 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
12599 assert_eq!(
12600 f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
12601 "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
12602 );
12603 assert_eq!(
12604 f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
12605 "*2\r\n:1\r\n:0\r\n"
12606 );
12607 }
12608
12609 #[test]
12612 fn insert_is_a_reserve_and_a_madd_with_different_errors() {
12613 let mut f = Fixture::new();
12614 assert_eq!(
12615 f.run(&[
12616 b"BF.INSERT",
12617 b"i",
12618 b"CAPACITY",
12619 b"50",
12620 b"ERROR",
12621 b"0.001",
12622 b"ITEMS",
12623 b"a",
12624 b"b"
12625 ]),
12626 "*2\r\n:1\r\n:1\r\n"
12627 );
12628 assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
12629 assert_eq!(
12631 f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
12632 "-ERR not found\r\n"
12633 );
12634 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
12635 assert_eq!(
12638 f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
12639 "-Bad capacity\r\n"
12640 );
12641 assert_eq!(
12642 f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
12643 "-Bad error rate\r\n"
12644 );
12645 assert_eq!(
12646 f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
12647 "-Bad expansion\r\n"
12648 );
12649 assert_eq!(
12654 f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
12655 "*1\r\n:1\r\n"
12656 );
12657 assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
12658 assert_eq!(
12662 f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
12663 "*1\r\n:1\r\n"
12664 );
12665 assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
12666 assert_eq!(
12667 f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
12668 "*1\r\n:1\r\n"
12669 );
12670 assert_eq!(
12671 f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
12672 "-ERR not found\r\n"
12673 );
12674 assert_eq!(
12676 f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12677 "-Unknown argument received\r\n"
12678 );
12679 assert_eq!(
12681 f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12682 "*1\r\n:1\r\n"
12683 );
12684 assert!(
12686 f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
12687 .contains("wrong number of arguments")
12688 );
12689 }
12690
12691 #[test]
12694 fn a_dump_replays_into_a_filter_that_answers_the_same() {
12695 let mut f = Fixture::new();
12696 f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
12697 for i in 0..25u32 {
12698 f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
12699 }
12700 assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
12701
12702 let mut iter = b"0".to_vec();
12705 let mut chunks = 0;
12706 loop {
12707 let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
12708 let text = String::from_utf8_lossy(&raw).into_owned();
12709 let next = text
12710 .split("\r\n")
12711 .nth(1)
12712 .and_then(|n| n.strip_prefix(':'))
12713 .expect("a two element reply of an iterator and a chunk")
12714 .to_owned();
12715 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
12716 let data = &body[body
12717 .windows(2)
12718 .position(|w| w == b"\r\n")
12719 .expect("a length line")
12720 + 2..body.len() - 2];
12721 if next == "0" {
12722 assert!(data.is_empty(), "the last chunk is empty");
12723 break;
12724 }
12725 let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
12726 assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
12727 iter = next.into_bytes();
12728 chunks += 1;
12729 }
12730 assert_eq!(chunks, 3, "a header and one chunk per link");
12731
12732 assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
12733 assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
12734 for i in 0..25u32 {
12735 assert_eq!(
12736 f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
12737 ":1\r\n"
12738 );
12739 }
12740
12741 assert_eq!(
12744 f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
12745 "-ERR received bad data\r\n"
12746 );
12747 assert_eq!(
12748 f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
12749 "-ERR received bad data\r\n"
12750 );
12751 assert_eq!(
12753 f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
12754 "-ERR invalid offset - no link found\r\n"
12755 );
12756 assert_eq!(
12757 f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
12758 "-ERR Second argument must be numeric\r\n"
12759 );
12760 assert_eq!(
12763 f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
12764 "-Second argument must be numeric\r\n"
12765 );
12766 }
12767
12768 #[test]
12771 fn reserve_reads_its_numbers_the_way_string2ll_does() {
12772 let mut f = Fixture::new();
12773 for (args, want) in [
12774 (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
12775 (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
12776 (
12777 vec![&b"0"[..], b"10"],
12778 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12779 ),
12780 (
12781 vec![&b"1"[..], b"10"],
12782 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12783 ),
12784 (
12785 vec![&b"inf"[..], b"10"],
12786 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12787 ),
12788 (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
12789 (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
12790 (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
12791 (
12792 vec![&b"0.01"[..], b"0"],
12793 "-ERR capacity must be in the range [1, 1073741824]\r\n",
12794 ),
12795 (
12796 vec![&b"0.01"[..], b"1073741825"],
12797 "-ERR capacity must be in the range [1, 1073741824]\r\n",
12798 ),
12799 ] {
12800 let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
12801 cmd.extend(args.iter().copied());
12802 assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
12803 }
12804 assert_eq!(
12805 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
12806 "-ERR no expansion\r\n"
12807 );
12808 assert_eq!(
12809 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
12810 "-ERR bad expansion\r\n"
12811 );
12812 assert_eq!(
12813 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
12814 "-ERR expansion must be in the range [0, 32768]\r\n"
12815 );
12816 assert_eq!(
12818 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
12819 "+OK\r\n"
12820 );
12821 assert_eq!(
12822 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
12823 "-ERR item exists\r\n"
12824 );
12825 assert_eq!(
12826 f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
12827 "-Invalid information value\r\n"
12828 );
12829 assert!(
12830 f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
12831 .contains("wrong number of arguments")
12832 );
12833 }
12834
12835 #[test]
12837 fn the_bloom_family_answers_in_resp3_spelling_too() {
12838 let mut f = Fixture::new();
12839 f.out.set_proto(Proto::Resp3);
12840 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
12841 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
12842 assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
12843 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
12844 assert_eq!(
12845 f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
12846 "*2\r\n#t\r\n#f\r\n"
12847 );
12848 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
12850 assert_eq!(
12851 f.run(&[b"BF.INFO", b"b"]),
12852 "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12853 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
12854 +Expansion rate\r\n:2\r\n"
12855 );
12856 assert_eq!(
12859 f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
12860 "%1\r\n+Capacity\r\n:100\r\n"
12861 );
12862 }
12863
12864 fn cf_header(
12873 items: u64,
12874 buckets: u64,
12875 deletes: u64,
12876 filters: u64,
12877 geometry: [u16; 3],
12878 ) -> Vec<u8> {
12879 let mut out = Vec::with_capacity(38);
12880 for n in [items, buckets, deletes, filters] {
12881 out.extend_from_slice(&n.to_le_bytes());
12882 }
12883 for n in geometry {
12884 out.extend_from_slice(&n.to_le_bytes());
12885 }
12886 out
12887 }
12888
12889 #[test]
12893 fn cf_add_makes_the_filter_and_counts_the_copies() {
12894 let mut f = Fixture::new();
12895 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12896 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12897 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
12898 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
12901 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
12902 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
12903 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
12904 assert_eq!(
12905 f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
12906 "*2\r\n:1\r\n:0\r\n"
12907 );
12908 assert_eq!(
12911 f.run(&[b"CF.INFO", b"d"]),
12912 "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
12913 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
12914 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
12915 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
12916 );
12917 assert_eq!(
12918 f.run(&[b"CF.DEBUG", b"d"]),
12919 "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
12920 max_iterations:20 expansion:1\r\n"
12921 );
12922 assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
12923 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
12924
12925 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12927 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
12928 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12929 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
12930 assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
12931
12932 assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
12935 assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
12936 assert_eq!(
12937 f.run(&[b"CF.COMPACT", b"gone"]),
12938 "-Cuckoo filter was not found\r\n"
12939 );
12940 assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
12941 assert!(
12945 f.run(&[b"CF.COMPACT", b"a", b"b"])
12946 .contains("wrong number of arguments")
12947 );
12948 }
12949
12950 #[test]
12953 fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
12954 let mut f = Fixture::new();
12955 f.run(&[b"SET", b"s", b"text"]);
12956 assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
12957 assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12958 assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
12959 assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
12963 assert_eq!(
12964 f.run(&[b"CF.COMPACT", b"s"]),
12965 "-Cuckoo filter was not found\r\n"
12966 );
12967 for cmd in [
12968 vec![&b"CF.ADD"[..], b"s", b"x"],
12969 vec![&b"CF.ADDNX"[..], b"s", b"x"],
12970 vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
12971 vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
12972 vec![&b"CF.INFO"[..], b"s"],
12973 vec![&b"CF.DEBUG"[..], b"s"],
12974 vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
12975 vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
12976 vec![&b"CF.RESERVE"[..], b"s", b"64"],
12977 ] {
12978 let name = String::from_utf8_lossy(cmd[0]).into_owned();
12979 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12980 }
12981 }
12982
12983 #[test]
12986 fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
12987 let mut f = Fixture::new();
12988 assert_eq!(
12989 f.run(&[
12990 b"CF.RESERVE",
12991 b"r",
12992 b"64",
12993 b"BUCKETSIZE",
12994 b"1",
12995 b"MAXITERATIONS",
12996 b"7",
12997 b"EXPANSION",
12998 b"4"
12999 ]),
13000 "+OK\r\n"
13001 );
13002 assert_eq!(
13003 f.run(&[b"CF.DEBUG", b"r"]),
13004 "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13005 max_iterations:7 expansion:4\r\n"
13006 );
13007 assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13008
13009 assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13010 assert_eq!(
13011 f.run(&[b"CF.RESERVE", b"q", b"1"]),
13012 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13013 );
13014 assert_eq!(
13017 f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13018 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13019 );
13020 assert_eq!(
13021 f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13022 "+OK\r\n"
13023 );
13024
13025 assert_eq!(
13030 f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13031 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13032 );
13033 assert_eq!(
13034 f.run(&[
13035 b"CF.RESERVE",
13036 b"q2",
13037 b"64",
13038 b"EXPANSION",
13039 b"xx",
13040 b"BUCKETSIZE",
13041 b"0"
13042 ]),
13043 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13044 );
13045 assert_eq!(
13046 f.run(&[
13047 b"CF.RESERVE",
13048 b"q2",
13049 b"64",
13050 b"MAXITERATIONS",
13051 b"0",
13052 b"BUCKETSIZE",
13053 b"0"
13054 ]),
13055 "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13056 );
13057 assert_eq!(
13061 f.run(&[
13062 b"CF.RESERVE",
13063 b"q3",
13064 b"64",
13065 b"BUCKETSIZE",
13066 b"1",
13067 b"BUCKETSIZE",
13068 b"0"
13069 ]),
13070 "+OK\r\n"
13071 );
13072 assert_eq!(
13075 f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13076 "+OK\r\n"
13077 );
13078 assert_eq!(
13079 f.run(&[b"CF.DEBUG", b"q4"]),
13080 "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13081 max_iterations:20 expansion:1\r\n"
13082 );
13083 assert!(
13086 f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13087 .contains("wrong number of arguments")
13088 );
13089 }
13090
13091 #[test]
13094 fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13095 let mut f = Fixture::new();
13096 assert_eq!(
13097 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13098 "*2\r\n:1\r\n:1\r\n"
13099 );
13100 assert_eq!(
13101 f.run(&[b"CF.DEBUG", b"i"]),
13102 "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13103 max_iterations:20 expansion:1\r\n"
13104 );
13105 assert_eq!(
13108 f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13109 "*2\r\n:0\r\n:1\r\n"
13110 );
13111 assert_eq!(
13112 f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13113 "-ERR not found\r\n"
13114 );
13115 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13116
13117 assert_eq!(
13118 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13119 "-Bad capacity\r\n"
13120 );
13121 assert_eq!(
13124 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13125 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13126 );
13127 assert_eq!(
13131 f.run(&[
13132 b"CF.INSERT",
13133 b"i",
13134 b"CAPACITY",
13135 b"8",
13136 b"CAPACITY",
13137 b"2",
13138 b"ITEMS",
13139 b"a"
13140 ]),
13141 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13142 );
13143 assert_eq!(
13147 f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13148 "*1\r\n:1\r\n"
13149 );
13150 assert_eq!(
13151 f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13152 "*1\r\n:1\r\n"
13153 );
13154 assert_eq!(
13155 f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13156 "-Unknown argument received\r\n"
13157 );
13158 assert_eq!(
13160 f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13161 "*1\r\n:1\r\n"
13162 );
13163 assert!(
13165 f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13166 .contains("wrong number of arguments")
13167 );
13168 assert!(
13169 f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13170 .contains("wrong number of arguments")
13171 );
13172 }
13173
13174 #[test]
13177 fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13178 let mut f = Fixture::new();
13179 f.run(&[
13180 b"CF.RESERVE",
13181 b"s",
13182 b"4",
13183 b"BUCKETSIZE",
13184 b"1",
13185 b"EXPANSION",
13186 b"0",
13187 ]);
13188 for i in 0..4u32 {
13189 assert_eq!(
13190 f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13191 ":1\r\n"
13192 );
13193 }
13194 assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13195 assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13196 assert_eq!(
13199 f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13200 "*2\r\n:-1\r\n:-1\r\n"
13201 );
13202 assert_eq!(
13203 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13204 "*2\r\n:0\r\n:-1\r\n"
13205 );
13206
13207 let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13213 assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13214 assert_eq!(
13215 f.run(&[b"CF.ADD", b"g", b"q"]),
13216 "-Maximum expansions reached\r\n"
13217 );
13218 assert_eq!(
13219 f.run(&[b"CF.INFO", b"g"]),
13220 "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13221 +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13222 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13223 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13224 );
13225 }
13226
13227 #[test]
13231 fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13232 let mut f = Fixture::new();
13233 f.run(&[
13234 b"CF.RESERVE",
13235 b"src",
13236 b"8",
13237 b"BUCKETSIZE",
13238 b"2",
13239 b"EXPANSION",
13240 b"2",
13241 ]);
13242 for i in 0..40u32 {
13243 f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13244 }
13245 let mut pos = b"0".to_vec();
13249 let mut chunks = 0;
13250 loop {
13251 let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13252 let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13253 let next = head
13254 .split("\r\n")
13255 .nth(1)
13256 .and_then(|n| n.strip_prefix(':'))
13257 .expect("a two element reply of a position and a chunk")
13258 .to_owned();
13259 if next == "0" {
13260 assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13261 break;
13262 }
13263 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13264 let at = body
13265 .windows(2)
13266 .position(|w| w == b"\r\n")
13267 .expect("a length line")
13268 + 2;
13269 let data = &body[at..body.len() - 2];
13270 assert_eq!(
13271 f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13272 "+OK\r\n",
13273 "loading chunk {chunks}"
13274 );
13275 pos = next.into_bytes();
13276 chunks += 1;
13277 }
13278 assert!(chunks >= 2, "a header and at least one chunk");
13279
13280 assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13281 assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13282 for i in 0..40u32 {
13283 assert_eq!(
13284 f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13285 ":1\r\n"
13286 );
13287 }
13288
13289 f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13292 assert_eq!(
13293 f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13294 "*2\r\n:0\r\n$-1\r\n"
13295 );
13296
13297 assert_eq!(
13301 f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13302 "-Invalid position\r\n"
13303 );
13304 assert_eq!(
13305 f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13306 "-Invalid position\r\n"
13307 );
13308 assert_eq!(
13309 f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13310 "-Invalid position\r\n"
13311 );
13312 assert_eq!(
13313 f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13314 "-Couldn't load chunk!\r\n"
13315 );
13316 let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13318 assert_eq!(
13319 f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13320 "-ERR item exists\r\n"
13321 );
13322 assert_eq!(
13326 f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13327 "-Invalid header\r\n"
13328 );
13329 for (why, bad) in [
13330 ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13331 ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13332 (
13333 "a bucket count that is not a power of two",
13334 cf_header(0, 3, 0, 1, [2, 20, 1]),
13335 ),
13336 ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13337 ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13338 (
13339 "a growth nobody could reach",
13340 cf_header(0, 8, 0, 1, [2, 20, 32769]),
13341 ),
13342 (
13343 "a chain that cannot grow and did",
13344 cf_header(0, 8, 0, 2, [2, 20, 0]),
13345 ),
13346 (
13349 "a filter count that wraps",
13350 cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13351 ),
13352 ] {
13353 assert_eq!(
13354 f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13355 "-Couldn't create filter!\r\n",
13356 "{why}"
13357 );
13358 }
13359 }
13360
13361 #[test]
13364 fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13365 let mut f = Fixture::new();
13366 f.out.set_proto(Proto::Resp3);
13367 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13368 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13369 assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13370 assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13371 assert_eq!(
13372 f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13373 "*2\r\n#t\r\n#f\r\n"
13374 );
13375 assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13376 assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13377 assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13379 assert_eq!(
13380 f.run(&[b"CF.INFO", b"c"]),
13381 "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13382 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13383 +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13384 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13385 );
13386
13387 f.run(&[
13392 b"CF.RESERVE",
13393 b"s",
13394 b"4",
13395 b"BUCKETSIZE",
13396 b"1",
13397 b"EXPANSION",
13398 b"0",
13399 ]);
13400 assert_eq!(
13401 f.run(&[
13402 b"CF.INSERT",
13403 b"s",
13404 b"ITEMS",
13405 b"a",
13406 b"b",
13407 b"c",
13408 b"d",
13409 b"e",
13410 b"f"
13411 ]),
13412 "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13413 );
13414 assert_eq!(
13415 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13416 "*2\r\n:0\r\n:-1\r\n"
13417 );
13418 assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13419 assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13422 }
13423
13424 #[test]
13429 fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13430 let mut f = Fixture::new();
13431 assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13432 assert_eq!(
13433 f.run(&[b"CMS.INFO", b"d"]),
13434 "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13435 );
13436 assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13437 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13438 assert_eq!(
13441 f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13442 "+OK\r\n"
13443 );
13444 assert_eq!(
13445 f.run(&[b"CMS.INFO", b"p"]),
13446 "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13447 );
13448 assert_eq!(
13451 f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13452 "-CMS: key already exists\r\n"
13453 );
13454 assert_eq!(
13455 f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13456 "-CMS: invalid width\r\n"
13457 );
13458 assert_eq!(
13459 f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13460 "-CMS: invalid depth\r\n"
13461 );
13462 assert_eq!(
13463 f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13464 "-CMS: invalid overestimation value\r\n"
13465 );
13466 assert_eq!(
13467 f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13468 "-CMS: invalid prob value\r\n"
13469 );
13470 assert_eq!(
13474 f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13475 "-CMS: invalid init arguments\r\n"
13476 );
13477 assert_eq!(
13481 f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13482 "-CMS: Insufficient memory to create the key\r\n"
13483 );
13484 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13485 }
13486
13487 #[test]
13490 fn increments_are_parsed_whole_and_the_counters_saturate() {
13491 let mut f = Fixture::new();
13492 f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13493 assert_eq!(
13494 f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13495 "*2\r\n:3\r\n:4\r\n"
13496 );
13497 assert_eq!(
13500 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13501 "*2\r\n:4\r\n:5\r\n"
13502 );
13503 assert_eq!(
13505 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13506 "-CMS: Cannot parse number\r\n"
13507 );
13508 assert_eq!(
13509 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
13510 "-CMS: Number cannot be negative\r\n"
13511 );
13512 assert_eq!(
13513 f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
13514 "*2\r\n:5\r\n:4\r\n"
13515 );
13516 f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
13519 assert_eq!(
13520 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
13521 "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
13522 );
13523 assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
13524 f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
13527 f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
13528 f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
13529 assert_eq!(
13530 f.run(&[b"CMS.INFO", b"w"]),
13531 "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
13532 );
13533 assert!(
13536 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
13537 .contains("wrong number of arguments")
13538 );
13539 assert_eq!(
13540 f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
13541 "-CMS: key does not exist\r\n"
13542 );
13543 assert_eq!(
13544 f.run(&[b"CMS.QUERY", b"nope", b"a"]),
13545 "-CMS: key does not exist\r\n"
13546 );
13547 }
13548
13549 #[test]
13552 fn a_merge_lands_whole_or_not_at_all() {
13553 let mut f = Fixture::new();
13554 for name in [&b"m1"[..], b"m2", b"dst"] {
13555 f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
13556 }
13557 f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
13558 f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
13559 assert_eq!(
13560 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13561 "+OK\r\n"
13562 );
13563 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13564 assert_eq!(
13567 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13568 "+OK\r\n"
13569 );
13570 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13571 assert_eq!(
13572 f.run(&[
13573 b"CMS.MERGE",
13574 b"dst",
13575 b"2",
13576 b"m1",
13577 b"m2",
13578 b"WEIGHTS",
13579 b"2",
13580 b"3"
13581 ]),
13582 "+OK\r\n"
13583 );
13584 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13585 assert_eq!(
13588 f.run(&[
13589 b"CMS.MERGE",
13590 b"dst",
13591 b"1",
13592 b"m1",
13593 b"WEIGHTS",
13594 b"4611686018427387904"
13595 ]),
13596 "-CMS: MERGE overflow\r\n"
13597 );
13598 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13599 f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
13602 assert_eq!(
13603 f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
13604 "-CMS: key does not exist\r\n"
13605 );
13606 assert_eq!(
13607 f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
13608 "-CMS: Number of keys must be positive\r\n"
13609 );
13610 assert_eq!(
13611 f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
13612 "-CMS: wrong number of keys\r\n"
13613 );
13614 assert_eq!(
13615 f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
13616 "-CMS: wrong number of keys/weights\r\n"
13617 );
13618 assert_eq!(
13619 f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
13620 "-CMS: width/depth is not equal\r\n"
13621 );
13622 assert_eq!(
13623 f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
13624 "-CMS: key does not exist\r\n"
13625 );
13626 }
13627
13628 #[test]
13631 fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13632 let mut f = Fixture::new();
13633 f.run(&[b"SET", b"s", b"text"]);
13634 for cmd in [
13635 vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
13636 vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
13637 vec![&b"CMS.QUERY"[..], b"s", b"a"],
13638 vec![&b"CMS.INFO"[..], b"s"],
13639 vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
13640 ] {
13641 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13642 let reply = f.run(&cmd);
13643 assert!(
13646 reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
13647 "{name}: {reply}"
13648 );
13649 }
13650 f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
13651 assert_eq!(
13655 f.run(&[b"COPY", b"c", b"c2"]),
13656 "-ERR not supported for this module key\r\n"
13657 );
13658 assert_eq!(
13659 f.run(&[b"DUMP", b"c"]),
13660 "-ERR DUMP is not supported for this module key\r\n"
13661 );
13662 f.run(&[b"G.NADD", b"g", b"a"]);
13664 assert_eq!(
13665 f.run(&[b"COPY", b"g", b"g2"]),
13666 "-ERR COPY is not supported for a graph\r\n"
13667 );
13668 assert_eq!(
13669 f.run(&[b"DUMP", b"g"]),
13670 "-ERR DUMP is not supported for a graph\r\n"
13671 );
13672 assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
13675 assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
13676 assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
13677 assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
13678 assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
13679 }
13680
13681 #[test]
13686 fn a_reserve_takes_three_arguments_or_six() {
13687 let mut f = Fixture::new();
13688 assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
13689 assert_eq!(
13690 f.run(&[b"TOPK.INFO", b"t"]),
13691 "*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"
13692 );
13693 for cmd in [
13696 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
13697 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
13698 ] {
13699 assert!(f.run(&cmd).contains("wrong number of arguments"));
13700 }
13701 assert_eq!(
13702 f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
13703 "+OK\r\n"
13704 );
13705 assert_eq!(
13708 f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
13709 "-TopK: key already exists\r\n"
13710 );
13711 assert_eq!(
13712 f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
13713 "-TopK: invalid k\r\n"
13714 );
13715 assert_eq!(
13716 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
13717 "-TopK: invalid width\r\n"
13718 );
13719 assert_eq!(
13720 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
13721 "-TopK: invalid depth\r\n"
13722 );
13723 assert_eq!(
13725 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
13726 "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
13727 );
13728 assert_eq!(
13729 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
13730 "+OK\r\n"
13731 );
13732 assert_eq!(
13734 f.run(&[
13735 b"TOPK.RESERVE",
13736 b"w",
13737 b"1",
13738 b"4294967295",
13739 b"4294967295",
13740 b"0.9"
13741 ]),
13742 "-ERR Insufficient memory to create topk data structure\r\n"
13743 );
13744 }
13745
13746 #[test]
13748 fn the_kept_set_is_what_query_and_list_answer_from() {
13749 let mut f = Fixture::new();
13750 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
13751 assert_eq!(
13754 f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
13755 "*2\r\n$-1\r\n$-1\r\n"
13756 );
13757 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
13758 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
13761 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
13762 assert_eq!(
13763 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
13764 "*3\r\n:1\r\n:0\r\n:1\r\n"
13765 );
13766 assert_eq!(
13768 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13769 "*3\r\n:11\r\n:1\r\n:6\r\n"
13770 );
13771 assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
13772 assert_eq!(
13773 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
13774 "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
13775 );
13776 assert_eq!(
13779 f.run(&[b"TOPK.LIST", b"t", b"w"]),
13780 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13781 );
13782 assert_eq!(
13783 f.run(&[b"TOPK.LIST", b"t", b""]),
13784 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13785 );
13786 assert_eq!(
13787 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
13788 "-WITHCOUNT keyword expected\r\n"
13789 );
13790 assert_eq!(
13793 f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
13794 "-WITHCOUNT keyword expected\r\n"
13795 );
13796 assert_eq!(
13797 f.run(&[b"TOPK.LIST", b"missing"]),
13798 "-TopK: key does not exist\r\n"
13799 );
13800 f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
13802 assert_eq!(
13803 f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
13804 "*1\r\n$-1\r\n"
13805 );
13806 assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
13807 assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
13808 }
13809
13810 #[test]
13813 fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
13814 let mut f = Fixture::new();
13815 f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
13816 assert_eq!(
13820 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
13821 format!(
13822 "*2\r\n$-1\r\n-{}\r\n",
13823 "TopK: increment must be an integer greater or equal to 0 and smaller or equal to 100,000"
13824 )
13825 );
13826 assert_eq!(
13827 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13828 "*3\r\n:3\r\n:0\r\n:0\r\n"
13829 );
13830 assert_eq!(
13832 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
13833 "*1\r\n$-1\r\n"
13834 );
13835 assert!(
13836 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
13837 .contains("smaller or equal to 100,000")
13838 );
13839 assert!(
13841 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
13842 .contains("wrong number of arguments")
13843 );
13844 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
13845 }
13846
13847 #[test]
13849 fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
13850 let mut f = Fixture::new();
13851 f.run(&[b"HELLO", b"3"]);
13852 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
13853 f.run(&[b"TOPK.ADD", b"t", b"a"]);
13854 assert_eq!(
13855 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
13856 "*2\r\n#t\r\n#f\r\n"
13857 );
13858 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
13860 assert_eq!(
13861 f.run(&[b"TOPK.INFO", b"t"]),
13862 "%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"
13863 );
13864 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
13865 }
13866
13867 #[test]
13870 fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13871 let mut f = Fixture::new();
13872 f.run(&[b"SET", b"s", b"text"]);
13873 for cmd in [
13874 vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
13875 vec![&b"TOPK.ADD"[..], b"s", b"a"],
13876 vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
13877 vec![&b"TOPK.QUERY"[..], b"s", b"a"],
13878 vec![&b"TOPK.COUNT"[..], b"s", b"a"],
13879 vec![&b"TOPK.LIST"[..], b"s"],
13880 vec![&b"TOPK.INFO"[..], b"s"],
13881 ] {
13882 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13883 let reply = f.run(&cmd);
13884 assert!(
13885 reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
13886 "{name}: {reply}"
13887 );
13888 }
13889 f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
13890 assert_eq!(
13891 f.run(&[b"COPY", b"t", b"t2"]),
13892 "-ERR not supported for this module key\r\n"
13893 );
13894 assert_eq!(
13895 f.run(&[b"DUMP", b"t"]),
13896 "-ERR DUMP is not supported for this module key\r\n"
13897 );
13898 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
13899 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
13900 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
13901 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
13902 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
13903 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
13904 assert_eq!(
13907 f.run(&[b"TOPK.INFO", b"t3"]),
13908 "-TopK: key does not exist\r\n"
13909 );
13910 }
13911
13912 #[test]
13917 fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
13918 let mut f = Fixture::new();
13919 assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
13920 assert_eq!(
13922 f.run(&[b"TDIGEST.INFO", b"t"]),
13923 "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
13924 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
13925 +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
13926 );
13927 assert_eq!(
13928 f.run(&[b"TDIGEST.CREATE", b"t"]),
13929 "-ERR T-Digest: key already exists\r\n"
13930 );
13931 assert!(
13933 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
13934 .contains("wrong number of arguments")
13935 );
13936 assert_eq!(
13937 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
13938 "+OK\r\n"
13939 );
13940 assert_eq!(
13941 f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
13942 "+OK\r\n"
13943 );
13944 assert_eq!(
13948 f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
13949 "-ERR T-Digest: error parsing compression parameter\r\n"
13950 );
13951 assert_eq!(
13952 f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
13953 "-ERR T-Digest: wrong keyword\r\n"
13954 );
13955 assert_eq!(
13956 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
13957 "-ERR T-Digest: error parsing compression parameter\r\n"
13958 );
13959 assert_eq!(
13960 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
13961 "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
13962 );
13963 assert_eq!(
13966 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
13967 "-ERR T-Digest: allocation failed\r\n"
13968 );
13969 assert_eq!(
13971 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
13972 "-ERR T-Digest: allocation failed\r\n"
13973 );
13974 assert_eq!(
13977 f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
13978 "-ERR T-Digest: key already exists\r\n"
13979 );
13980 }
13981
13982 #[test]
13985 fn the_quantile_family_answers_what_the_module_answers() {
13986 let mut f = Fixture::new();
13987 f.run(&[b"TDIGEST.CREATE", b"s"]);
13988 assert_eq!(
13989 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
13990 "+OK\r\n"
13991 );
13992 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
13993 assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
13994 assert_eq!(
13996 f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
13997 "*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"
13998 );
13999 assert_eq!(
14000 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14001 "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14002 );
14003 assert_eq!(
14006 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14007 "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14008 );
14009 assert_eq!(
14010 f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14011 "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14012 );
14013 assert_eq!(
14014 f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14015 "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14016 );
14017 assert_eq!(
14018 f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14019 "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14020 );
14021 assert_eq!(
14022 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14023 "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14024 );
14025 assert_eq!(
14026 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14027 "$3\r\n2.5\r\n"
14028 );
14029 assert_eq!(
14030 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14031 "$3\r\n2.5\r\n"
14032 );
14033 assert_eq!(
14035 f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14036 "-ERR T-Digest: quantile should be in [0,1]\r\n"
14037 );
14038 assert_eq!(
14039 f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14040 "-ERR T-Digest: error parsing quantile\r\n"
14041 );
14042 assert_eq!(
14043 f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14044 "-ERR T-Digest: error parsing cdf\r\n"
14045 );
14046 assert_eq!(
14047 f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14048 "-ERR T-Digest: error parsing value\r\n"
14049 );
14050 assert_eq!(
14051 f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14052 "-ERR T-Digest: rank needs to be non negative\r\n"
14053 );
14054 assert_eq!(
14055 f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14056 "-ERR T-Digest: error parsing rank\r\n"
14057 );
14058 assert_eq!(
14061 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14062 "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14063 );
14064 assert_eq!(
14065 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14066 "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14067 );
14068 assert_eq!(
14069 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14070 "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14071 );
14072 assert_eq!(
14073 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14074 "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14075 );
14076 }
14077
14078 #[test]
14081 fn an_empty_digest_has_an_answer_for_everything() {
14082 let mut f = Fixture::new();
14083 f.run(&[b"TDIGEST.CREATE", b"e"]);
14084 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14085 assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14086 assert_eq!(
14087 f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14088 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14089 );
14090 assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14091 assert_eq!(
14092 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14093 "$3\r\nnan\r\n"
14094 );
14095 assert_eq!(
14098 f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14099 "*2\r\n:-2\r\n:-2\r\n"
14100 );
14101 assert_eq!(
14102 f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14103 "*2\r\n:-2\r\n:-2\r\n"
14104 );
14105 assert_eq!(
14106 f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14107 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14108 );
14109 f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14111 assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14112 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14113 f.run(&[b"TDIGEST.CREATE", b"e2"]);
14116 assert_eq!(
14117 f.run(&[b"TDIGEST.INFO", b"e"]),
14118 f.run(&[b"TDIGEST.INFO", b"e2"])
14119 );
14120 }
14121
14122 #[test]
14125 fn a_sample_is_read_the_way_redis_reads_a_double() {
14126 let mut f = Fixture::new();
14127 f.run(&[b"TDIGEST.CREATE", b"a"]);
14128 for bad in [
14131 &b"nan"[..],
14132 b"1e400",
14133 b"-1e400",
14134 b"1e309",
14135 b"1e-400",
14136 b"",
14137 b" 1",
14138 b"1 ",
14139 b"1e",
14140 b"--1",
14141 ] {
14142 assert_eq!(
14143 f.run(&[b"TDIGEST.ADD", b"a", bad]),
14144 "-ERR T-Digest: error parsing val parameter\r\n",
14145 "{}",
14146 String::from_utf8_lossy(bad)
14147 );
14148 }
14149 for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14152 assert_eq!(
14153 f.run(&[b"TDIGEST.ADD", b"a", word]),
14154 "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14155 "{}",
14156 String::from_utf8_lossy(word)
14157 );
14158 }
14159 for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14162 assert_eq!(
14163 f.run(&[b"TDIGEST.ADD", b"a", good]),
14164 "+OK\r\n",
14165 "{}",
14166 String::from_utf8_lossy(good)
14167 );
14168 }
14169 assert!(
14171 f.run(&[b"TDIGEST.INFO", b"a"])
14172 .contains("Observations\r\n:6\r\n")
14173 );
14174 assert_eq!(
14177 f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14178 "-ERR T-Digest: error parsing val parameter\r\n"
14179 );
14180 assert!(
14181 f.run(&[b"TDIGEST.INFO", b"a"])
14182 .contains("Observations\r\n:6\r\n")
14183 );
14184 }
14185
14186 #[test]
14189 fn a_merge_sweeps_the_destination_between_its_inputs() {
14190 let mut f = Fixture::new();
14191 f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14192 f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14193 f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14194 f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14195 assert_eq!(
14196 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14197 "+OK\r\n"
14198 );
14199 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14204 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14205 assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14206 assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14207 assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14208 assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14209 assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14210 assert!(
14213 f.run(&[b"TDIGEST.INFO", b"m1"])
14214 .contains("Merged nodes\r\n:3\r\n")
14215 );
14216 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14219 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14220 assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14221 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14222 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14225 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14226 assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14227 assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14228 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14230 assert!(
14231 f.run(&[b"TDIGEST.INFO", b"d"])
14232 .contains("Compression\r\n:500\r\n")
14233 );
14234 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14236 assert!(
14237 f.run(&[b"TDIGEST.INFO", b"d"])
14238 .contains("Observations\r\n:12\r\n")
14239 );
14240 assert_eq!(
14242 f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14243 "-ERR T-Digest: error parsing numkeys\r\n"
14244 );
14245 assert_eq!(
14246 f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14247 "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14248 );
14249 assert!(
14250 f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14251 .contains("wrong number of arguments")
14252 );
14253 assert!(
14254 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14255 .contains("wrong number of arguments")
14256 );
14257 assert_eq!(
14258 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14259 "-ERR T-Digest: wrong keyword\r\n"
14260 );
14261 assert_eq!(
14264 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14265 "-ERR T-Digest: key does not exist\r\n"
14266 );
14267 assert!(
14268 f.run(&[b"TDIGEST.INFO", b"d"])
14269 .contains("Observations\r\n:12\r\n")
14270 );
14271 assert_eq!(
14274 f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14275 "-ERR T-Digest: key does not exist\r\n"
14276 );
14277 }
14278
14279 #[test]
14281 fn a_digest_answers_doubles_and_a_map_on_resp3() {
14282 let mut f = Fixture::new();
14283 f.run(&[b"HELLO", b"3"]);
14284 f.run(&[b"TDIGEST.CREATE", b"s"]);
14285 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14286 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14287 assert_eq!(
14288 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14289 "*2\r\n,1\r\n,4\r\n"
14290 );
14291 assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14292 assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14294 assert_eq!(
14295 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14296 "*1\r\n,-inf\r\n"
14297 );
14298 f.run(&[b"TDIGEST.CREATE", b"e"]);
14299 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14300 assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14302 assert_eq!(
14305 f.run(&[b"TDIGEST.INFO", b"s"]),
14306 "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14307 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14308 +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14309 );
14310 }
14311
14312 #[test]
14315 fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14316 let mut f = Fixture::new();
14317 f.run(&[b"SET", b"s", b"text"]);
14318 for cmd in [
14319 vec![&b"TDIGEST.CREATE"[..], b"s"],
14320 vec![&b"TDIGEST.RESET"[..], b"s"],
14321 vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14322 vec![&b"TDIGEST.MIN"[..], b"s"],
14323 vec![&b"TDIGEST.MAX"[..], b"s"],
14324 vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14325 vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14326 vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14327 vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14328 vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14329 vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14330 vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14331 vec![&b"TDIGEST.INFO"[..], b"s"],
14332 ] {
14333 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14334 let reply = f.run(&cmd);
14335 assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14336 }
14337 f.run(&[b"TDIGEST.CREATE", b"t"]);
14339 assert!(
14340 f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14341 .starts_with("-WRONGTYPE")
14342 );
14343 assert!(
14344 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14345 .starts_with("-WRONGTYPE")
14346 );
14347 assert_eq!(
14348 f.run(&[b"COPY", b"t", b"t2"]),
14349 "-ERR not supported for this module key\r\n"
14350 );
14351 assert_eq!(
14352 f.run(&[b"DUMP", b"t"]),
14353 "-ERR DUMP is not supported for this module key\r\n"
14354 );
14355 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14356 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14357 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14358 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14359 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14360 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14361 assert_eq!(
14364 f.run(&[b"TDIGEST.INFO", b"t3"]),
14365 "-ERR T-Digest: key does not exist\r\n"
14366 );
14367 assert_eq!(
14370 f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14371 "-ERR T-Digest: key does not exist\r\n"
14372 );
14373 }
14374
14375 fn without_memory(reply: &str) -> String {
14385 let head = "+memoryUsage\r\n:";
14386 let at = reply.find(head).expect("every TS.INFO reports memory");
14387 let rest = &reply[at + head.len()..];
14388 let end = rest.find("\r\n").expect("and it is a whole number");
14389 format!("{}{}", &reply[..at + head.len()], &rest[end..])
14390 }
14391
14392 #[test]
14395 fn a_series_is_made_empty_and_reports_on_itself() {
14396 let mut f = Fixture::new();
14397 assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
14398 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14399 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
14400 assert_eq!(
14404 without_memory(&f.run(&[b"TS.INFO", b"t"])),
14405 "*28\r\n\
14406 +totalSamples\r\n:0\r\n\
14407 +memoryUsage\r\n:\r\n\
14408 +firstTimestamp\r\n:0\r\n\
14409 +lastTimestamp\r\n:0\r\n\
14410 +retentionTime\r\n:0\r\n\
14411 +chunkCount\r\n:1\r\n\
14412 +chunkSize\r\n:4096\r\n\
14413 +chunkType\r\n+compressed\r\n\
14414 +duplicatePolicy\r\n+block\r\n\
14415 +labels\r\n*0\r\n\
14416 +sourceKey\r\n$-1\r\n\
14417 +rules\r\n*0\r\n\
14418 +ignoreMaxTimeDiff\r\n:0\r\n\
14419 +ignoreMaxValDiff\r\n$1\r\n0\r\n"
14420 );
14421 assert_eq!(
14424 f.run(&[b"TS.CREATE", b"t"]),
14425 "-ERR TSDB: key already exists\r\n"
14426 );
14427 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14428 assert_eq!(
14429 f.run(&[b"TS.CREATE", b"str"]),
14430 "-ERR TSDB: key already exists\r\n"
14431 );
14432 assert_eq!(
14435 f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
14436 "-ERR TSDB: Couldn't parse RETENTION\r\n"
14437 );
14438 let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14444 assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
14445 assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
14446 assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
14447 assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
14448 assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
14449 assert_eq!(
14450 f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
14451 "-ERR TSDB: the key is not a TSDB key\r\n"
14452 );
14453 assert_eq!(
14455 f.run(&[b"TS.INFO", b"nope"]),
14456 "-ERR TSDB: the key does not exist\r\n"
14457 );
14458 assert_eq!(
14459 f.run(&[b"TS.GET", b"nope"]),
14460 "-ERR TSDB: the key does not exist\r\n"
14461 );
14462 assert_eq!(
14463 f.run(&[b"TS.ALTER", b"nope"]),
14464 "-ERR TSDB: the key does not exist\r\n"
14465 );
14466 assert_eq!(
14467 f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
14468 "-ERR TSDB: the key does not exist\r\n"
14469 );
14470 }
14471
14472 #[test]
14475 fn the_options_are_a_keyword_scan_and_not_a_grammar() {
14476 let mut f = Fixture::new();
14477 assert_eq!(
14478 f.run(&[
14479 b"TS.CREATE",
14480 b"t",
14481 b"RETENTION",
14482 b"5000",
14483 b"ENCODING",
14484 b"UNCOMPRESSED",
14485 b"CHUNK_SIZE",
14486 b"128",
14487 b"DUPLICATE_POLICY",
14488 b"LAST",
14489 b"IGNORE",
14490 b"10",
14491 b"0.5",
14492 b"LABELS",
14493 b"room",
14494 b"kitchen"
14495 ]),
14496 "+OK\r\n"
14497 );
14498 let info = f.run(&[b"TS.INFO", b"t"]);
14499 assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
14500 assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
14501 assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
14502 assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
14503 assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
14504 assert!(
14507 info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
14508 "{info}"
14509 );
14510 assert!(
14511 info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
14512 "{info}"
14513 );
14514
14515 assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
14517 assert_eq!(
14521 f.run(&[
14522 b"TS.CREATE",
14523 b"g",
14524 b"LABELS",
14525 b"a",
14526 b"b",
14527 b"RETENTION",
14528 b"5"
14529 ]),
14530 "+OK\r\n"
14531 );
14532 let greedy = f.run(&[b"TS.INFO", b"g"]);
14533 assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
14534 assert!(
14535 greedy.contains("*2\r\n$1\r\na\r\n$1\r\nb\r\n*2\r\n$9\r\nRETENTION\r\n$1\r\n5\r\n"),
14536 "{greedy}"
14537 );
14538
14539 assert_eq!(
14541 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
14542 "-ERR TSDB: Couldn't parse LABELS\r\n"
14543 );
14544 assert_eq!(
14545 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
14546 "-ERR TSDB: Couldn't parse LABELS\r\n"
14547 );
14548 assert_eq!(
14549 f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
14550 "-ERR TSDB: Couldn't parse RETENTION\r\n"
14551 );
14552 assert_eq!(
14555 f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
14556 "-TSDB: Couldn't parse RETENTION\r\n"
14557 );
14558 assert_eq!(
14559 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
14560 "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
14561 );
14562 assert_eq!(
14563 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
14564 "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
14565 );
14566 assert_eq!(
14567 f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
14568 "-ERR TSDB: unknown ENCODING parameter\r\n"
14569 );
14570 assert!(
14573 f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
14574 .contains("wrong number of arguments for 'ts.create' command")
14575 );
14576 assert_eq!(
14577 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
14578 "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
14579 );
14580 assert_eq!(
14581 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
14582 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14583 );
14584 assert_eq!(
14585 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
14586 "-ERR TSDB: Couldn't parse IGNORE\r\n"
14587 );
14588 assert_eq!(
14589 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
14590 "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
14591 );
14592 assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
14593
14594 assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
14597 let after = f.run(&[b"TS.INFO", b"t"]);
14598 assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
14599 assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
14600 assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
14601 assert_eq!(
14602 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
14603 "-ERR TSDB: unknown ENCODING parameter\r\n"
14604 );
14605 assert_eq!(
14607 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
14608 "+OK\r\n"
14609 );
14610 assert!(
14611 f.run(&[b"TS.INFO", b"t"])
14612 .contains("+chunkType\r\n+uncompressed\r\n")
14613 );
14614 }
14615
14616 #[test]
14619 fn samples_land_where_they_are_put_and_the_newest_comes_back() {
14620 let mut f = Fixture::new();
14621 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
14622 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14624 assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
14625 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
14628 assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
14629 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
14630 assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
14633 assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
14634
14635 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14638 assert_eq!(
14639 f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
14640 "-ERR TSDB: invalid value\r\n"
14641 );
14642 for bad in [
14646 &b".5"[..],
14647 b"1.",
14648 b"+1",
14649 b" 1",
14650 b"0x10",
14651 b"inf",
14652 b"1e400",
14653 b"--1",
14654 b"1e",
14655 ] {
14656 assert_eq!(
14657 f.run(&[b"TS.ADD", b"v", b"1", bad]),
14658 "-ERR TSDB: invalid value\r\n",
14659 "{}",
14660 String::from_utf8_lossy(bad)
14661 );
14662 }
14663 assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
14665
14666 assert_eq!(
14669 f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
14670 "-ERR TSDB: invalid timestamp\r\n"
14671 );
14672 assert_eq!(
14673 f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
14674 "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
14675 );
14676
14677 assert_eq!(
14680 f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
14681 "-ERR TSDB: Error at upsert, update is not supported when DUPLICATE_POLICY is set to BLOCK mode, or either current or new value is NaN and DUPLICATE_POLICY is MAX/MIN/SUM\r\n"
14682 );
14683 assert_eq!(
14684 f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
14685 ":300\r\n"
14686 );
14687 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
14688 assert_eq!(
14691 f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
14692 ":1\r\n"
14693 );
14694 assert_eq!(
14695 f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
14696 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14697 );
14698
14699 assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
14702 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
14703 assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
14704 assert_eq!(
14705 f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
14706 "-ERR TSDB: Timestamp is older than retention\r\n"
14707 );
14708 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
14710 assert!(
14711 f.run(&[b"TS.INFO", b"r"])
14712 .contains("+totalSamples\r\n:1\r\n")
14713 );
14714
14715 assert_eq!(
14718 f.run(&[
14719 b"TS.CREATE",
14720 b"i",
14721 b"DUPLICATE_POLICY",
14722 b"LAST",
14723 b"IGNORE",
14724 b"10",
14725 b"0.5"
14726 ]),
14727 "+OK\r\n"
14728 );
14729 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
14730 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
14731 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
14732 }
14733
14734 #[test]
14737 fn a_madd_answers_each_triple_and_creates_nothing() {
14738 let mut f = Fixture::new();
14739 assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
14740 assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
14741 assert_eq!(
14742 f.run(&[
14743 b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
14744 ]),
14745 "*3\r\n:100\r\n:100\r\n:200\r\n"
14746 );
14747 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14750 assert_eq!(
14751 f.run(&[
14752 b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
14753 ]),
14754 "*3\r\n\
14755 -ERR TSDB: the key is not a TSDB key\r\n\
14756 -ERR TSDB: the key is not a TSDB key\r\n\
14757 :300\r\n"
14758 );
14759 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
14760 assert_eq!(
14762 f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
14763 "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
14764 );
14765 assert!(
14767 f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
14768 .contains("wrong number of arguments for 'ts.madd' command")
14769 );
14770 }
14771
14772 #[test]
14774 fn an_increment_walks_the_newest_value_up_and_down() {
14775 let mut f = Fixture::new();
14776 assert_eq!(
14777 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
14778 ":100\r\n"
14779 );
14780 assert_eq!(
14781 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
14782 ":100\r\n"
14783 );
14784 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
14787 assert_eq!(
14788 f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
14789 ":200\r\n"
14790 );
14791 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
14792 assert_eq!(
14795 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
14796 "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
14797 );
14798 assert_eq!(
14802 f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
14803 ":1\r\n"
14804 );
14805 assert_eq!(
14806 f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
14807 ":1\r\n"
14808 );
14809 assert_eq!(
14810 f.run(&[b"TS.INCRBY", b"t", b"nan"]),
14811 "-ERR TSDB: invalid increase/decrease value\r\n"
14812 );
14813 assert_eq!(
14814 f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
14815 "-ERR TSDB: invalid increase/decrease value\r\n"
14816 );
14817 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14820 assert_eq!(
14821 f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
14822 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
14823 );
14824 assert_eq!(
14829 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
14830 "-ERR TSDB: invalid timestamp\r\n"
14831 );
14832 assert_eq!(
14835 f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
14836 format!(":{}\r\n", f.server.now_ms())
14837 );
14838 assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
14840 assert_eq!(
14841 f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
14842 "-ERR TSDB: cannot increment/decrement NaN value\r\n"
14843 );
14844 }
14845
14846 #[test]
14848 fn deleting_takes_out_a_span_and_answers_how_many_went() {
14849 let mut f = Fixture::new();
14850 for at in [b"100".as_slice(), b"200", b"300", b"400"] {
14851 f.run(&[b"TS.ADD", b"t", at, b"1"]);
14852 }
14853 assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
14854 assert!(
14855 f.run(&[b"TS.INFO", b"t"])
14856 .contains("+totalSamples\r\n:2\r\n")
14857 );
14858 assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
14860 assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
14862 let empty = f.run(&[b"TS.INFO", b"t"]);
14865 assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
14866 assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
14867 assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
14868 assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
14869 assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
14870 assert_eq!(
14872 f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
14873 "-ERR TSDB: wrong fromTimestamp\r\n"
14874 );
14875 assert_eq!(
14876 f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
14877 "-ERR TSDB: wrong toTimestamp\r\n"
14878 );
14879 assert_eq!(
14880 f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
14881 "-ERR TSDB: wrong fromTimestamp\r\n"
14882 );
14883 }
14884
14885 #[test]
14888 fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
14889 let mut f = Fixture::new();
14890 f.out = Out::new(Proto::Resp3);
14891 assert_eq!(
14892 f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
14893 "+OK\r\n"
14894 );
14895 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
14896 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
14898 assert_eq!(
14899 without_memory(&f.run(&[b"TS.INFO", b"t"])),
14900 "%14\r\n\
14901 +totalSamples\r\n:1\r\n\
14902 +memoryUsage\r\n:\r\n\
14903 +firstTimestamp\r\n:100\r\n\
14904 +lastTimestamp\r\n:100\r\n\
14905 +retentionTime\r\n:0\r\n\
14906 +chunkCount\r\n:1\r\n\
14907 +chunkSize\r\n:4096\r\n\
14908 +chunkType\r\n+compressed\r\n\
14909 +duplicatePolicy\r\n+block\r\n\
14910 +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
14911 +sourceKey\r\n_\r\n\
14912 +rules\r\n%0\r\n\
14913 +ignoreMaxTimeDiff\r\n:0\r\n\
14914 +ignoreMaxValDiff\r\n,0\r\n"
14915 );
14916 }
14917
14918 #[test]
14921 fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
14922 let mut f = Fixture::new();
14923 for (at, v) in [
14924 (b"100".as_slice(), b"1".as_slice()),
14925 (b"200", b"2"),
14926 (b"300", b"3"),
14927 (b"400", b"4"),
14928 ] {
14929 f.run(&[b"TS.ADD", b"t", at, v]);
14930 }
14931 assert_eq!(
14932 f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
14933 "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
14934 *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
14935 );
14936 assert_eq!(
14938 f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
14939 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
14940 );
14941 assert_eq!(
14944 f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
14945 "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
14946 );
14947 assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
14949 assert_eq!(
14951 f.run(&[
14952 b"TS.RANGE",
14953 b"t",
14954 b"-",
14955 b"+",
14956 b"FILTER_BY_VALUE",
14957 b"2",
14958 b"3"
14959 ]),
14960 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
14961 );
14962 assert_eq!(
14963 f.run(&[
14964 b"TS.RANGE",
14965 b"t",
14966 b"-",
14967 b"+",
14968 b"FILTER_BY_TS",
14969 b"100",
14970 b"400"
14971 ]),
14972 "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
14973 );
14974 assert_eq!(
14976 f.run(&[
14977 b"TS.RANGE",
14978 b"t",
14979 b"-",
14980 b"+",
14981 b"ZZZ",
14982 b"FILTER_BY_TS",
14983 b"400"
14984 ]),
14985 "*1\r\n*2\r\n:400\r\n+4\r\n"
14986 );
14987 assert_eq!(
14989 f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
14990 "*1\r\n*2\r\n:100\r\n+1\r\n"
14991 );
14992 }
14993
14994 #[test]
14996 fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
14997 let mut f = Fixture::new();
14998 for (at, v) in [
14999 (b"100".as_slice(), b"1".as_slice()),
15000 (b"200", b"2"),
15001 (b"300", b"3"),
15002 (b"400", b"4"),
15003 ] {
15004 f.run(&[b"TS.ADD", b"t", at, v]);
15005 }
15006 assert_eq!(
15007 f.run(&[
15008 b"TS.RANGE",
15009 b"t",
15010 b"-",
15011 b"+",
15012 b"AGGREGATION",
15013 b"avg",
15014 b"200"
15015 ]),
15016 "*3\r\n*2\r\n:0\r\n+1\r\n*2\r\n:200\r\n+2.5\r\n*2\r\n:400\r\n+4\r\n"
15017 );
15018 assert_eq!(
15021 f.run(&[
15022 b"TS.RANGE",
15023 b"t",
15024 b"-",
15025 b"+",
15026 b"AGGREGATION",
15027 b"min,max,count",
15028 b"200"
15029 ]),
15030 "*3\r\n\
15031 *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15032 *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15033 *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15034 );
15035 assert_eq!(
15037 f.run(&[
15038 b"TS.RANGE",
15039 b"t",
15040 b"-",
15041 b"+",
15042 b"AGGREGATION",
15043 b"avg",
15044 b"200",
15045 b"BUCKETTIMESTAMP",
15046 b"+"
15047 ]),
15048 "*3\r\n*2\r\n:200\r\n+1\r\n*2\r\n:400\r\n+2.5\r\n*2\r\n:600\r\n+4\r\n"
15049 );
15050 assert_eq!(
15052 f.run(&[
15053 b"TS.RANGE",
15054 b"t",
15055 b"100",
15056 b"400",
15057 b"ALIGN",
15058 b"100",
15059 b"AGGREGATION",
15060 b"sum",
15061 b"200"
15062 ]),
15063 "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15064 );
15065 assert_eq!(
15068 f.run(&[
15069 b"TS.RANGE",
15070 b"t",
15071 b"-",
15072 b"+",
15073 b"AGGREGATION",
15074 b"count",
15075 b"200"
15076 ]),
15077 "*3\r\n*2\r\n:0\r\n+1\r\n*2\r\n:200\r\n+2\r\n*2\r\n:400\r\n+1\r\n"
15078 );
15079 assert_eq!(
15080 f.run(&[
15081 b"TS.RANGE",
15082 b"t",
15083 b"-",
15084 b"+",
15085 b"AGGREGATION",
15086 b"count",
15087 b"200",
15088 b"COUNT",
15089 b"1"
15090 ]),
15091 "*1\r\n*2\r\n:0\r\n+1\r\n"
15092 );
15093 }
15094
15095 #[test]
15098 fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15099 let mut f = Fixture::new();
15100 for (at, v) in [
15101 (b"0".as_slice(), b"1".as_slice()),
15102 (b"100", b"2"),
15103 (b"500", b"nan"),
15104 (b"600", b"3"),
15105 ] {
15106 f.run(&[b"TS.ADD", b"g", at, v]);
15107 }
15108 assert_eq!(
15111 f.run(&[
15112 b"TS.RANGE",
15113 b"g",
15114 b"-",
15115 b"+",
15116 b"AGGREGATION",
15117 b"avg",
15118 b"100"
15119 ]),
15120 "*3\r\n*2\r\n:0\r\n+1\r\n*2\r\n:100\r\n+2\r\n*2\r\n:600\r\n+3\r\n"
15121 );
15122 assert_eq!(
15124 f.run(&[
15125 b"TS.RANGE",
15126 b"g",
15127 b"-",
15128 b"+",
15129 b"AGGREGATION",
15130 b"sum",
15131 b"100",
15132 b"EMPTY"
15133 ]),
15134 "*7\r\n*2\r\n:0\r\n+1\r\n*2\r\n:100\r\n+2\r\n*2\r\n:200\r\n+0\r\n\
15135 *2\r\n:300\r\n+0\r\n*2\r\n:400\r\n+0\r\n*2\r\n:500\r\n+0\r\n\
15136 *2\r\n:600\r\n+3\r\n"
15137 );
15138 assert_eq!(
15143 f.run(&[
15144 b"TS.RANGE",
15145 b"g",
15146 b"-",
15147 b"+",
15148 b"AGGREGATION",
15149 b"last",
15150 b"100",
15151 b"EMPTY"
15152 ]),
15153 "*7\r\n*2\r\n:0\r\n+1\r\n*2\r\n:100\r\n+2\r\n*2\r\n:200\r\n+2\r\n\
15154 *2\r\n:300\r\n+2\r\n*2\r\n:400\r\n+2\r\n*2\r\n:500\r\n+2\r\n\
15155 *2\r\n:600\r\n+3\r\n"
15156 );
15157 assert_eq!(
15158 f.run(&[
15159 b"TS.REVRANGE",
15160 b"g",
15161 b"-",
15162 b"+",
15163 b"AGGREGATION",
15164 b"last",
15165 b"100",
15166 b"EMPTY"
15167 ]),
15168 "*7\r\n*2\r\n:600\r\n+3\r\n*2\r\n:500\r\n+3\r\n*2\r\n:400\r\n+2\r\n\
15169 *2\r\n:300\r\n+2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:100\r\n+2\r\n\
15170 *2\r\n:0\r\n+1\r\n"
15171 );
15172 assert_eq!(
15175 f.run(&[
15176 b"TS.RANGE",
15177 b"g",
15178 b"500",
15179 b"600",
15180 b"AGGREGATION",
15181 b"last",
15182 b"100",
15183 b"EMPTY"
15184 ]),
15185 "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15186 );
15187 }
15188
15189 #[test]
15192 fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15193 let mut f = Fixture::new();
15194 f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15195 f.run(&[b"SET", b"str", b"x"]);
15196 let cases: &[(&[&[u8]], &str)] = &[
15197 (
15198 &[b"TS.RANGE", b"t"],
15199 "-ERR wrong number of arguments for 'ts.range' command\r\n",
15200 ),
15201 (
15203 &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15204 "-ERR TSDB: the key does not exist\r\n",
15205 ),
15206 (
15207 &[b"TS.RANGE", b"str", b"-", b"+"],
15208 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15209 ),
15210 (
15211 &[b"TS.RANGE", b"t", b"abc", b"+"],
15212 "-ERR TSDB: wrong fromTimestamp\r\n",
15213 ),
15214 (
15215 &[b"TS.RANGE", b"t", b"-", b"abc"],
15216 "-ERR TSDB: wrong toTimestamp\r\n",
15217 ),
15218 (
15219 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15220 "-ERR TSDB: COUNT argument is missing\r\n",
15221 ),
15222 (
15223 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15224 "-ERR TSDB: Couldn't parse COUNT\r\n",
15225 ),
15226 (
15227 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15228 "-ERR TSDB: Invalid COUNT value\r\n",
15229 ),
15230 (
15231 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15232 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15233 ),
15234 (
15235 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15236 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15237 ),
15238 (
15239 &[
15240 b"TS.RANGE",
15241 b"t",
15242 b"-",
15243 b"+",
15244 b"AGGREGATION",
15245 b"nope",
15246 b"100",
15247 ],
15248 "-ERR TSDB: Unknown aggregation type\r\n",
15249 ),
15250 (
15251 &[
15252 b"TS.RANGE",
15253 b"t",
15254 b"-",
15255 b"+",
15256 b"AGGREGATION",
15257 b"avg,,min",
15258 b"100",
15259 ],
15260 "-ERR TSDB: Empty aggregation type in list\r\n",
15261 ),
15262 (
15264 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15265 "-ERR TSDB: Unknown aggregation type\r\n",
15266 ),
15267 (
15268 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15269 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15270 ),
15271 (
15272 &[
15273 b"TS.RANGE",
15274 b"t",
15275 b"-",
15276 b"+",
15277 b"AGGREGATION",
15278 b"avg",
15279 b"100",
15280 b"X",
15281 b"EMPTY",
15282 ],
15283 "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15284 ),
15285 (
15286 &[
15287 b"TS.RANGE",
15288 b"t",
15289 b"-",
15290 b"+",
15291 b"AGGREGATION",
15292 b"avg",
15293 b"100",
15294 b"BUCKETTIMESTAMP",
15295 b"z",
15296 ],
15297 "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15298 ),
15299 (
15300 &[
15301 b"TS.RANGE",
15302 b"t",
15303 b"-",
15304 b"+",
15305 b"AGGREGATION",
15306 b"avg",
15307 b"100",
15308 b"X",
15309 b"Y",
15310 b"BUCKETTIMESTAMP",
15311 b"-",
15312 ],
15313 "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15314 AGGREGATION flag\r\n",
15315 ),
15316 (
15317 &[
15318 b"TS.RANGE",
15319 b"t",
15320 b"-",
15321 b"+",
15322 b"ALIGN",
15323 b"z",
15324 b"AGGREGATION",
15325 b"avg",
15326 b"100",
15327 ],
15328 "-ERR TSDB: unknown ALIGN parameter\r\n",
15329 ),
15330 (
15331 &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15332 "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15333 ),
15334 (
15335 &[
15336 b"TS.RANGE",
15337 b"t",
15338 b"-",
15339 b"+",
15340 b"ALIGN",
15341 b"-",
15342 b"AGGREGATION",
15343 b"avg",
15344 b"100",
15345 ],
15346 "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15347 ),
15348 (
15349 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15350 "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15351 ),
15352 (
15353 &[
15354 b"TS.RANGE",
15355 b"t",
15356 b"-",
15357 b"+",
15358 b"FILTER_BY_VALUE",
15359 b"x",
15360 b"2",
15361 ],
15362 "-ERR TSDB: Couldn't parse MIN\r\n",
15363 ),
15364 (
15365 &[
15366 b"TS.RANGE",
15367 b"t",
15368 b"-",
15369 b"+",
15370 b"FILTER_BY_VALUE",
15371 b"1",
15372 b"y",
15373 ],
15374 "-ERR TSDB: Couldn't parse MAX\r\n",
15375 ),
15376 (
15377 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15378 "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15379 ),
15380 ];
15381 for (argv, want) in cases {
15382 let got = f.run(argv);
15383 assert_eq!(&got, want, "{:?}", argv.last());
15384 }
15385 f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
15389 f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
15390 assert_eq!(
15391 f.run(&[
15392 b"TS.RANGE",
15393 b"wide",
15394 b"-",
15395 b"+",
15396 b"AGGREGATION",
15397 b"avg",
15398 b"1",
15399 b"EMPTY"
15400 ]),
15401 "-ERR TSDB: the requested range holds too many empty buckets\r\n"
15402 );
15403 }
15404
15405 #[test]
15407 fn resp3_writes_a_read_value_as_a_double() {
15408 let mut f = Fixture::new();
15409 f.out = Out::new(Proto::Resp3);
15410 for (at, v) in [
15411 (b"0".as_slice(), b"1".as_slice()),
15412 (b"100", b"2"),
15413 (b"500", b"nan"),
15414 (b"600", b"3"),
15415 ] {
15416 f.run(&[b"TS.ADD", b"g", at, v]);
15417 }
15418 assert_eq!(
15419 f.run(&[
15420 b"TS.RANGE",
15421 b"g",
15422 b"0",
15423 b"100",
15424 b"AGGREGATION",
15425 b"avg,min",
15426 b"200"
15427 ]),
15428 "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
15429 );
15430 assert_eq!(
15431 f.run(&[
15432 b"TS.RANGE",
15433 b"g",
15434 b"500",
15435 b"600",
15436 b"AGGREGATION",
15437 b"last",
15438 b"100",
15439 b"EMPTY"
15440 ]),
15441 "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
15442 );
15443 }
15444
15445 fn joined() -> Fixture {
15448 let mut f = Fixture::new();
15449 f.run(&[b"TS.CREATE", b"z"]);
15450 for (at, v) in [
15451 (b"10".as_slice(), b"1".as_slice()),
15452 (b"20", b"2"),
15453 (b"40", b"4"),
15454 (b"50", b"5"),
15455 ] {
15456 f.run(&[b"TS.ADD", b"x", at, v]);
15457 }
15458 for (at, v) in [
15459 (b"20".as_slice(), b"20".as_slice()),
15460 (b"30", b"30"),
15461 (b"50", b"50"),
15462 (b"60", b"60"),
15463 ] {
15464 f.run(&[b"TS.ADD", b"y", at, v]);
15465 }
15466 f
15467 }
15468
15469 #[test]
15473 fn an_nrange_joins_its_keys_on_the_timestamp() {
15474 let mut f = joined();
15475 assert_eq!(
15477 f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
15478 "*4\r\n*2\r\n:10\r\n*1\r\n+1\r\n*2\r\n:20\r\n*1\r\n+2\r\n\
15479 *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
15480 );
15481 assert_eq!(
15483 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
15484 "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
15485 *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15486 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15487 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15488 *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
15489 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15490 );
15491 assert_eq!(
15494 f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
15495 "*2\r\n*2\r\n:20\r\n*2\r\n+2\r\n+NaN\r\n*2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n"
15496 );
15497 assert_eq!(
15498 f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
15499 "*2\r\n*2\r\n:40\r\n*2\r\n+4\r\n+4\r\n*2\r\n:50\r\n*2\r\n+5\r\n+5\r\n"
15500 );
15501 assert_eq!(
15504 f.run(&[
15505 b"TS.NREVRANGE",
15506 b"2",
15507 b"x",
15508 b"y",
15509 b"-",
15510 b"+",
15511 b"COUNT",
15512 b"1"
15513 ]),
15514 "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15515 );
15516 assert_eq!(
15517 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
15518 "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
15519 );
15520 assert_eq!(
15522 f.run(&[
15523 b"TS.NRANGE",
15524 b"2",
15525 b"x",
15526 b"y",
15527 b"-",
15528 b"+",
15529 b"FILTER_BY_VALUE",
15530 b"2",
15531 b"30"
15532 ]),
15533 "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15534 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15535 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15536 *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
15537 );
15538 }
15539
15540 #[test]
15544 fn an_nrange_aggregation_names_one_reduction_a_key() {
15545 let mut f = joined();
15546 assert_eq!(
15547 f.run(&[
15548 b"TS.NRANGE",
15549 b"2",
15550 b"x",
15551 b"y",
15552 b"-",
15553 b"+",
15554 b"AGGREGATION",
15555 b"sum",
15556 b"sum",
15557 b"20"
15558 ]),
15559 "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
15560 *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
15561 *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
15562 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15563 );
15564 assert_eq!(
15566 f.run(&[
15567 b"TS.NRANGE",
15568 b"2",
15569 b"x",
15570 b"y",
15571 b"-",
15572 b"+",
15573 b"AGGREGATION",
15574 b"sum,count",
15575 b"avg",
15576 b"20"
15577 ]),
15578 "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
15579 *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
15580 *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
15581 *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
15582 );
15583 assert_eq!(
15586 f.run(&[
15587 b"TS.NRANGE",
15588 b"2",
15589 b"x",
15590 b"y",
15591 b"-",
15592 b"+",
15593 b"AGGREGATION",
15594 b"avg",
15595 b"sum",
15596 b"100",
15597 b"EMPTY",
15598 b"BUCKETTIMESTAMP",
15599 b"end"
15600 ]),
15601 "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
15602 );
15603 assert_eq!(
15606 f.run(&[
15607 b"TS.NRANGE",
15608 b"2",
15609 b"x",
15610 b"y",
15611 b"-",
15612 b"+",
15613 b"AGGREGATION",
15614 b"avg",
15615 b"COUNT",
15616 b"100"
15617 ]),
15618 "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
15619 );
15620 }
15621
15622 #[test]
15625 fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
15626 let mut f = joined();
15627 f.run(&[b"SET", b"str", b"hi"]);
15628 let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
15629 let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
15630 must be equal to numkeys\r\n";
15631 let cases: &[(&[&[u8]], &str)] = &[
15632 (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
15633 (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
15634 (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
15635 (
15638 &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
15639 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15640 ),
15641 (
15642 &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
15643 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15644 ),
15645 (
15648 &[
15649 b"TS.NRANGE",
15650 b"2",
15651 b"x",
15652 b"y",
15653 b"abc",
15654 b"+",
15655 b"AGGREGATION",
15656 b"nope",
15657 b"sum",
15658 b"100",
15659 ],
15660 "-ERR TSDB: Unknown aggregation type\r\n",
15661 ),
15662 (
15663 &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
15664 "-ERR TSDB: wrong fromTimestamp\r\n",
15665 ),
15666 (
15667 &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
15668 "-ERR TSDB: wrong toTimestamp\r\n",
15669 ),
15670 (
15674 &[
15675 b"TS.NRANGE",
15676 b"2",
15677 b"x",
15678 b"y",
15679 b"-",
15680 b"+",
15681 b"AGGREGATION",
15682 b"avg",
15683 ],
15684 numkeys,
15685 ),
15686 (
15687 &[
15688 b"TS.NRANGE",
15689 b"2",
15690 b"x",
15691 b"y",
15692 b"-",
15693 b"+",
15694 b"AGGREGATION",
15695 b"100",
15696 b"sum",
15697 b"100",
15698 ],
15699 numkeys,
15700 ),
15701 (
15702 &[
15703 b"TS.NRANGE",
15704 b"2",
15705 b"x",
15706 b"y",
15707 b"-",
15708 b"+",
15709 b"AGGREGATION",
15710 b"avg",
15711 b"sum",
15712 b"sum",
15713 b"100",
15714 ],
15715 numkeys,
15716 ),
15717 (
15718 &[
15719 b"TS.NRANGE",
15720 b"2",
15721 b"x",
15722 b"y",
15723 b"-",
15724 b"+",
15725 b"AGGREGATION",
15726 b"avg",
15727 b"sum",
15728 b"abc",
15729 ],
15730 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15731 ),
15732 (
15733 &[
15734 b"TS.NRANGE",
15735 b"2",
15736 b"x",
15737 b"y",
15738 b"-",
15739 b"+",
15740 b"AGGREGATION",
15741 b"avg",
15742 b"sum",
15743 b"0",
15744 ],
15745 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15746 ),
15747 (
15750 &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
15751 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15752 ),
15753 (
15754 &[
15755 b"TS.NRANGE",
15756 b"1",
15757 b"x",
15758 b"-",
15759 b"+",
15760 b"AGGREGATION",
15761 b"100",
15762 b"200",
15763 ],
15764 "-ERR TSDB: Unknown aggregation type\r\n",
15765 ),
15766 (
15768 &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
15769 "-ERR TSDB: the key does not exist\r\n",
15770 ),
15771 (
15772 &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
15773 "-ERR WRONGTYPE Operation against a key \
15774 holding the wrong kind of value\r\n",
15775 ),
15776 ];
15777 for (argv, want) in cases {
15778 let got = f.run(argv);
15779 assert_eq!(&got, want, "{argv:?}");
15780 }
15781 }
15782
15783 #[test]
15785 fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
15786 let mut f = joined();
15787 assert_eq!(
15788 f.run(&[b"TS.READ", b"x", b"-"]),
15789 "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
15790 *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
15791 );
15792 assert_eq!(
15795 f.run(&[b"TS.READ", b"x", b"+"]),
15796 "*1\r\n*2\r\n:50\r\n+5\r\n"
15797 );
15798 assert_eq!(
15799 f.run(&[b"TS.READ", b"x", b"25"]),
15800 "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
15801 );
15802 assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
15805 assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
15806 assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
15807 assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
15808 assert_eq!(
15812 f.run(&[b"TS.READ", b"x", b"abc"]),
15813 "-TSDB: invalid timestamp\r\n"
15814 );
15815 assert_eq!(
15816 f.run(&[b"TS.READ", b"x", b"-1"]),
15817 "-TSDB: invalid timestamp\r\n"
15818 );
15819 f.run(&[b"SET", b"str", b"hi"]);
15820 assert_eq!(
15821 f.run(&[b"TS.READ", b"str", b"-"]),
15822 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15823 );
15824 assert_eq!(
15827 f.run(&[b"TS.READ", b"x"]),
15828 "-ERR wrong number of arguments for 'ts.read' command\r\n"
15829 );
15830 assert_eq!(
15831 f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
15832 "-ERR wrong number of arguments for 'ts.read' command\r\n"
15833 );
15834 }
15835
15836 #[test]
15839 fn getkeys_reads_the_count_of_a_joined_read() {
15840 let mut f = Fixture::new();
15841 assert_eq!(
15842 f.run(&[
15843 b"COMMAND",
15844 b"GETKEYS",
15845 b"TS.NRANGE",
15846 b"2",
15847 b"a",
15848 b"b",
15849 b"-",
15850 b"+"
15851 ]),
15852 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
15853 );
15854 assert_eq!(
15855 f.run(&[
15856 b"COMMAND",
15857 b"GETKEYS",
15858 b"TS.NREVRANGE",
15859 b"1",
15860 b"a",
15861 b"-",
15862 b"+"
15863 ]),
15864 "*1\r\n$1\r\na\r\n"
15865 );
15866 for n in [b"0".as_slice(), b"9", b"abc"] {
15869 assert_eq!(
15870 f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
15871 "-ERR Invalid arguments specified for command\r\n"
15872 );
15873 }
15874 }
15875
15876 fn labelled() -> Fixture {
15878 let mut f = Fixture::new();
15879 f.run(&[
15880 b"TS.CREATE",
15881 b"a",
15882 b"LABELS",
15883 b"room",
15884 b"kitchen",
15885 b"x",
15886 b"1",
15887 ]);
15888 f.run(&[
15889 b"TS.CREATE",
15890 b"b",
15891 b"LABELS",
15892 b"room",
15893 b"bedroom",
15894 b"x",
15895 b"2",
15896 ]);
15897 f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
15898 f.run(&[b"TS.CREATE", b"d"]);
15899 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
15900 f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
15901 f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
15902 f
15903 }
15904
15905 #[test]
15908 fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
15909 let mut f = labelled();
15910 let cases: &[(&[&[u8]], &str)] = &[
15911 (
15914 &[b"TS.QUERYINDEX", b"room=kitchen"],
15915 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15916 ),
15917 (
15918 &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
15919 "*1\r\n$1\r\na\r\n",
15920 ),
15921 (
15922 &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
15923 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
15924 ),
15925 (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
15928 (
15930 &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
15931 "*1\r\n$1\r\nc\r\n",
15932 ),
15933 (
15934 &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
15935 "*1\r\n$1\r\na\r\n",
15936 ),
15937 (
15938 &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
15939 "-ERR TSDB: please provide at least one matcher\r\n",
15940 ),
15941 (
15944 &[b"TS.QUERYINDEX", b"room==kitchen"],
15945 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15946 ),
15947 (
15948 &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
15949 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15950 ),
15951 (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
15952 (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
15955 (
15956 &[b"TS.QUERYINDEX", b"=(1)"],
15957 "-ERR TSDB: failed parsing labels\r\n",
15958 ),
15959 (
15960 &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
15961 "-ERR TSDB: failed parsing labels\r\n",
15962 ),
15963 (
15964 &[b"TS.QUERYINDEX", b"room=(kitchen"],
15965 "-ERR TSDB: failed parsing labels\r\n",
15966 ),
15967 (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
15968 (
15969 &[b"TS.QUERYINDEX", b"nonsense"],
15970 "-ERR TSDB: failed parsing labels\r\n",
15971 ),
15972 (
15974 &[b"TS.QUERYINDEX", b"room!=kitchen"],
15975 "-ERR TSDB: please provide at least one matcher\r\n",
15976 ),
15977 (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
15979 (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
15980 (
15981 &[b"TS.QUERYINDEX"],
15982 "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
15983 ),
15984 ];
15985 for (argv, want) in cases {
15986 let got = f.run(argv);
15987 assert_eq!(&got, want, "{:?}", argv.last());
15988 }
15989 }
15990
15991 #[test]
15993 fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
15994 let mut f = labelled();
15995 let cases: &[(&[&[u8]], &str)] = &[
15996 (
15997 &[b"TS.QUERYLABELS", b"LABELS"],
15998 "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
15999 ),
16000 (
16001 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16002 "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16003 ),
16004 (
16005 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16006 "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16007 ),
16008 (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16011 (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16012 (
16013 &[b"TS.QUERYLABELS", b"VALUES"],
16014 "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16015 ),
16016 (
16017 &[b"TS.QUERYLABELS", b"ZZZ"],
16018 "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16019 ),
16020 (
16021 &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16022 "-ERR TSDB: unknown argument, expected FILTER\r\n",
16023 ),
16024 (
16025 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16026 "-ERR TSDB: FILTER given with no filter expressions\r\n",
16027 ),
16028 (
16032 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16033 "-ERR TSDB: please provide at least one matcher\r\n",
16034 ),
16035 (
16036 &[
16037 b"TS.QUERYLABELS",
16038 b"LABELS",
16039 b"FILTER",
16040 b"room=kitchen",
16041 b"x=",
16042 ],
16043 "*1\r\n$4\r\nroom\r\n",
16044 ),
16045 ];
16046 for (argv, want) in cases {
16047 let got = f.run(argv);
16048 assert_eq!(&got, want, "{:?}", argv.last());
16049 }
16050 }
16051
16052 #[test]
16055 fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16056 let mut f = labelled();
16057 let cases: &[(&[&[u8]], &str)] = &[
16058 (
16061 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16062 "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16063 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16064 ),
16065 (
16066 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16067 "*2\r\n*3\r\n$1\r\na\r\n*2\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16068 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16069 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16070 ),
16071 (
16073 &[
16074 b"TS.MGET",
16075 b"SELECTED_LABELS",
16076 b"x",
16077 b"FILTER",
16078 b"room=kitchen",
16079 ],
16080 "*2\r\n*3\r\n$1\r\na\r\n*1\r\n*2\r\n$1\r\nx\r\n$1\r\n1\r\n\
16081 *2\r\n:100\r\n+1.5\r\n\
16082 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n*0\r\n",
16083 ),
16084 (
16087 &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16088 "*1\r\n*3\r\n$1\r\ne\r\n*1\r\n*2\r\n$1\r\nr\r\n$2\r\nbb\r\n*0\r\n",
16089 ),
16090 (
16091 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16092 "*1\r\n*3\r\n$1\r\ne\r\n*2\r\n*2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16093 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16094 ),
16095 (
16098 &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16099 "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16100 ),
16101 (
16102 &[b"TS.MGET", b"a", b"b", b"c"],
16103 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16104 ),
16105 (
16106 &[b"TS.MGET", b"FILTER"],
16107 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16108 ),
16109 (
16112 &[
16113 b"TS.MGET",
16114 b"WITHLABELS",
16115 b"SELECTED_LABELS",
16116 b"x",
16117 b"FILTER",
16118 b"bad",
16119 ],
16120 "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16121 ),
16122 (
16123 &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16124 "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16125 ),
16126 ];
16127 for (argv, want) in cases {
16128 let got = f.run(argv);
16129 assert_eq!(&got, want, "{:?}", argv.last());
16130 }
16131 }
16132
16133 #[test]
16136 fn resp3_writes_the_label_surface_as_sets_and_maps() {
16137 let mut f = labelled();
16138 f.out = Out::new(Proto::Resp3);
16139 let cases: &[(&[&[u8]], &str)] = &[
16140 (
16141 &[b"TS.QUERYINDEX", b"room=kitchen"],
16142 "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16143 ),
16144 (
16145 &[b"TS.QUERYLABELS", b"LABELS"],
16146 "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16147 ),
16148 (
16149 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16150 "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16151 ),
16152 (
16155 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16156 "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16157 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16158 ),
16159 (
16160 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16161 "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16162 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16163 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16164 ),
16165 (
16166 &[
16167 b"TS.MGET",
16168 b"SELECTED_LABELS",
16169 b"x",
16170 b"FILTER",
16171 b"room=kitchen",
16172 ],
16173 "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16174 *2\r\n:100\r\n,1.5\r\n\
16175 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16176 ),
16177 (
16180 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16181 "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16182 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16183 ),
16184 ];
16185 for (argv, want) in cases {
16186 let got = f.run(argv);
16187 assert_eq!(&got, want, "{:?}", argv.last());
16188 }
16189 }
16190
16191 fn spanned() -> Fixture {
16194 let mut f = labelled();
16195 f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16196 f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16197 f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16198 f
16199 }
16200
16201 #[test]
16204 fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16205 let mut f = spanned();
16206 let cases: &[(&[&[u8]], &str)] = &[
16207 (
16208 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16209 "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16210 *3\r\n$1\r\nc\r\n*0\r\n*2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16211 ),
16212 (
16215 &[
16216 b"TS.MREVRANGE",
16217 b"-",
16218 b"+",
16219 b"WITHLABELS",
16220 b"FILTER",
16221 b"room=kitchen",
16222 ],
16223 "*2\r\n*3\r\n$1\r\na\r\n*2\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16224 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n*2\r\n:200\r\n+2.5\r\n*2\r\n:100\r\n+1.5\r\n\
16225 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16226 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16227 ),
16228 (
16231 &[
16232 b"TS.MRANGE",
16233 b"-",
16234 b"+",
16235 b"SELECTED_LABELS",
16236 b"x",
16237 b"FILTER",
16238 b"room=kitchen",
16239 ],
16240 "*2\r\n*3\r\n$1\r\na\r\n*1\r\n*2\r\n$1\r\nx\r\n$1\r\n1\r\n\
16241 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16242 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16243 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16244 ),
16245 (
16248 &[
16249 b"TS.MRANGE",
16250 b"-",
16251 b"+",
16252 b"FILTER",
16253 b"room=kitchen",
16254 b"GROUPBY",
16255 b"room",
16256 b"REDUCE",
16257 b"sum",
16258 ],
16259 "*1\r\n*3\r\n$12\r\nroom=kitchen\r\n*0\r\n*3\r\n*2\r\n:100\r\n+11.5\r\n\
16260 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16261 ),
16262 (
16265 &[
16266 b"TS.MRANGE",
16267 b"-",
16268 b"+",
16269 b"WITHLABELS",
16270 b"FILTER",
16271 b"room=kitchen",
16272 b"GROUPBY",
16273 b"room",
16274 b"REDUCE",
16275 b"max",
16276 ],
16277 "*1\r\n*3\r\n$12\r\nroom=kitchen\r\n*3\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16278 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16279 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16280 *3\r\n*2\r\n:100\r\n+10\r\n*2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16281 ),
16282 (
16284 &[
16285 b"TS.MREVRANGE",
16286 b"-",
16287 b"+",
16288 b"COUNT",
16289 b"1",
16290 b"FILTER",
16291 b"room=kitchen",
16292 b"GROUPBY",
16293 b"room",
16294 b"REDUCE",
16295 b"count",
16296 ],
16297 "*1\r\n*3\r\n$12\r\nroom=kitchen\r\n*0\r\n*1\r\n*2\r\n:300\r\n+1\r\n",
16298 ),
16299 (
16301 &[
16302 b"TS.MRANGE",
16303 b"-",
16304 b"+",
16305 b"FILTER",
16306 b"room=kitchen",
16307 b"GROUPBY",
16308 b"nope",
16309 b"REDUCE",
16310 b"sum",
16311 ],
16312 "*0\r\n",
16313 ),
16314 (
16315 &[
16316 b"TS.MRANGE",
16317 b"-",
16318 b"+",
16319 b"AGGREGATION",
16320 b"sum,avg",
16321 b"100",
16322 b"FILTER",
16323 b"room=bedroom",
16324 ],
16325 "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*1\r\n*3\r\n:200\r\n+2\r\n+2\r\n",
16326 ),
16327 (
16329 &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16330 "-ERR TSDB: missing FILTER argument\r\n",
16331 ),
16332 (
16333 &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16334 "-ERR TSDB: missing labels for filter argument\r\n",
16335 ),
16336 (
16337 &[
16338 b"TS.MRANGE",
16339 b"-",
16340 b"+",
16341 b"GROUPBY",
16342 b"room",
16343 b"REDUCE",
16344 b"sum",
16345 b"FILTER",
16346 b"room=kitchen",
16347 ],
16348 "-ERR TSDB: GROUPBY should always come after filter\r\n",
16349 ),
16350 (
16353 &[
16354 b"TS.MRANGE",
16355 b"-",
16356 b"+",
16357 b"FILTER",
16358 b"room=kitchen",
16359 b"GROUPBY",
16360 b"room",
16361 b"REDUCE",
16362 b"sum",
16363 b"x",
16364 ],
16365 "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16366 ),
16367 (
16369 &[
16370 b"TS.MRANGE",
16371 b"-",
16372 b"+",
16373 b"FILTER",
16374 b"nope",
16375 b"GROUPBY",
16376 b"room",
16377 b"REDUCE",
16378 b"sum",
16379 b"x",
16380 ],
16381 "-ERR TSDB: failed parsing labels\r\n",
16382 ),
16383 (
16384 &[
16385 b"TS.MRANGE",
16386 b"-",
16387 b"+",
16388 b"FILTER",
16389 b"room=kitchen",
16390 b"GROUPBY",
16391 b"room",
16392 b"REDUCE",
16393 b"twa",
16394 ],
16395 "-ERR TSDB: Invalid reducer type\r\n",
16396 ),
16397 (
16398 &[
16399 b"TS.MRANGE",
16400 b"-",
16401 b"+",
16402 b"AGGREGATION",
16403 b"sum,avg",
16404 b"100",
16405 b"FILTER",
16406 b"room=kitchen",
16407 b"GROUPBY",
16408 b"room",
16409 b"REDUCE",
16410 b"sum",
16411 ],
16412 "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
16413 ),
16414 (
16417 &[
16418 b"TS.MRANGE",
16419 b"-",
16420 b"+",
16421 b"SELECTED_LABELS",
16422 b"COUNT",
16423 b"FILTER",
16424 b"room=kitchen",
16425 ],
16426 "-ERR TSDB: Couldn't parse COUNT\r\n",
16427 ),
16428 ];
16429 for (argv, want) in cases {
16430 let got = f.run(argv);
16431 assert_eq!(&got, want, "{argv:?}");
16432 }
16433 }
16434
16435 #[test]
16438 fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
16439 let mut f = spanned();
16440 f.out = Out::new(Proto::Resp3);
16441 let cases: &[(&[&[u8]], &str)] = &[
16442 (
16443 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
16444 "%1\r\n$1\r\nb\r\n*3\r\n%0\r\n%1\r\n$11\r\naggregators\r\n*0\r\n\
16445 *1\r\n*2\r\n:200\r\n,2\r\n",
16446 ),
16447 (
16450 &[
16451 b"TS.MRANGE",
16452 b"-",
16453 b"+",
16454 b"AGGREGATION",
16455 b"sum,avg",
16456 b"100",
16457 b"FILTER",
16458 b"room=bedroom",
16459 ],
16460 "%1\r\n$1\r\nb\r\n*3\r\n%0\r\n%1\r\n$11\r\naggregators\r\n*2\r\n$3\r\nsum\r\n\
16461 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
16462 ),
16463 (
16464 &[
16465 b"TS.MRANGE",
16466 b"-",
16467 b"+",
16468 b"FILTER",
16469 b"room=kitchen",
16470 b"GROUPBY",
16471 b"room",
16472 b"REDUCE",
16473 b"sum",
16474 ],
16475 "%1\r\n$12\r\nroom=kitchen\r\n*4\r\n%0\r\n%1\r\n$8\r\nreducers\r\n*1\r\n\
16476 $3\r\nsum\r\n%1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16477 *3\r\n*2\r\n:100\r\n,11.5\r\n*2\r\n:200\r\n,2.5\r\n*2\r\n:300\r\n,30\r\n",
16478 ),
16479 (
16482 &[
16483 b"TS.MRANGE",
16484 b"-",
16485 b"+",
16486 b"WITHLABELS",
16487 b"FILTER",
16488 b"room=kitchen",
16489 b"GROUPBY",
16490 b"room",
16491 b"REDUCE",
16492 b"max",
16493 ],
16494 "%1\r\n$12\r\nroom=kitchen\r\n*4\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16495 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
16496 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16497 *3\r\n*2\r\n:100\r\n,10\r\n*2\r\n:200\r\n,2.5\r\n*2\r\n:300\r\n,30\r\n",
16498 ),
16499 (
16500 &[
16501 b"TS.MRANGE",
16502 b"-",
16503 b"+",
16504 b"FILTER",
16505 b"room=kitchen",
16506 b"GROUPBY",
16507 b"nope",
16508 b"REDUCE",
16509 b"sum",
16510 ],
16511 "%0\r\n",
16512 ),
16513 ];
16514 for (argv, want) in cases {
16515 let got = f.run(argv);
16516 assert_eq!(&got, want, "{argv:?}");
16517 }
16518 }
16519
16520 #[test]
16522 fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
16523 let mut f = Fixture::new();
16524 f.run(&[b"TS.CREATE", b"src"]);
16525 f.run(&[b"TS.CREATE", b"dst"]);
16526 f.run(&[b"SET", b"plain", b"v"]);
16527 let cases: &[(&[&[u8]], &str)] = &[
16528 (
16532 &[
16533 b"TS.CREATERULE",
16534 b"src",
16535 b"dst",
16536 b"AGGREGATION",
16537 b"nope",
16538 b"x",
16539 ],
16540 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16541 ),
16542 (
16543 &[
16544 b"TS.CREATERULE",
16545 b"src",
16546 b"dst",
16547 b"AGGREGATION",
16548 b"nope",
16549 b"10",
16550 ],
16551 "-ERR TSDB: Unknown aggregation type\r\n",
16552 ),
16553 (
16554 &[
16555 b"TS.CREATERULE",
16556 b"src",
16557 b"dst",
16558 b"AGGREGATION",
16559 b"avg",
16560 b"0",
16561 ],
16562 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16563 ),
16564 (
16565 &[
16566 b"TS.CREATERULE",
16567 b"src",
16568 b"dst",
16569 b"AGGREGATION",
16570 b"avg",
16571 b"10",
16572 b"x",
16573 ],
16574 "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
16575 ),
16576 (
16577 &[
16578 b"TS.CREATERULE",
16579 b"src",
16580 b"src",
16581 b"AGGREGATION",
16582 b"avg",
16583 b"10",
16584 ],
16585 "-ERR TSDB: the source key and destination key should be different\r\n",
16586 ),
16587 (
16591 &[
16592 b"TS.CREATERULE",
16593 b"nope",
16594 b"plain",
16595 b"AGGREGATION",
16596 b"avg",
16597 b"10",
16598 ],
16599 "-ERR TSDB: the key does not exist\r\n",
16600 ),
16601 (
16602 &[
16603 b"TS.CREATERULE",
16604 b"src",
16605 b"nope",
16606 b"AGGREGATION",
16607 b"avg",
16608 b"10",
16609 ],
16610 "-ERR TSDB: the key does not exist\r\n",
16611 ),
16612 (
16615 &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
16616 "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
16617 ),
16618 (
16619 &[
16620 b"TS.CREATERULE",
16621 b"src",
16622 b"dst",
16623 b"AGGREGATION",
16624 b"avg",
16625 b"10",
16626 ],
16627 "+OK\r\n",
16628 ),
16629 (
16632 &[
16633 b"TS.CREATERULE",
16634 b"src",
16635 b"dst",
16636 b"AGGREGATION",
16637 b"avg",
16638 b"10",
16639 ],
16640 "-ERR TSDB: the destination key already has a src rule\r\n",
16641 ),
16642 (
16645 &[
16646 b"TS.CREATERULE",
16647 b"dst",
16648 b"src",
16649 b"AGGREGATION",
16650 b"avg",
16651 b"10",
16652 ],
16653 "-ERR TSDB: the source key already has a source rule\r\n",
16654 ),
16655 (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
16656 (
16657 &[b"TS.DELETERULE", b"src", b"dst"],
16658 "-ERR TSDB: compaction rule does not exist\r\n",
16659 ),
16660 (
16664 &[b"TS.DELETERULE", b"src", b"nope"],
16665 "-ERR TSDB: compaction rule does not exist\r\n",
16666 ),
16667 (
16668 &[b"TS.DELETERULE", b"nope", b"dst"],
16669 "-ERR TSDB: the key does not exist\r\n",
16670 ),
16671 ];
16672 for (argv, want) in cases {
16673 let got = f.run(argv);
16674 assert_eq!(&got, want, "{argv:?}");
16675 }
16676 }
16677
16678 #[test]
16680 fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
16681 let mut f = Fixture::new();
16682 f.run(&[b"TS.CREATE", b"src"]);
16683 f.run(&[b"TS.CREATE", b"dst"]);
16684 f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
16687 f.run(&[
16688 b"TS.CREATERULE",
16689 b"src",
16690 b"dst",
16691 b"AGGREGATION",
16692 b"sum",
16693 b"100",
16694 ]);
16695 f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
16696 assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
16697 assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
16700 f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
16701 assert_eq!(
16702 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16703 "*1\r\n*2\r\n:0\r\n+2\r\n"
16704 );
16705 f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
16708 assert_eq!(
16709 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16710 "*1\r\n*2\r\n:0\r\n+11\r\n"
16711 );
16712 assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
16715 assert_eq!(
16716 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16717 "*1\r\n*2\r\n:0\r\n+8\r\n"
16718 );
16719 assert_eq!(
16720 f.run(&[b"TS.GET", b"dst", b"LATEST"]),
16721 "*2\r\n:100\r\n+4\r\n"
16722 );
16723 assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
16725 f.run(&[b"DEL", b"dst"]);
16726 assert_eq!(
16727 f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
16728 "-ERR TSDB: compaction rule does not exist\r\n"
16729 );
16730 }
16731
16732 #[test]
16735 fn xadd_ids_only_ever_go_up() {
16736 let mut f = Fixture::new();
16737 assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
16739 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
16741 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
16742 assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
16743 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
16744
16745 assert!(
16746 f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
16747 .contains("equal or smaller")
16748 );
16749 assert!(
16750 f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
16751 .contains("must be greater than 0-0")
16752 );
16753 assert!(
16754 f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
16755 .contains("Invalid stream ID")
16756 );
16757 assert!(
16760 f.run(&[b"XADD", b"s", b"*", b"a"])
16761 .contains("wrong number of arguments")
16762 );
16763
16764 assert_eq!(
16767 f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
16768 "$-1\r\n"
16769 );
16770 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
16771 assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
16772 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
16773 }
16774
16775 #[test]
16778 fn trimming_reads_its_options_the_way_redis_does() {
16779 let mut f = Fixture::new();
16780 for i in 1..=10u32 {
16781 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
16782 }
16783 assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
16784 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
16785 assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
16786 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
16787
16788 assert!(
16792 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
16793 .contains("not an integer")
16794 );
16795 assert!(
16796 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
16797 .contains("MAXLEN argument must be >= 0")
16798 );
16799 assert!(
16802 f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
16803 .contains("without specifying a trimming strategy")
16804 );
16805 assert!(
16806 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
16807 .contains("without the special ~ option")
16808 );
16809 assert!(
16810 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
16811 .contains("at the same time are not compatible")
16812 );
16813 assert!(
16815 f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
16816 .contains("syntax error")
16817 );
16818 assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
16819 }
16820
16821 #[test]
16823 fn xrange_looks_the_key_up_before_it_reads_the_count() {
16824 let mut f = Fixture::new();
16825 f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
16826 f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
16827
16828 assert_eq!(
16829 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
16830 "*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\
16831 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
16832 );
16833 assert_eq!(
16834 f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
16835 "*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"
16836 );
16837 assert_eq!(
16841 f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
16842 "*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\
16843 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
16844 );
16845 assert_eq!(
16846 f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
16847 "*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"
16848 );
16849 assert!(
16850 f.run(&[b"XRANGE", b"s", b"(-", b"+"])
16851 .contains("Invalid stream ID")
16852 );
16853
16854 assert_eq!(
16858 f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
16859 "*0\r\n"
16860 );
16861 assert_eq!(
16862 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
16863 "*-1\r\n"
16864 );
16865 f.run(&[b"SET", b"str", b"v"]);
16866 assert!(
16867 f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
16868 .starts_with("-WRONGTYPE")
16869 );
16870 assert_eq!(
16872 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
16873 "*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"
16874 );
16875 }
16876
16877 #[test]
16879 fn a_bad_id_late_in_the_list_stops_the_whole_command() {
16880 let mut f = Fixture::new();
16881 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
16882 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
16883 assert!(
16884 f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
16885 .contains("Invalid stream ID")
16886 );
16887 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
16888 assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
16889 assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
16890 assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
16891 assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
16892 }
16893
16894 #[test]
16896 fn xgroup_has_an_arity_per_subcommand() {
16897 let mut f = Fixture::new();
16898 assert!(
16899 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
16900 .contains("requires the key")
16901 );
16902 assert_eq!(
16903 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
16904 "+OK\r\n"
16905 );
16906 assert!(
16909 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
16910 .starts_with("-BUSYGROUP")
16911 );
16912 assert_eq!(
16913 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
16914 ":1\r\n"
16915 );
16916 assert_eq!(
16917 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
16918 ":0\r\n"
16919 );
16920 assert_eq!(
16921 f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
16922 ":0\r\n"
16923 );
16924
16925 let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
16927 assert!(
16928 short.contains("wrong number of arguments for 'xgroup|destroy' command"),
16929 "{short}"
16930 );
16931 let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
16933 assert!(
16934 odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
16935 "{odd}"
16936 );
16937 assert!(
16938 f.run(&[b"XGROUP", b"NOSUCH", b"s"])
16939 .contains("Try XGROUP HELP")
16940 );
16941
16942 assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
16943 assert!(
16944 f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
16945 .starts_with("-NOGROUP")
16946 );
16947 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
16948 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
16949 assert!(
16950 f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
16951 .contains("requires the key")
16952 );
16953 }
16954
16955 #[test]
16957 fn xreadgroup_hands_out_and_xack_takes_back() {
16958 let mut f = Fixture::new();
16959 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
16960 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
16961 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
16962
16963 let first = f.run(&[
16964 b"XREADGROUP",
16965 b"GROUP",
16966 b"g",
16967 b"c1",
16968 b"COUNT",
16969 b"1",
16970 b"STREAMS",
16971 b"s",
16972 b">",
16973 ]);
16974 assert_eq!(
16975 first,
16976 "*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"
16977 );
16978 assert_eq!(
16981 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
16982 "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
16983 );
16984 assert_eq!(
16985 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
16986 "*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"
16987 );
16988
16989 assert_eq!(
16990 f.run(&[b"XPENDING", b"s", b"g"]),
16991 "*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"
16992 );
16993 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
16994 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
16995 assert_eq!(
16997 f.run(&[b"XPENDING", b"s", b"g"]),
16998 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
16999 );
17000
17001 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17004 f.run(&[b"XDEL", b"s", b"2-1"]);
17005 assert_eq!(
17006 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17007 "*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"
17008 );
17009
17010 assert!(
17013 f.run(&[
17014 b"XREADGROUP",
17015 b"GROUP",
17016 b"nope",
17017 b"c",
17018 b"STREAMS",
17019 b"s",
17020 b"+"
17021 ])
17022 .starts_with("-NOGROUP")
17023 );
17024 assert!(
17025 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17026 .contains("meaningless in the context of XREADGROUP")
17027 );
17028 assert!(
17029 f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17030 .contains("only supported by XREADGROUP")
17031 );
17032 assert!(
17033 f.run(&[
17034 b"XREADGROUP",
17035 b"GROUP",
17036 b"g",
17037 b"c",
17038 b"STREAMS",
17039 b"s",
17040 b"a",
17041 b"b"
17042 ])
17043 .contains("Unbalanced 'xreadgroup' list of streams")
17044 );
17045 }
17046
17047 #[test]
17050 fn xread_with_no_block_writes_the_null_itself() {
17051 let mut f = Fixture::new();
17052 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17053 assert_eq!(
17054 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17055 "*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"
17056 );
17057 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17060 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17061 f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17062 assert_eq!(
17063 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17064 "*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"
17065 );
17066 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17068 assert_eq!(
17070 f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17071 "*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"
17072 );
17073 assert_eq!(
17076 f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17077 "*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"
17078 );
17079 assert!(
17081 f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17082 .contains("not an integer")
17083 );
17084 assert!(
17085 f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17086 .contains("timeout is negative")
17087 );
17088 assert!(
17089 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17090 .contains("Unbalanced 'xread' list of streams")
17091 );
17092 }
17093
17094 #[test]
17096 fn a_blocked_xread_wakes_on_the_next_entry() {
17097 let mut f = Fixture::new();
17098 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17099 let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17100 assert_eq!(flow, Flow::Block);
17101 assert!(reply.is_empty());
17102
17103 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17106 assert_eq!(flow, Flow::Block);
17107 assert_eq!(f.server.waiters().len(), 2);
17108
17109 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17110 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";
17111 for at in 0..2 {
17112 let mut out = Out::new(Proto::Resp2);
17113 assert!(f.server.serve_waiter(at, 0, &mut out));
17114 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17115 }
17116
17117 f.server.waiters_mut().forget(7);
17120 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17121 assert_eq!(flow, Flow::Block);
17122 let mut out = Out::new(Proto::Resp2);
17123 assert!(!f.server.serve_waiter(0, 0, &mut out));
17124 assert!(out.as_slice().is_empty());
17125 assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
17126 assert_eq!(
17127 core::str::from_utf8(out.as_slice()).expect("ascii"),
17128 "*-1\r\n"
17129 );
17130 }
17131
17132 #[test]
17134 fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17135 let mut f = Fixture::new();
17136 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17137 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17138 let (flow, _) = f.flow(&[
17139 b"XREADGROUP",
17140 b"GROUP",
17141 b"g",
17142 b"c",
17143 b"BLOCK",
17144 b"0",
17145 b"STREAMS",
17146 b"s",
17147 b">",
17148 ]);
17149 assert_eq!(flow, Flow::Block);
17150
17151 f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17152 let mut out = Out::new(Proto::Resp2);
17153 assert!(f.server.serve_waiter(0, 0, &mut out));
17154 assert_eq!(
17157 core::str::from_utf8(out.as_slice()).expect("ascii"),
17158 "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17159 );
17160 }
17161
17162 #[test]
17164 fn xclaim_reads_ids_until_one_will_not_parse() {
17165 let mut f = Fixture::new();
17166 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17167 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17168 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17169 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17170
17171 assert!(
17174 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17175 .contains("Unrecognized XCLAIM option '-'")
17176 );
17177 assert_eq!(
17178 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17179 "*1\r\n$3\r\n1-1\r\n"
17180 );
17181 f.run(&[b"XDEL", b"s", b"2-1"]);
17184 assert_eq!(
17185 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17186 "*0\r\n"
17187 );
17188 assert!(
17189 f.run(&[b"XPENDING", b"s", b"g"])
17190 .starts_with("*4\r\n:1\r\n")
17191 );
17192 assert!(
17193 f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17194 .starts_with("-NOGROUP")
17195 );
17196 assert!(
17197 f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17198 .contains("Invalid min-idle-time argument for XCLAIM")
17199 );
17200 }
17201
17202 #[test]
17204 fn xautoclaim_reports_what_it_dropped() {
17205 let mut f = Fixture::new();
17206 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17207 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17208 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17209 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17210 f.run(&[b"XDEL", b"s", b"1-1"]);
17211
17212 assert_eq!(
17215 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17216 "*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"
17217 );
17218 assert!(
17219 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17220 .contains("COUNT must be > 0")
17221 );
17222 assert!(
17223 f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17224 .starts_with("-NOGROUP")
17225 );
17226 }
17227
17228 #[test]
17230 fn xdelex_answers_one_integer_an_id() {
17231 let mut f = Fixture::new();
17232 for i in 1..=4 {
17233 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17234 }
17235 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17236 f.run(&[
17237 b"XREADGROUP",
17238 b"GROUP",
17239 b"g",
17240 b"c",
17241 b"COUNT",
17242 b"2",
17243 b"STREAMS",
17244 b"s",
17245 b">",
17246 ]);
17247
17248 assert_eq!(
17250 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17251 "*2\r\n:1\r\n:-1\r\n"
17252 );
17253 assert!(
17256 f.run(&[b"XPENDING", b"s", b"g"])
17257 .starts_with("*4\r\n:2\r\n")
17258 );
17259 assert_eq!(
17261 f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17262 "*1\r\n:1\r\n"
17263 );
17264 assert_eq!(
17266 f.run(&[b"XPENDING", b"s", b"g"]),
17267 "*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"
17268 );
17269
17270 assert_eq!(
17274 f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17275 "*2\r\n:2\r\n:2\r\n"
17276 );
17277
17278 assert_eq!(
17280 f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17281 "*2\r\n:-1\r\n:-1\r\n"
17282 );
17283 assert!(
17285 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17286 .starts_with("-ERR Invalid stream ID")
17287 );
17288 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17289
17290 assert!(
17291 f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17292 .contains("Number of IDs must be a positive integer")
17293 );
17294 assert!(
17295 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17296 .contains("The `numids` parameter must match the number of arguments")
17297 );
17298 assert!(
17301 f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17302 .starts_with("-ERR syntax error")
17303 );
17304 assert!(
17305 f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17306 .starts_with("-ERR syntax error")
17307 );
17308 f.run(&[b"SET", b"str", b"v"]);
17310 assert!(
17311 f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17312 .starts_with("-WRONGTYPE")
17313 );
17314 }
17315
17316 #[test]
17318 fn xackdel_reports_what_the_group_was_holding() {
17319 let mut f = Fixture::new();
17320 for i in 1..=3 {
17321 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17322 }
17323 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17324 f.run(&[
17325 b"XREADGROUP",
17326 b"GROUP",
17327 b"g",
17328 b"c",
17329 b"COUNT",
17330 b"1",
17331 b"STREAMS",
17332 b"s",
17333 b">",
17334 ]);
17335
17336 assert_eq!(
17340 f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17341 "*2\r\n:1\r\n:-1\r\n"
17342 );
17343 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17344
17345 assert_eq!(
17347 f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17348 "*1\r\n:-1\r\n"
17349 );
17350 assert_eq!(
17351 f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17352 "*1\r\n:-1\r\n"
17353 );
17354
17355 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17358 f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17359 assert_eq!(
17360 f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17361 "*1\r\n:2\r\n"
17362 );
17363 assert_eq!(
17364 f.run(&[b"XPENDING", b"s", b"g"]),
17365 "*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"
17366 );
17367 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17368 }
17369
17370 #[test]
17372 fn xnack_releases_an_entry_for_the_next_claim() {
17373 let mut f = Fixture::new();
17374 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17375 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17376 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17377 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17378 f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17381
17382 assert_eq!(
17383 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
17384 ":1\r\n"
17385 );
17386 assert_eq!(
17390 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
17391 "*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"
17392 );
17393 assert_eq!(
17395 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17396 "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
17397 );
17398 assert_eq!(
17400 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17401 "*-1\r\n"
17402 );
17403 assert_eq!(
17405 f.run(&[
17406 b"XAUTOCLAIM",
17407 b"s",
17408 b"g",
17409 b"c2",
17410 b"99999999",
17411 b"-",
17412 b"JUSTID"
17413 ]),
17414 "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
17415 );
17416
17417 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17421 assert!(
17422 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17423 .contains(":-1\r\n:1\r\n")
17424 );
17425 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17427 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17428 assert!(
17429 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17430 .contains(":-1\r\n:0\r\n")
17431 );
17432 f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
17434 assert!(
17435 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17436 .contains(":9223372036854775807\r\n")
17437 );
17438 f.run(&[
17439 b"XNACK",
17440 b"s",
17441 b"g",
17442 b"FATAL",
17443 b"IDS",
17444 b"1",
17445 b"1-1",
17446 b"RETRYCOUNT",
17447 b"3",
17448 ]);
17449 assert!(
17450 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17451 .contains(":-1\r\n:3\r\n")
17452 );
17453
17454 f.run(&[b"XACK", b"s", b"g", b"2-1"]);
17458 assert_eq!(
17459 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
17460 ":0\r\n"
17461 );
17462 assert_eq!(
17463 f.run(&[
17464 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
17465 ]),
17466 ":1\r\n"
17467 );
17468 assert!(
17469 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17470 .contains(":-1\r\n:0\r\n")
17471 );
17472 assert_eq!(
17474 f.run(&[
17475 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
17476 ]),
17477 ":0\r\n"
17478 );
17479
17480 assert_eq!(
17483 f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
17484 "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
17485 );
17486 assert!(
17487 f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
17488 .starts_with("-ERR")
17489 );
17490 assert!(
17492 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
17493 .contains("numids must be a positive integer")
17494 );
17495 assert!(
17496 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
17497 .contains("number of IDs doesn't match numids")
17498 );
17499 assert!(
17502 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
17503 .contains("Unrecognized XNACK option '2-1'")
17504 );
17505 }
17506
17507 #[test]
17509 fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
17510 let mut f = Fixture::new();
17511 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17512 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17513 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17514 f.run(&[
17515 b"XREADGROUP",
17516 b"GROUP",
17517 b"g",
17518 b"c1",
17519 b"COUNT",
17520 b"1",
17521 b"STREAMS",
17522 b"s",
17523 b">",
17524 ]);
17525
17526 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17527 assert!(info.starts_with("*20\r\n"), "{info}");
17530 assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
17531 assert!(
17532 info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
17533 "{info}"
17534 );
17535 assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
17536 assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
17537
17538 let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
17539 assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
17540 assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
17541 assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
17542 assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
17543
17544 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
17548 let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
17549 assert!(consumers.starts_with("*2\r\n"), "{consumers}");
17550 assert!(
17551 consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
17552 "{consumers}"
17553 );
17554 let c1 = consumers.find("c1").unwrap();
17556 let c2 = consumers.find("c2").unwrap();
17557 assert!(c1 < c2, "{consumers}");
17558
17559 let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
17560 assert!(full.starts_with("*18\r\n"), "{full}");
17561 assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
17562 assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
17563
17564 assert!(
17565 f.run(&[b"XINFO", b"STREAM", b"missing"])
17566 .contains("no such key")
17567 );
17568 assert!(
17569 f.run(&[b"XINFO", b"GROUPS", b"missing"])
17570 .contains("no such key")
17571 );
17572 assert!(
17573 f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
17574 .starts_with("-NOGROUP")
17575 );
17576 assert!(
17577 f.run(&[b"XINFO", b"NOSUCH", b"s"])
17578 .contains("Try XINFO HELP")
17579 );
17580 assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
17581 assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
17582 }
17583
17584 #[test]
17586 fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
17587 let mut f = Fixture::new();
17588 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17589 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17590 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17591
17592 let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
17593 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");
17594 assert_eq!(
17595 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17596 "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
17597 );
17598 assert_eq!(
17600 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
17601 "*0\r\n"
17602 );
17603 assert_eq!(
17604 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
17605 list
17606 );
17607 assert!(
17609 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
17610 .contains("syntax error")
17611 );
17612 assert!(
17613 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
17614 .contains("syntax error")
17615 );
17616 assert_eq!(
17617 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
17618 "*0\r\n"
17619 );
17620 assert!(
17621 f.run(&[b"XPENDING", b"missing", b"g"])
17622 .starts_with("-NOGROUP")
17623 );
17624 }
17625
17626 #[test]
17628 fn xsetid_will_not_go_below_what_is_there() {
17629 let mut f = Fixture::new();
17630 f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
17631 assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
17632 assert_eq!(
17633 f.run(&[
17634 b"XSETID",
17635 b"s",
17636 b"10-1",
17637 b"ENTRIESADDED",
17638 b"7",
17639 b"MAXDELETEDID",
17640 b"9-1"
17641 ]),
17642 "+OK\r\n"
17643 );
17644 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17645 assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
17646 assert!(
17647 info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
17648 "{info}"
17649 );
17650
17651 assert!(
17652 f.run(&[b"XSETID", b"s", b"1-1"])
17653 .contains("smaller than the target stream top item")
17654 );
17655 assert!(
17656 f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
17657 .contains("entries_added must be positive")
17658 );
17659 assert!(
17660 f.run(&[b"XSETID", b"missing", b"1-1"])
17661 .contains("no such key")
17662 );
17663 }
17664
17665 #[test]
17667 fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
17668 let mut f = Fixture::new();
17669 f.run(&[b"HELLO", b"3"]);
17670 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17671 assert_eq!(
17674 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17675 "%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"
17676 );
17677 assert_eq!(
17680 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17681 "*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"
17682 );
17683 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
17684 }
17685
17686 struct Mem {
17692 blobs: Vec<Vec<u8>>,
17693 }
17694
17695 impl yo_kv::cold::Blocks for Mem {
17696 fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
17697 self.blobs.push(bytes.to_vec());
17698 Ok(yo_common::Addr::new(
17699 yo_common::Space::Log,
17700 (self.blobs.len() - 1) as u64,
17701 ))
17702 }
17703
17704 fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
17705 self.blobs
17706 .get(at.offset() as usize)
17707 .map(Vec::as_slice)
17708 .ok_or_else(|| {
17709 yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
17710 })
17711 }
17712
17713 fn bytes(&self) -> u64 {
17714 self.blobs.iter().map(|b| b.len() as u64).sum()
17715 }
17716 }
17717
17718 fn filled(attach: bool) -> (Fixture, usize) {
17722 let mut f = Fixture::new();
17723 if attach {
17724 f.server
17725 .striped(0)
17726 .stripe_mut(0)
17727 .attach(Box::new(Mem { blobs: Vec::new() }));
17728 }
17729 let val = vec![b'v'; 256];
17730 for i in 0..24000u32 {
17731 let k = format!("key:{i:08}");
17732 f.run(&[b"SET", k.as_bytes(), &val]);
17733 }
17734 let full = f.server.memory_bytes();
17735 assert!(full > 3 * 1024 * 1024, "the arena is several segments");
17736 (f, full)
17737 }
17738
17739 fn press(f: &mut Fixture, limit: usize) {
17745 let val = vec![b'v'; 256];
17746 for i in 0..3000u32 {
17747 let k = format!("new:{i:08}");
17748 assert_eq!(
17749 f.run(&[b"SET", k.as_bytes(), &val]),
17750 "+OK\r\n",
17751 "write {i} was refused"
17752 );
17753 f.server.refresh_memory();
17754 if f.server.memory_bytes() <= limit {
17755 return;
17756 }
17757 }
17758 panic!(
17759 "it never got under: {} against {limit}",
17760 f.server.memory_bytes()
17761 );
17762 }
17763
17764 #[test]
17765 fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
17766 let mut f = Fixture::new();
17767 assert_eq!(
17768 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
17769 "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
17770 "no limit is the default"
17771 );
17772 for (typed, bytes) in [
17775 (&b"0"[..], "0"),
17776 (b"1024", "1024"),
17777 (b"1k", "1000"),
17778 (b"1gb", "1073741824"),
17779 (b"-1", "-1"),
17780 ] {
17781 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
17782 assert_eq!(
17783 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
17784 format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
17785 "set {}",
17786 String::from_utf8_lossy(typed)
17787 );
17788 }
17789 for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
17790 assert_eq!(
17791 f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
17792 "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
17793 "refused {}",
17794 String::from_utf8_lossy(bad)
17795 );
17796 }
17797 let info = f.run(&[b"INFO", b"memory"]);
17799 assert!(info.contains("maxstore:-1"), "{info}");
17800 assert!(info.contains("yo_memory_regime:evict"), "{info}");
17801 assert!(info.contains("yo_store_bytes:0"), "{info}");
17802 }
17803
17804 #[test]
17805 fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
17806 let (mut f, full) = filled(true);
17810 let keys = f.run(&[b"DBSIZE"]);
17811 assert!(
17812 f.run(&[b"INFO", b"memory"])
17813 .contains("yo_memory_regime:migrate"),
17814 "a database with somewhere to put values migrates"
17815 );
17816
17817 let limit = full - 2 * 1024 * 1024;
17818 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17819 f.run(&[
17820 b"CONFIG",
17821 b"SET",
17822 b"maxmemory",
17823 limit.to_string().as_bytes(),
17824 ]);
17825 press(&mut f, limit);
17826
17827 assert!(
17828 f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17829 "nothing was thrown away"
17830 );
17831 let after: usize = f.run(&[b"DBSIZE"])[1..]
17832 .trim_end()
17833 .parse()
17834 .expect("a count");
17835 let before: usize = keys[1..].trim_end().parse().expect("a count");
17836 assert!(after > before, "the keys that came in are all still here");
17837 assert!(
17838 f.server.store_bytes() > 0,
17839 "and what came out of memory went to the file"
17840 );
17841 let val = format!("$256\r\n{}\r\n", "v".repeat(256));
17844 assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
17845 assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
17846 }
17847
17848 #[test]
17849 fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
17850 let (mut f, full) = filled(true);
17854 f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
17855 assert!(
17856 f.run(&[b"INFO", b"memory"])
17857 .contains("yo_memory_regime:evict"),
17858 "nothing may go to the file"
17859 );
17860
17861 let limit = full - 2 * 1024 * 1024;
17862 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17863 f.run(&[
17864 b"CONFIG",
17865 b"SET",
17866 b"maxmemory",
17867 limit.to_string().as_bytes(),
17868 ]);
17869 press(&mut f, limit);
17870
17871 assert!(
17872 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17873 "keys were thrown away, which is what was asked for"
17874 );
17875 assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
17876 }
17877
17878 #[test]
17879 fn a_full_file_goes_back_to_evicting() {
17880 let (mut f, full) = filled(true);
17884 f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
17885 let limit = full - 2 * 1024 * 1024;
17886 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17887 f.run(&[
17888 b"CONFIG",
17889 b"SET",
17890 b"maxmemory",
17891 limit.to_string().as_bytes(),
17892 ]);
17893 press(&mut f, limit);
17894
17895 assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
17896 assert!(
17897 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17898 "and then it started evicting"
17899 );
17900 assert!(
17901 f.run(&[b"INFO", b"memory"])
17902 .contains("yo_memory_regime:evict"),
17903 "and it says so"
17904 );
17905 }
17906 #[test]
17922 fn the_string_group_answers_the_same_however_many_stripes_there_are() {
17923 let script: &[&[&[u8]]] = &[
17924 &[b"SET", b"k1", b"v1"],
17927 &[b"SET", b"k2", b"v2"],
17928 &[b"GET", b"k1"],
17929 &[b"GET", b"nothing"],
17930 &[b"GETSET", b"k1", b"v1b"],
17931 &[b"SETNX", b"k1", b"no"],
17932 &[b"SETNX", b"k3", b"yes"],
17933 &[b"APPEND", b"k3", b"!"],
17934 &[b"STRLEN", b"k3"],
17935 &[b"SETRANGE", b"k3", b"1", b"XY"],
17936 &[b"GETRANGE", b"k3", b"0", b"-1"],
17937 &[b"INCR", b"n1"],
17938 &[b"INCRBY", b"n1", b"41"],
17939 &[b"DECRBY", b"n1", b"2"],
17940 &[b"INCRBYFLOAT", b"f1", b"1.5"],
17941 &[b"SETEX", b"e1", b"100", b"v"],
17942 &[b"PSETEX", b"e2", b"100000", b"v"],
17943 &[b"GETEX", b"e1", b"PERSIST"],
17944 &[b"GETDEL", b"k2"],
17945 &[b"GET", b"k2"],
17946 &[b"DIGEST", b"k1"],
17947 &[b"DELEX", b"k3"],
17948 &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
17951 &[b"MGET", b"a", b"b", b"c", b"missing"],
17952 &[b"MSETNX", b"d", b"4", b"e", b"5"],
17953 &[b"MSETNX", b"e", b"6", b"f", b"7"],
17954 &[b"MGET", b"d", b"e", b"f"],
17955 &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
17956 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
17957 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
17958 &[b"MGET", b"g", b"h"],
17959 &[b"SET", b"s1", b"ohmytext"],
17960 &[b"SET", b"s2", b"mynewtext"],
17961 &[b"LCS", b"s1", b"s2"],
17962 &[b"LCS", b"s1", b"s2", b"LEN"],
17963 &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
17964 &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
17965 &[b"LCS", b"s1", b"gone"],
17966 &[b"MSET", b"odd"],
17968 &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
17969 &[b"MGET"],
17970 ];
17971
17972 let mut one = Fixture::new();
17973 let mut many = Fixture::striped(8);
17974 for parts in script {
17975 let a = one.run(parts);
17976 let b = many.run(parts);
17977 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
17978 }
17979 }
17980
17981 #[test]
17986 fn a_striped_database_spreads_the_keys_it_is_given() {
17987 let mut f = Fixture::striped(8);
17988 for i in 0..256 {
17989 let key = format!("key:{i}");
17990 f.run(&[b"SET", key.as_bytes(), b"v"]);
17991 }
17992 assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
17993 }
17994
17995 #[test]
17998 fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
17999 let mut one = Fixture::new();
18000 let mut many = Fixture::striped(8);
18001 for f in [&mut one, &mut many] {
18002 f.run(&[b"SET", b"str", b"v"]);
18003 f.server
18009 .striped(0)
18010 .at(b"list")
18011 .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18012 .expect("a new list");
18013 }
18014 assert_eq!(
18015 one.run(&[b"MGET", b"str", b"list", b"gone"]),
18016 many.run(&[b"MGET", b"str", b"list", b"gone"])
18017 );
18018 }
18019
18020 #[test]
18028 fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18029 let script: &[&[&[u8]]] = &[
18030 &[b"SET", b"k1", b"v1"],
18031 &[b"SET", b"k2", b"v2"],
18032 &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18033 &[b"TYPE", b"k1"],
18034 &[b"TYPE", b"gone"],
18035 &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18036 &[b"EXPIRE", b"k1", b"100"],
18037 &[b"TTL", b"k1"],
18038 &[b"EXPIRE", b"k1", b"200", b"NX"],
18039 &[b"PERSIST", b"k1"],
18040 &[b"TTL", b"k1"],
18041 &[b"PEXPIREAT", b"k2", b"1900000000000"],
18042 &[b"EXPIRETIME", b"k2"],
18043 &[b"PEXPIRETIME", b"k2"],
18044 &[b"PERSIST", b"k2"],
18045 &[b"OBJECT", b"ENCODING", b"k1"],
18046 &[b"OBJECT", b"REFCOUNT", b"k1"],
18047 &[b"OBJECT", b"IDLETIME", b"k1"],
18048 &[b"OBJECT", b"FREQ", b"k1"],
18049 &[b"OBJECT", b"ENCODING", b"gone"],
18050 &[b"OBJECT", b"HELP"],
18051 &[b"RENAME", b"k1", b"k9"],
18052 &[b"GET", b"k9"],
18053 &[b"RENAME", b"gone", b"x"],
18054 &[b"RENAMENX", b"k9", b"k2"],
18055 &[b"RENAMENX", b"k9", b"k8"],
18056 &[b"GET", b"k8"],
18057 &[b"COPY", b"k8", b"c1"],
18058 &[b"COPY", b"k8", b"c1"],
18059 &[b"COPY", b"k8", b"c1", b"REPLACE"],
18060 &[b"COPY", b"k8", b"k8"],
18061 &[b"COPY", b"gone", b"c2"],
18062 &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18063 &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18064 &[b"MOVE", b"c1", b"1"],
18065 &[b"MOVE", b"c1", b"1"],
18066 &[b"MOVE", b"k8", b"0"],
18067 &[b"DEL", b"k2", b"gone"],
18068 &[b"UNLINK", b"k8", b"k8"],
18069 &[b"DBSIZE"],
18070 ];
18071
18072 let mut one = Fixture::new();
18073 let mut many = Fixture::striped(8);
18074 for parts in script {
18075 let a = one.run(parts);
18076 let b = many.run(parts);
18077 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18078 }
18079
18080 for f in [&mut one, &mut many] {
18086 f.run(&[b"SET", b"d1", b"payload"]);
18087 let payload = f
18088 .server
18089 .striped(0)
18090 .at(b"d1")
18091 .dump(b"d1")
18092 .expect("a key that is there");
18093 assert!(
18094 f.run(&[b"DUMP", b"d1"])
18095 .starts_with(&format!("${}", payload.len())),
18096 "a payload of the length the store gave"
18097 );
18098 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18099 assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18100 assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18101 assert_eq!(
18102 f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18103 "-BUSYKEY Target key name already exists.\r\n"
18104 );
18105 assert_eq!(
18106 f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18107 "-ERR DUMP payload version or checksum are wrong\r\n"
18108 );
18109 }
18110 }
18111
18112 #[test]
18119 fn a_scan_of_a_striped_database_walks_all_of_it() {
18120 let mut f = Fixture::striped(8);
18121 for i in 0..500 {
18122 let key = format!("key:{i}");
18123 f.run(&[b"SET", key.as_bytes(), b"v"]);
18124 }
18125
18126 let mut seen = Vec::new();
18127 let mut cursor = "0".to_owned();
18128 let mut calls = 0;
18129 loop {
18130 let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18131 let (next, keys) = scan_reply(&reply);
18132 seen.extend(keys);
18133 cursor = next;
18134 calls += 1;
18135 assert!(calls < 5_000, "a scan that will not finish");
18136 if cursor == "0" {
18137 break;
18138 }
18139 }
18140 seen.sort();
18141 assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18142 assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18143
18144 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18148 let (_, keys) = scan_reply(&reply);
18149 assert_eq!(keys.len(), 10, "key:40 through key:49");
18150 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18151 let (_, keys) = scan_reply(&reply);
18152 assert!(keys.is_empty(), "nothing here is a list");
18153 }
18154
18155 #[test]
18161 fn a_random_key_can_come_from_any_stripe() {
18162 let mut f = Fixture::striped(8);
18163 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18164 for i in 0..200 {
18165 let key = format!("key:{i}");
18166 f.run(&[b"SET", key.as_bytes(), b"v"]);
18167 }
18168 let mut homes = std::collections::HashSet::new();
18169 for _ in 0..200 {
18170 let got = f.run(&[b"RANDOMKEY"]);
18171 let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18172 assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18173 homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18174 }
18175 assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18176 }
18177
18178 fn apart(f: &mut Fixture, src: &str) -> String {
18182 let home = f.server.striped(0).stripe_of(src.as_bytes());
18183 for i in 0..1_000 {
18184 let dst = format!("dst:{i}");
18185 if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18186 return dst;
18187 }
18188 }
18189 panic!("eight stripes and a thousand keys all landed in one place");
18190 }
18191
18192 #[test]
18195 fn a_rename_across_stripes_takes_everything_with_it() {
18196 let mut f = Fixture::striped(8);
18197 let dst = apart(&mut f, "src");
18198 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18199
18200 f.run(&[b"SET", src, b"v"]);
18201 f.run(&[b"EXPIRE", src, b"100"]);
18202 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18203 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18204 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18205 assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18206
18207 f.server
18212 .striped(0)
18213 .at(src)
18214 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18215 .expect("a new list");
18216 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18217 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18218 assert_eq!(
18219 f.server.striped(0).at(dst).llen(dst).expect("a list"),
18220 2,
18221 "the members are on the stripe the key moved to"
18222 );
18223
18224 f.run(&[b"SET", src, b"v"]);
18227 assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18228 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18229 assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18230 }
18231
18232 #[test]
18234 fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18235 let mut f = Fixture::striped(8);
18236 let dst = apart(&mut f, "src");
18237 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18238
18239 f.run(&[b"SET", src, b"v"]);
18240 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18241 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18242 assert_eq!(
18243 f.run(&[b"COPY", src, dst]),
18244 ":0\r\n",
18245 "the destination is taken"
18246 );
18247 f.run(&[b"SET", src, b"w"]);
18248 assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18249 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18250
18251 f.run(&[b"DEL", src, dst]);
18255 f.server
18256 .striped(0)
18257 .at(src)
18258 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18259 .expect("a new list");
18260 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18261 f.server
18262 .striped(0)
18263 .at(src)
18264 .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18265 .expect("a list that is there");
18266 assert_eq!(f.server.striped(0).at(src).llen(src).expect("a list"), 3);
18267 assert_eq!(f.server.striped(0).at(dst).llen(dst).expect("a list"), 2);
18268 }
18269
18270 #[test]
18278 fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18279 let script: &[&[&[u8]]] = &[
18280 &[b"SET", b"k1", b"foobar"],
18281 &[b"SETBIT", b"b1", b"7", b"1"],
18282 &[b"SETBIT", b"b1", b"7", b"0"],
18283 &[b"GETBIT", b"k1", b"6"],
18284 &[b"GETBIT", b"k1", b"100"],
18285 &[b"BITCOUNT", b"k1"],
18286 &[b"BITCOUNT", b"k1", b"0", b"0"],
18287 &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18288 &[b"BITPOS", b"k1", b"1"],
18289 &[b"BITPOS", b"k1", b"0", b"2"],
18290 &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18291 &[
18292 b"BITFIELD",
18293 b"bf",
18294 b"SET",
18295 b"u8",
18296 b"0",
18297 b"255",
18298 b"GET",
18299 b"u8",
18300 b"0",
18301 ],
18302 &[
18303 b"BITFIELD",
18304 b"bf",
18305 b"OVERFLOW",
18306 b"SAT",
18307 b"INCRBY",
18308 b"u8",
18309 b"0",
18310 b"10",
18311 ],
18312 &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18313 &[b"SET", b"s1", b"abc"],
18316 &[b"SET", b"s2", b"abd"],
18317 &[b"SET", b"s3", b"a"],
18318 &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18319 &[b"GET", b"d1"],
18320 &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18321 &[b"GET", b"d2"],
18322 &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18323 &[b"STRLEN", b"d3"],
18324 &[b"BITOP", b"NOT", b"d4", b"s1"],
18325 &[b"STRLEN", b"d4"],
18326 &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18327 &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18328 &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18329 &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18330 &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18333 &[b"EXISTS", b"d1"],
18334 &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18335 &[b"GET", b"d9"],
18336 &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18340 &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18341 &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18342 &[b"BITOP", b"DIFF", b"d1", b"s1"],
18343 &[b"BITOP", b"NOPE", b"d1", b"s1"],
18344 &[b"BITCOUNT", b"list"],
18345 &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18346 ];
18347
18348 let mut one = Fixture::new();
18349 let mut many = Fixture::striped(8);
18350 for f in [&mut one, &mut many] {
18351 plant_list(f, b"list");
18352 }
18353 for parts in script {
18354 let a = one.run(parts);
18355 let b = many.run(parts);
18356 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18357 }
18358 }
18359
18360 fn plant_list(f: &mut Fixture, key: &[u8]) {
18366 f.server
18367 .striped(0)
18368 .at(key)
18369 .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18370 .expect("a new list");
18371 }
18372
18373 #[test]
18380 fn a_bitop_across_stripes_reads_every_source() {
18381 let mut f = Fixture::striped(8);
18382 let other = apart(&mut f, "src");
18383 let (src, far) = (b"src".as_slice(), other.as_bytes());
18384 assert_ne!(
18385 f.server.striped(0).stripe_of(src),
18386 f.server.striped(0).stripe_of(far),
18387 "the two keys are the point of the test"
18388 );
18389
18390 f.run(&[b"SET", src, b"abc"]);
18391 f.run(&[b"SET", far, b"abd"]);
18392 assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
18393 assert_eq!(
18394 f.run(&[b"GET", far]),
18395 "$3\r\nab`\r\n",
18396 "a destination that is also a source"
18397 );
18398 f.run(&[b"SET", far, b"abd"]);
18399 assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
18400 assert_eq!(
18401 f.run(&[b"GET", src]),
18402 "$3\r\n\0\0\x07\r\n",
18403 "and the other way round"
18404 );
18405
18406 f.run(&[b"SET", src, b"abc"]);
18410 f.run(&[b"DEL", far]);
18411 assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
18412 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
18413 f.run(&[b"SET", src, b"abc"]);
18414 f.run(&[b"DEL", far]);
18415 plant_list(&mut f, far);
18416 assert_eq!(
18417 f.run(&[b"BITOP", b"OR", b"out", src, far]),
18418 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18419 );
18420 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
18421 }
18422
18423 #[test]
18425 fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
18426 let script: &[&[&[u8]]] = &[
18427 &[b"PFADD", b"h1", b"a", b"b", b"c"],
18428 &[b"PFADD", b"h1", b"a"],
18429 &[b"PFADD", b"h2"],
18430 &[b"PFADD", b"h2", b"c", b"d", b"e"],
18431 &[b"PFCOUNT", b"h1"],
18432 &[b"PFCOUNT", b"h2"],
18433 &[b"PFCOUNT", b"missing"],
18434 &[b"PFCOUNT", b"h1", b"h2"],
18436 &[b"PFCOUNT", b"h1", b"missing"],
18437 &[b"PFMERGE", b"m", b"h1", b"h2"],
18438 &[b"PFCOUNT", b"m"],
18439 &[b"STRLEN", b"m"],
18440 &[b"PFMERGE", b"m"],
18441 &[b"PFCOUNT", b"m"],
18442 &[b"PFMERGE", b"m2", b"missing"],
18443 &[b"PFCOUNT", b"m2"],
18444 &[b"PFDEBUG", b"ENCODING", b"h1"],
18447 &[b"PFDEBUG", b"DECODE", b"h1"],
18448 &[b"PFDEBUG", b"TODENSE", b"h1"],
18449 &[b"PFDEBUG", b"ENCODING", b"h1"],
18450 &[b"PFDEBUG", b"TODENSE", b"h1"],
18451 &[b"PFCOUNT", b"h1", b"h2"],
18452 &[b"PFSELFTEST"],
18453 &[b"SET", b"plain", b"not a sketch at all"],
18455 &[b"PFADD", b"plain", b"a"],
18456 &[b"PFCOUNT", b"plain"],
18457 &[b"PFCOUNT", b"h1", b"plain"],
18458 &[b"PFMERGE", b"plain", b"h1"],
18459 &[b"PFMERGE", b"m", b"plain"],
18460 &[b"PFDEBUG", b"ENCODING", b"gone"],
18461 &[b"PFDEBUG", b"NOPE", b"h1"],
18462 ];
18463
18464 let mut one = Fixture::new();
18465 let mut many = Fixture::striped(8);
18466 for parts in script {
18467 let a = one.run(parts);
18468 let b = many.run(parts);
18469 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18470 }
18471 }
18472
18473 #[test]
18482 fn the_set_group_answers_the_same_however_many_stripes_there_are() {
18483 const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
18484 let script: &[&[&[u8]]] = &[
18485 &[b"SADD", b"s1", b"a", b"b", b"c"],
18486 &[b"SADD", b"s1", b"a"],
18487 &[b"SADD", b"s2", b"b", b"c", b"d"],
18488 &[b"SADD", b"ints", b"1", b"2", b"3"],
18489 &[b"SCARD", b"s1"],
18490 &[b"SISMEMBER", b"s1", b"a"],
18491 &[b"SISMEMBER", b"s1", b"z"],
18492 &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
18493 &[b"SMEMBERS", b"s1"],
18494 &[b"SREM", b"s1", b"c"],
18495 &[b"SADD", b"s1", b"c"],
18496 &[b"SSCAN", b"s1", b"0"],
18497 &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
18498 &[b"SADD", b"one", b"m"],
18501 &[b"SRANDMEMBER", b"one"],
18502 &[b"SRANDMEMBER", b"one", b"-3"],
18503 &[b"SRANDMEMBER", b"gone"],
18504 &[b"SPOP", b"one"],
18505 &[b"SPOP", b"one"],
18506 &[b"SPOP", b"gone", b"2"],
18507 &[b"SMOVE", b"s1", b"s2", b"a"],
18509 &[b"SMOVE", b"s1", b"s2", b"zzz"],
18510 &[b"SMOVE", b"gone", b"s2", b"a"],
18511 &[b"SMEMBERS", b"s1"],
18512 &[b"SMEMBERS", b"s2"],
18513 &[b"SINTER", b"s1", b"s2"],
18515 &[b"SUNION", b"s1", b"s2"],
18516 &[b"SDIFF", b"s2", b"s1"],
18517 &[b"SINTER", b"s1", b"gone"],
18518 &[b"SUNION", b"s1", b"gone"],
18519 &[b"SDIFF", b"gone", b"s1"],
18520 &[b"SINTER", b"ints", b"s1"],
18521 &[b"SINTERCARD", b"2", b"s1", b"s2"],
18522 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
18523 &[b"SUNIONCARD", b"2", b"s1", b"s2"],
18524 &[b"SDIFFCARD", b"2", b"s2", b"s1"],
18525 &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
18526 &[b"SMEMBERS", b"d1"],
18527 &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
18528 &[b"SCARD", b"d2"],
18529 &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
18530 &[b"SCARD", b"d3"],
18531 &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
18534 &[b"EXISTS", b"d4"],
18535 &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
18537 &[b"SCARD", b"s2"],
18538 &[b"SET", b"str", b"v"],
18540 &[b"SADD", b"str", b"a"],
18541 &[b"SINTER", b"s1", b"str"],
18542 &[b"SINTERSTORE", b"d5", b"s1", b"str"],
18543 &[b"EXISTS", b"d5"],
18544 &[b"SMOVE", b"str", b"s2", b"a"],
18545 &[b"SMOVE", b"s1", b"str", b"b"],
18546 &[b"SMOVE", b"gone", b"str", b"b"],
18547 &[b"SINTERCARD", b"0", b"s1"],
18548 &[b"SINTERCARD", b"3", b"s1", b"s2"],
18549 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
18550 &[b"SPOP", b"s1", b"-1"],
18551 ];
18552
18553 let mut one = Fixture::new();
18554 let mut many = Fixture::striped(8);
18555 for parts in script {
18556 let a = one.run(parts);
18557 let b = many.run(parts);
18558 let name = String::from_utf8_lossy(parts[0]).to_uppercase();
18559 if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
18560 assert_eq!(sorted(&a), sorted(&b), "{name}");
18561 } else {
18562 assert_eq!(a, b, "{name}");
18563 }
18564 }
18565 }
18566
18567 #[test]
18569 fn a_set_operation_across_stripes_reads_every_set() {
18570 let mut f = Fixture::striped(8);
18571 let second = apart(&mut f, "s1");
18572 let third = apart(&mut f, &second);
18573 let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
18574
18575 f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
18576 f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
18577 assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
18578 assert_eq!(
18579 sorted(&f.run(&[b"SUNION", s1, s2])),
18580 ["a", "b", "c", "d"],
18581 "a union of two stripes is both of them"
18582 );
18583 assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
18584 assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
18585 assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
18586 assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
18587
18588 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
18590 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
18591 assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
18592 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
18593 assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
18594 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
18595
18596 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
18599 assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
18600 f.run(&[b"SET", s3, b"v"]);
18601 assert_eq!(
18602 f.run(&[b"SINTER", s1, s3]),
18603 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18604 );
18605 assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
18606 }
18607
18608 #[test]
18610 fn a_move_across_stripes_takes_the_member_with_it() {
18611 let mut f = Fixture::striped(8);
18612 let other = apart(&mut f, "src");
18613 let (src, dst) = (b"src".as_slice(), other.as_bytes());
18614
18615 f.run(&[b"SADD", src, b"a", b"b"]);
18616 f.run(&[b"SADD", dst, b"c"]);
18617 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
18618 assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
18619 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
18620 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
18621
18622 f.run(&[b"DEL", dst]);
18625 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
18626 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
18627 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
18628
18629 f.run(&[b"SET", dst, b"v"]);
18633 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
18634 f.run(&[b"SADD", src, b"b"]);
18635 assert_eq!(
18636 f.run(&[b"SMOVE", src, dst, b"b"]),
18637 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18638 );
18639 }
18640
18641 #[test]
18643 fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
18644 let mut f = Fixture::striped(8);
18645 let other = apart(&mut f, "src");
18646 let (src, far) = (b"src".as_slice(), other.as_bytes());
18647
18648 for i in 0..150 {
18649 let ele = format!("e:{i}");
18650 f.run(&[b"PFADD", src, ele.as_bytes()]);
18651 }
18652 for i in 150..200 {
18653 let ele = format!("e:{i}");
18654 f.run(&[b"PFADD", far, ele.as_bytes()]);
18655 }
18656 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
18659 assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
18660 assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
18661
18662 let dest = apart(&mut f, &other);
18665 assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
18666 assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
18667 assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
18668 assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
18669 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
18670 }
18671
18672 #[test]
18679 fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
18680 let script: &[&[&[u8]]] = &[
18681 &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
18682 &[b"ZADD", b"z1", b"NX", b"9", b"a"],
18683 &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
18684 &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
18685 &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
18686 &[b"ZINCRBY", b"z1", b"1.5", b"b"],
18687 &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
18688 &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
18689 &[b"ZADD", b"one", b"1", b"m"],
18690 &[b"ZCARD", b"z1"],
18691 &[b"ZCARD", b"gone"],
18692 &[b"ZSCORE", b"z1", b"a"],
18693 &[b"ZSCORE", b"z1", b"zz"],
18694 &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
18695 &[b"ZRANK", b"z1", b"c"],
18696 &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
18697 &[b"ZREVRANK", b"z1", b"c"],
18698 &[b"ZRANK", b"z1", b"gone"],
18699 &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
18700 &[b"ZCOUNT", b"z1", b"(1", b"3"],
18701 &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
18702 &[b"ZRANGE", b"z1", b"0", b"-1"],
18704 &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
18705 &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
18706 &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
18707 &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
18708 &[b"ZREVRANGE", b"z1", b"0", b"-1"],
18709 &[
18710 b"ZRANGEBYSCORE",
18711 b"z1",
18712 b"-inf",
18713 b"+inf",
18714 b"LIMIT",
18715 b"1",
18716 b"1",
18717 ],
18718 &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
18719 &[b"ZSCAN", b"z1", b"0"],
18720 &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
18721 &[b"ZRANDMEMBER", b"one"],
18724 &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
18725 &[b"ZRANDMEMBER", b"gone"],
18726 &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
18728 &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
18729 &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
18730 &[b"EXISTS", b"d0"],
18731 &[b"ZUNION", b"2", b"z1", b"z2"],
18733 &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
18734 &[
18735 b"ZUNION",
18736 b"2",
18737 b"z1",
18738 b"z2",
18739 b"WEIGHTS",
18740 b"2",
18741 b"3",
18742 b"AGGREGATE",
18743 b"MAX",
18744 b"WITHSCORES",
18745 ],
18746 &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
18747 &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
18748 &[b"ZDIFF", b"2", b"gone", b"z1"],
18749 &[b"ZINTERCARD", b"2", b"z1", b"z2"],
18750 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
18751 &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
18752 &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
18753 &[
18754 b"ZINTERSTORE",
18755 b"d2",
18756 b"2",
18757 b"z1",
18758 b"z2",
18759 b"AGGREGATE",
18760 b"MIN",
18761 ],
18762 &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
18763 &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
18764 &[b"ZCARD", b"d3"],
18765 &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
18768 &[b"EXISTS", b"d4"],
18769 &[b"SADD", b"plain", b"a", b"x"],
18772 &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
18773 &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
18774 &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
18776 &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
18777 &[b"ZREM", b"d5", b"x", b"nothere"],
18779 &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
18780 &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
18781 &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
18782 &[b"ZPOPMIN", b"z1"],
18783 &[b"ZPOPMAX", b"z1", b"2"],
18784 &[b"ZPOPMIN", b"gone"],
18785 &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
18786 &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
18787 &[b"SET", b"str", b"v"],
18789 &[b"ZADD", b"str", b"1", b"a"],
18790 &[b"ZSCORE", b"str", b"a"],
18791 &[b"ZADD", b"z1", b"nan", b"a"],
18792 &[b"ZUNION", b"2", b"z1", b"str"],
18793 &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
18794 &[b"EXISTS", b"d6"],
18795 &[b"ZINTERCARD", b"0", b"z1"],
18796 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
18797 &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
18798 &[b"ZMPOP", b"1", b"str", b"MIN"],
18799 &[b"ZPOPMIN", b"z1", b"-1"],
18800 ];
18801
18802 let mut one = Fixture::new();
18803 let mut many = Fixture::striped(8);
18804 for parts in script {
18805 let a = one.run(parts);
18806 let b = many.run(parts);
18807 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18808 }
18809 }
18810
18811 #[test]
18813 fn a_sorted_set_operation_across_stripes_reads_every_input() {
18814 let mut f = Fixture::striped(8);
18815 let second = apart(&mut f, "z1");
18816 let third = apart(&mut f, &second);
18817 let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
18818
18819 f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
18820 f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
18821 assert_eq!(
18824 f.run(&[b"ZUNION", b"2", z1, z2]),
18825 "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
18826 );
18827 assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
18828 assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
18829 assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
18830 assert_eq!(
18831 f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
18832 ":1\r\n"
18833 );
18834
18835 assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
18838 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
18839 assert_eq!(
18840 f.run(&[
18841 b"ZUNIONSTORE",
18842 z3,
18843 b"2",
18844 z1,
18845 z2,
18846 b"WEIGHTS",
18847 b"2",
18848 b"3",
18849 b"AGGREGATE",
18850 b"MAX"
18851 ]),
18852 ":3\r\n"
18853 );
18854 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
18855 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
18856 assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
18857 assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
18858 assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
18859
18860 let popped = format!(
18863 "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
18864 second.len()
18865 );
18866 assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
18867 f.run(&[b"ZADD", z2, b"3", b"b"]);
18868
18869 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
18872 assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
18873 f.run(&[b"SET", z3, b"v"]);
18874 assert_eq!(
18875 f.run(&[b"ZUNION", b"2", z1, z3]),
18876 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18877 );
18878 assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
18879
18880 assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
18884 assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
18885 assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
18886 }
18887
18888 #[test]
18890 fn a_range_store_across_stripes_copies_the_window() {
18891 let mut f = Fixture::striped(8);
18892 let other = apart(&mut f, "src");
18893 let third = apart(&mut f, &other);
18894 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
18895
18896 f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
18897 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
18898 assert_eq!(
18899 f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
18900 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
18901 );
18902 assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
18903
18904 assert_eq!(
18907 f.run(&[
18908 b"ZRANGESTORE",
18909 dst,
18910 src,
18911 b"+inf",
18912 b"-inf",
18913 b"BYSCORE",
18914 b"REV",
18915 b"LIMIT",
18916 b"0",
18917 b"2"
18918 ]),
18919 ":2\r\n"
18920 );
18921 assert_eq!(
18922 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
18923 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
18924 );
18925
18926 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
18929 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
18930 f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
18931 f.run(&[b"SET", plain, b"v"]);
18932 assert_eq!(
18933 f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
18934 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18935 );
18936 assert_eq!(
18937 f.run(&[b"ZCARD", dst]),
18938 ":3\r\n",
18939 "and left the destination"
18940 );
18941 }
18942
18943 #[test]
18950 fn the_list_group_answers_the_same_however_many_stripes_there_are() {
18951 let script: &[&[&[u8]]] = &[
18952 &[b"RPUSH", b"l1", b"a", b"b", b"c"],
18953 &[b"LPUSH", b"l1", b"z"],
18954 &[b"RPUSHX", b"l1", b"d"],
18955 &[b"LPUSHX", b"gone", b"x"],
18956 &[b"RPUSHX", b"gone", b"x"],
18957 &[b"LLEN", b"l1"],
18958 &[b"LLEN", b"gone"],
18959 &[b"LRANGE", b"l1", b"0", b"-1"],
18960 &[b"LRANGE", b"l1", b"1", b"2"],
18961 &[b"LRANGE", b"l1", b"5", b"9"],
18962 &[b"LINDEX", b"l1", b"0"],
18963 &[b"LINDEX", b"l1", b"-1"],
18964 &[b"LINDEX", b"l1", b"99"],
18965 &[b"LSET", b"l1", b"0", b"y"],
18966 &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
18967 &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
18968 &[b"LPOS", b"l1", b"b"],
18969 &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
18970 &[b"LPOS", b"l1", b"nothere"],
18971 &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
18972 &[b"LREM", b"l1", b"1", b"aa"],
18973 &[b"LTRIM", b"l1", b"0", b"3"],
18974 &[b"LRANGE", b"l1", b"0", b"-1"],
18975 &[b"LPOP", b"l1"],
18976 &[b"RPOP", b"l1"],
18977 &[b"LPOP", b"l1", b"2"],
18978 &[b"LPOP", b"gone"],
18979 &[b"LPOP", b"gone", b"2"],
18980 &[b"EXISTS", b"l1"],
18981 &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
18984 &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
18985 &[b"RPOPLPUSH", b"src", b"dst"],
18986 &[b"LRANGE", b"dst", b"0", b"-1"],
18987 &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
18988 &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
18989 &[
18990 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
18991 ],
18992 &[
18993 b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
18994 ],
18995 &[b"LRANGE", b"dst", b"0", b"-1"],
18996 &[
18997 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
18998 ],
18999 &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19000 &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19001 &[b"LMPOP", b"1", b"gone", b"LEFT"],
19002 &[b"RPUSH", b"q", b"a", b"b", b"c"],
19005 &[b"BLPOP", b"gone", b"q", b"0"],
19006 &[b"BRPOP", b"q", b"0"],
19007 &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19008 &[b"RPUSH", b"q", b"x", b"y", b"z"],
19009 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19010 &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19011 &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19012 &[b"BLPOP", b"q", b"0"],
19013 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19014 &[b"SET", b"plain", b"v"],
19016 &[b"LPUSH", b"plain", b"a"],
19017 &[b"LLEN", b"plain"],
19018 &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19019 &[b"LRANGE", b"dst", b"0", b"-1"],
19020 &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19021 &[b"LSET", b"gone", b"0", b"v"],
19022 &[b"LSET", b"dst", b"99", b"v"],
19023 &[b"LPOP", b"dst", b"-1"],
19024 &[b"LMPOP", b"0", b"dst", b"LEFT"],
19025 &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19026 ];
19027
19028 let mut one = Fixture::new();
19029 let mut many = Fixture::striped(8);
19030 for parts in script {
19031 let a = one.run(parts);
19032 let b = many.run(parts);
19033 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19034 }
19035 }
19036
19037 #[test]
19039 fn a_list_move_across_stripes_takes_the_elements_with_it() {
19040 let mut f = Fixture::striped(8);
19041 let other = apart(&mut f, "src");
19042 let third = apart(&mut f, &other);
19043 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19044
19045 f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19046 assert_eq!(
19047 f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19048 "$1\r\na\r\n"
19049 );
19050 assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19051 assert_eq!(
19052 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19053 "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19054 "one went on each end of the destination"
19055 );
19056 assert_eq!(
19057 f.run(&[b"LRANGE", src, b"0", b"-1"]),
19058 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19059 );
19060
19061 assert_eq!(
19063 f.run(&[
19064 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19065 ]),
19066 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19067 );
19068 assert_eq!(
19069 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19070 "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19071 );
19072 assert_eq!(
19073 f.run(&[b"EXISTS", src]),
19074 ":0\r\n",
19075 "and the source is gone with its last element"
19076 );
19077
19078 f.run(&[b"RPUSH", src, b"e", b"f"]);
19081 assert_eq!(
19082 f.run(&[
19083 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19084 ]),
19085 "*-1\r\n"
19086 );
19087 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19088 assert_eq!(
19089 f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19090 "$-1\r\n"
19091 );
19092 assert_eq!(
19093 f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19094 "*-1\r\n"
19095 );
19096
19097 f.run(&[b"SET", plain, b"v"]);
19101 assert_eq!(
19102 f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19103 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19104 );
19105 assert_eq!(
19106 f.run(&[b"LLEN", src]),
19107 ":2\r\n",
19108 "and left the source alone"
19109 );
19110 assert_eq!(
19111 f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19112 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19113 );
19114 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19115 }
19116
19117 #[test]
19123 fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19124 let mut f = Fixture::striped(8);
19125 let other = apart(&mut f, "q");
19126 let (q, far) = (b"q".as_slice(), other.as_bytes());
19127
19128 assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19129 assert_eq!(f.server.waiters().len(), 1);
19130 f.run(&[b"RPUSH", far, b"v"]);
19131 let mut out = Out::new(Proto::Resp2);
19132 assert!(f.server.serve_waiter(0, 0, &mut out));
19133 let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19134 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19135 assert_eq!(
19136 f.run(&[b"EXISTS", far]),
19137 ":0\r\n",
19138 "and it took the element with it"
19139 );
19140
19141 f.server.waiters_mut().forget(7);
19144 assert_eq!(
19145 f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19146 Flow::Block
19147 );
19148 f.run(&[b"RPUSH", q, b"w"]);
19149 let mut out = Out::new(Proto::Resp2);
19150 assert!(f.server.serve_waiter(0, 0, &mut out));
19151 assert_eq!(
19152 core::str::from_utf8(out.as_slice()).expect("ascii"),
19153 "$1\r\nw\r\n"
19154 );
19155 assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19156 }
19157
19158 #[test]
19164 fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19165 let script: &[&[&[u8]]] = &[
19166 &[b"XADD", b"s", b"1-1", b"a", b"1"],
19167 &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19168 &[b"XADD", b"s", b"3-1", b"d", b"4"],
19169 &[b"XADD", b"s", b"1-1", b"e", b"5"],
19170 &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19171 &[b"XLEN", b"s"],
19172 &[b"XLEN", b"gone"],
19173 &[b"XRANGE", b"s", b"-", b"+"],
19174 &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19175 &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19176 &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19177 &[b"XREVRANGE", b"s", b"+", b"-"],
19178 &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19179 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19180 &[b"XREAD", b"STREAMS", b"s", b"$"],
19181 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19183 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19184 &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19185 &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19186 &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19187 &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19188 &[
19189 b"XREADGROUP",
19190 b"GROUP",
19191 b"g",
19192 b"c1",
19193 b"COUNT",
19194 b"1",
19195 b"STREAMS",
19196 b"s",
19197 b"0",
19198 ],
19199 &[
19200 b"XREADGROUP",
19201 b"GROUP",
19202 b"nope",
19203 b"c1",
19204 b"STREAMS",
19205 b"s",
19206 b">",
19207 ],
19208 &[b"XPENDING", b"s", b"g"],
19209 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19210 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19211 &[b"XPENDING", b"s", b"nope"],
19212 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19213 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19214 &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19215 &[b"XACK", b"s", b"g", b"1-1"],
19216 &[b"XACK", b"s", b"g", b"1-1"],
19217 &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19218 &[b"XPENDING", b"s", b"g"],
19219 &[b"XINFO", b"STREAM", b"s"],
19220 &[b"XINFO", b"GROUPS", b"s"],
19221 &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19222 &[b"XINFO", b"STREAM", b"gone"],
19223 &[b"XDEL", b"s", b"3-1"],
19225 &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19226 &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19227 &[b"XADD", b"s", b"9-1", b"z", b"9"],
19228 &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19229 &[b"XTRIM", b"s", b"MINID", b"9"],
19230 &[b"XSETID", b"s", b"99-1"],
19231 &[b"XSETID", b"s", b"1-1"],
19232 &[b"XLEN", b"s"],
19233 &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19234 &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19235 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19236 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19237 &[b"SET", b"plain", b"v"],
19239 &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19240 &[b"XLEN", b"plain"],
19241 &[b"XREAD", b"STREAMS", b"plain", b"0"],
19242 &[b"XRANGE", b"s", b"bogus", b"+"],
19243 &[b"XADD", b"s", b"1-1", b"a"],
19244 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19245 &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19246 ];
19247
19248 let mut one = Fixture::new();
19249 let mut many = Fixture::striped(8);
19250 for parts in script {
19251 let a = one.run(parts);
19252 let b = many.run(parts);
19253 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19254 }
19255 }
19256
19257 #[test]
19263 fn a_stream_read_across_stripes_reads_every_key() {
19264 let mut f = Fixture::striped(8);
19265 let other = apart(&mut f, "s1");
19266 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19267
19268 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19269 f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19270 let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19271 assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19272 assert!(got.contains("1-1"), "the first one is in there: {got}");
19273 assert!(got.contains("2-1"), "and so is the second: {got}");
19274
19275 f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19278 let got = f.run(&[
19279 b"XREADGROUP",
19280 b"GROUP",
19281 b"g",
19282 b"c",
19283 b"STREAMS",
19284 s1,
19285 s2,
19286 b">",
19287 b">",
19288 ]);
19289 assert!(got.starts_with("-NOGROUP"), "{got}");
19290 assert_eq!(
19291 f.run(&[b"XPENDING", s1, b"g"]),
19292 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19293 "and read nothing from the key that did have the group"
19294 );
19295
19296 f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19297 let got = f.run(&[
19298 b"XREADGROUP",
19299 b"GROUP",
19300 b"g",
19301 b"c",
19302 b"STREAMS",
19303 s1,
19304 s2,
19305 b">",
19306 b">",
19307 ]);
19308 assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19309 }
19310
19311 #[test]
19313 fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19314 let mut f = Fixture::striped(8);
19315 let other = apart(&mut f, "s1");
19316 let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19317 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19318 f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19319
19320 assert_eq!(
19321 f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19322 .0,
19323 Flow::Block
19324 );
19325 f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19326 let mut out = Out::new(Proto::Resp2);
19327 assert!(f.server.serve_waiter(0, 0, &mut out));
19328 let want = format!(
19329 "*1\r\n*2\r\n${}\r\n{other}\r\n*1\r\n*2\r\n$3\r\n2-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
19330 other.len()
19331 );
19332 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19333 }
19334
19335 #[test]
19337 fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19338 let script: &[&[&[u8]]] = &[
19339 &[
19340 b"JSON.SET",
19341 b"d",
19342 b"$",
19343 br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19344 ],
19345 &[b"JSON.SET", b"d", b"$.a", b"2"],
19346 &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19347 &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19348 &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19349 &[b"JSON.GET", b"d"],
19350 &[b"JSON.GET", b"d", b"$.b"],
19351 &[b"JSON.GET", b"gone", b"$"],
19352 &[b"JSON.TYPE", b"d", b"$.b"],
19353 &[b"JSON.TYPE", b"d", b"$.s"],
19354 &[b"JSON.TOGGLE", b"d", b"$.t"],
19355 &[b"JSON.ARRLEN", b"d", b"$.b"],
19356 &[b"JSON.OBJLEN", b"d", b"$"],
19357 &[b"JSON.OBJKEYS", b"d", b"$"],
19358 &[b"JSON.STRLEN", b"d", b"$.s"],
19359 &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19360 &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19361 &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19362 &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19363 &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19364 &[b"JSON.ARRPOP", b"d", b"$.b"],
19365 &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19366 &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19367 &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19368 &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19369 &[b"JSON.RESP", b"d", b"$.b"],
19370 &[b"JSON.DEBUG", b"MEMORY", b"d"],
19371 &[b"JSON.CLEAR", b"d", b"$.b"],
19372 &[b"JSON.DEL", b"d", b"$.m"],
19373 &[b"JSON.FORGET", b"d", b"$.nothere"],
19374 &[
19376 b"JSON.MSET",
19377 b"m1",
19378 b"$",
19379 b"1",
19380 b"m2",
19381 b"$",
19382 b"2",
19383 b"m3",
19384 b"$",
19385 b"3",
19386 ],
19387 &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
19388 &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
19389 &[b"JSON.GET", b"m1", b"$"],
19390 &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
19391 &[b"JSON.GET", b"m2", b"$"],
19392 &[b"SET", b"plain", b"v"],
19394 &[b"JSON.GET", b"plain", b"$"],
19395 &[b"JSON.SET", b"plain", b"$", b"1"],
19396 &[b"JSON.MGET", b"m1", b"plain", b"$"],
19397 &[b"JSON.SET", b"d", b"$.b", b"["],
19398 &[b"JSON.DEL", b"plain"],
19399 ];
19400
19401 let mut one = Fixture::new();
19402 let mut many = Fixture::striped(8);
19403 for parts in script {
19404 let a = one.run(parts);
19405 let b = many.run(parts);
19406 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19407 }
19408 }
19409
19410 #[test]
19417 fn a_json_multi_write_across_stripes_reaches_every_key() {
19418 let mut f = Fixture::striped(8);
19419 let second = apart(&mut f, "m1");
19420 let third = apart(&mut f, &second);
19421 let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
19422
19423 assert_eq!(
19424 f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
19425 "+OK\r\n"
19426 );
19427 assert_eq!(
19428 f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
19429 "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
19430 );
19431
19432 assert_eq!(
19435 f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
19436 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
19437 );
19438 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
19439
19440 assert_eq!(
19444 f.run(&[
19445 b"JSON.MSET",
19446 m1,
19447 b"$",
19448 b"9",
19449 m2,
19450 b"$.deep",
19451 b"9",
19452 m3,
19453 b"$",
19454 b"7"
19455 ]),
19456 "$-1\r\n"
19457 );
19458 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
19459 assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
19460 assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
19461 }
19462
19463 #[test]
19465 fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
19466 let script: &[&[&[u8]]] = &[
19467 &[
19468 b"GEOADD",
19469 b"g",
19470 b"13.361389",
19471 b"38.115556",
19472 b"palermo",
19473 b"15.087269",
19474 b"37.502669",
19475 b"catania",
19476 ],
19477 &[
19478 b"GEOADD",
19479 b"g",
19480 b"NX",
19481 b"13.361389",
19482 b"38.115556",
19483 b"palermo",
19484 ],
19485 &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
19486 &[b"GEOPOS", b"g", b"palermo", b"nothere"],
19487 &[b"GEOHASH", b"g", b"palermo", b"catania"],
19488 &[b"GEODIST", b"g", b"palermo", b"catania"],
19489 &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
19490 &[b"GEODIST", b"g", b"palermo", b"nothere"],
19491 &[
19492 b"GEOSEARCH",
19493 b"g",
19494 b"FROMLONLAT",
19495 b"15",
19496 b"37",
19497 b"BYRADIUS",
19498 b"200",
19499 b"KM",
19500 b"ASC",
19501 b"WITHCOORD",
19502 b"WITHDIST",
19503 b"WITHHASH",
19504 ],
19505 &[
19506 b"GEOSEARCH",
19507 b"g",
19508 b"FROMMEMBER",
19509 b"palermo",
19510 b"BYBOX",
19511 b"400",
19512 b"400",
19513 b"KM",
19514 b"DESC",
19515 ],
19516 &[
19517 b"GEORADIUS",
19518 b"g",
19519 b"15",
19520 b"37",
19521 b"200",
19522 b"KM",
19523 b"COUNT",
19524 b"1",
19525 ],
19526 &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
19527 &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
19528 &[
19529 b"GEOSEARCHSTORE",
19530 b"dst",
19531 b"g",
19532 b"FROMLONLAT",
19533 b"15",
19534 b"37",
19535 b"BYRADIUS",
19536 b"200",
19537 b"KM",
19538 ],
19539 &[b"ZRANGE", b"dst", b"0", b"-1"],
19540 &[
19541 b"GEOSEARCHSTORE",
19542 b"dst",
19543 b"g",
19544 b"FROMLONLAT",
19545 b"15",
19546 b"37",
19547 b"BYRADIUS",
19548 b"1",
19549 b"M",
19550 b"STOREDIST",
19551 ],
19552 &[b"EXISTS", b"dst"],
19553 &[
19554 b"GEORADIUS",
19555 b"g",
19556 b"15",
19557 b"37",
19558 b"200",
19559 b"KM",
19560 b"STORE",
19561 b"dst",
19562 ],
19563 &[b"ZCARD", b"dst"],
19564 &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
19566 &[b"SET", b"plain", b"v"],
19567 &[b"GEOPOS", b"plain", b"a"],
19568 &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
19569 &[
19570 b"GEOSEARCHSTORE",
19571 b"dst",
19572 b"g",
19573 b"FROMLONLAT",
19574 b"15",
19575 b"37",
19576 b"BYRADIUS",
19577 b"200",
19578 b"KM",
19579 b"WITHCOORD",
19580 ],
19581 ];
19582
19583 let mut one = Fixture::new();
19584 let mut many = Fixture::striped(8);
19585 for parts in script {
19586 let a = one.run(parts);
19587 let b = many.run(parts);
19588 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19589 }
19590 }
19591
19592 #[test]
19594 fn a_geo_search_store_across_stripes_writes_what_it_found() {
19595 let mut f = Fixture::striped(8);
19596 let other = apart(&mut f, "g");
19597 let third = apart(&mut f, &other);
19598 let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
19599
19600 f.run(&[
19601 b"GEOADD",
19602 g,
19603 b"13.361389",
19604 b"38.115556",
19605 b"palermo",
19606 b"15.087269",
19607 b"37.502669",
19608 b"catania",
19609 ]);
19610 assert_eq!(
19611 f.run(&[
19612 b"GEOSEARCHSTORE",
19613 dst,
19614 g,
19615 b"FROMLONLAT",
19616 b"15",
19617 b"37",
19618 b"BYRADIUS",
19619 b"200",
19620 b"KM",
19621 b"ASC",
19622 ]),
19623 ":2\r\n"
19624 );
19625 assert_eq!(
19626 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19627 "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
19628 "the geohash is the score, so the order is not the search order"
19629 );
19630 assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
19631
19632 assert_eq!(
19635 f.run(&[
19636 b"GEOSEARCHSTORE",
19637 dst,
19638 g,
19639 b"FROMMEMBER",
19640 b"palermo",
19641 b"BYRADIUS",
19642 b"200",
19643 b"KM",
19644 b"STOREDIST",
19645 ]),
19646 ":2\r\n"
19647 );
19648 assert_eq!(
19649 f.run(&[b"ZSCORE", dst, b"palermo"]),
19650 "$1\r\n0\r\n",
19651 "the centre is nought away from itself"
19652 );
19653
19654 assert_eq!(
19658 f.run(&[
19659 b"GEOSEARCHSTORE",
19660 dst,
19661 g,
19662 b"FROMLONLAT",
19663 b"0",
19664 b"0",
19665 b"BYRADIUS",
19666 b"1",
19667 b"M",
19668 ]),
19669 ":0\r\n"
19670 );
19671 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19672 f.run(&[
19673 b"GEOSEARCHSTORE",
19674 dst,
19675 g,
19676 b"FROMLONLAT",
19677 b"15",
19678 b"37",
19679 b"BYRADIUS",
19680 b"200",
19681 b"KM",
19682 ]);
19683 f.run(&[b"SET", plain, b"v"]);
19684 assert_eq!(
19685 f.run(&[
19686 b"GEOSEARCHSTORE",
19687 dst,
19688 plain,
19689 b"FROMLONLAT",
19690 b"15",
19691 b"37",
19692 b"BYRADIUS",
19693 b"200",
19694 b"KM",
19695 ]),
19696 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19697 );
19698 assert_eq!(
19699 f.run(&[b"ZCARD", dst]),
19700 ":2\r\n",
19701 "and left the destination"
19702 );
19703 }
19704
19705 #[test]
19711 fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
19712 let script: &[&[&[u8]]] = &[
19713 &[
19714 b"TS.CREATE",
19715 b"ts:a",
19716 b"LABELS",
19717 b"sensor",
19718 b"a",
19719 b"room",
19720 b"1",
19721 ],
19722 &[b"TS.CREATE", b"ts:a"],
19723 &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
19724 &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
19725 &[
19726 b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
19727 ],
19728 &[
19729 b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
19730 ],
19731 &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
19732 &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
19733 &[b"TS.GET", b"ts:a"],
19734 &[b"TS.GET", b"gone"],
19735 &[b"TS.RANGE", b"ts:a", b"-", b"+"],
19736 &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
19737 &[
19738 b"TS.RANGE",
19739 b"ts:a",
19740 b"-",
19741 b"+",
19742 b"AGGREGATION",
19743 b"avg",
19744 b"2000",
19745 ],
19746 &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
19747 &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
19748 &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
19749 &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
19750 &[b"TS.READ", b"ts:a", b"0"],
19751 &[b"TS.READ", b"ts:a", b"+"],
19752 &[b"TS.QUERYINDEX", b"sensor=a"],
19754 &[b"TS.QUERYINDEX", b"room=1"],
19755 &[b"TS.QUERYINDEX", b"room=9"],
19756 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
19757 &[
19758 b"TS.QUERYLABELS",
19759 b"VALUES",
19760 b"sensor",
19761 b"FILTER",
19762 b"room=1",
19763 ],
19764 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
19765 &[
19766 b"TS.MGET",
19767 b"SELECTED_LABELS",
19768 b"sensor",
19769 b"FILTER",
19770 b"sensor=a",
19771 ],
19772 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
19773 &[
19774 b"TS.MREVRANGE",
19775 b"-",
19776 b"+",
19777 b"WITHLABELS",
19778 b"FILTER",
19779 b"sensor=a",
19780 ],
19781 &[
19782 b"TS.MRANGE",
19783 b"-",
19784 b"+",
19785 b"FILTER",
19786 b"room=1",
19787 b"GROUPBY",
19788 b"room",
19789 b"REDUCE",
19790 b"max",
19791 ],
19792 &[b"TS.INFO", b"ts:a"],
19793 &[
19795 b"TS.CREATERULE",
19796 b"ts:a",
19797 b"ts:down",
19798 b"AGGREGATION",
19799 b"avg",
19800 b"1000",
19801 ],
19802 &[b"TS.CREATE", b"ts:down"],
19803 &[
19804 b"TS.CREATERULE",
19805 b"ts:a",
19806 b"ts:down",
19807 b"AGGREGATION",
19808 b"avg",
19809 b"1000",
19810 ],
19811 &[b"TS.ADD", b"ts:a", b"5000", b"4"],
19812 &[b"TS.ADD", b"ts:a", b"6000", b"5"],
19813 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
19814 &[b"TS.GET", b"ts:down", b"LATEST"],
19815 &[b"TS.INFO", b"ts:down"],
19816 &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
19817 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
19818 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
19819 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
19820 &[b"TS.DEL", b"ts:a", b"0", b"1000"],
19821 &[b"SET", b"plain", b"v"],
19823 &[b"TS.ADD", b"plain", b"1", b"1"],
19824 &[b"TS.GET", b"plain"],
19825 &[b"TS.READ", b"plain", b"0"],
19826 &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
19827 &[b"TS.RANGE", b"gone", b"-", b"+"],
19828 &[b"TS.INFO", b"gone"],
19829 ];
19830
19831 let mut one = Fixture::new();
19832 let mut many = Fixture::striped(8);
19833 for parts in script {
19834 let a = one.run(parts);
19835 let b = many.run(parts);
19836 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19837 }
19838 }
19839
19840 #[test]
19850 fn a_compaction_rule_across_stripes_reaches_both_ends() {
19851 let mut many = Fixture::striped(8);
19852 let other = apart(&mut many, "src");
19853 let (src, dst) = (b"src".as_slice(), other.as_bytes());
19854 let mut one = Fixture::new();
19855 let mut both = |parts: &[&[u8]]| {
19856 let a = one.run(parts);
19857 let b = many.run(parts);
19858 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19859 a
19860 };
19861
19862 both(&[b"TS.CREATE", src]);
19863 both(&[b"TS.CREATE", dst]);
19864 assert_eq!(
19865 both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
19866 "+OK\r\n"
19867 );
19868 both(&[b"TS.ADD", src, b"1000", b"1"]);
19869 both(&[b"TS.ADD", src, b"1500", b"3"]);
19870 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
19873 let open = both(&[b"TS.GET", dst, b"LATEST"]);
19874 assert!(open.contains(":1000"), "the open bucket is folded: {open}");
19875
19876 both(&[b"TS.ADD", src, b"2000", b"5"]);
19879 let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
19880 assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
19881 assert!(got.contains(":1000"), "{got}");
19882
19883 both(&[b"TS.DEL", src, b"1000", b"1999"]);
19885 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
19886
19887 assert!(
19890 both(&[b"TS.INFO", dst]).contains("src"),
19891 "the source is named"
19892 );
19893 assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
19894 assert_eq!(
19895 both(&[b"TS.DELETERULE", src, dst]),
19896 "-ERR TSDB: compaction rule does not exist\r\n"
19897 );
19898 }
19899
19900 #[test]
19902 fn a_label_query_across_stripes_finds_every_series() {
19903 let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
19904 let mut many = Fixture::striped(8);
19905 let mut homes: Vec<usize> = names
19906 .iter()
19907 .map(|name| many.server.striped(0).stripe_of(name))
19908 .collect();
19909 homes.sort_unstable();
19910 homes.dedup();
19911 assert!(homes.len() > 1, "the six keys are not all on one stripe");
19912
19913 let mut one = Fixture::new();
19914 let mut both = |parts: &[&[u8]]| {
19915 let a = one.run(parts);
19916 let b = many.run(parts);
19917 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19918 a
19919 };
19920 for name in &names {
19921 both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
19922 both(&[b"TS.ADD", name, b"1000", b"1"]);
19923 }
19924
19925 let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
19926 assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
19927 assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
19928 assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
19929 assert_eq!(
19930 both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
19931 "*1\r\n$4\r\nroom\r\n"
19932 );
19933 }
19934
19935 #[test]
19942 fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
19943 let script: &[&[&[u8]]] = &[
19944 &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
19945 &[b"HMSET", b"h", b"c", b"3"],
19946 &[b"HSETNX", b"h", b"a", b"9"],
19947 &[b"HSETNX", b"h", b"d", b"4"],
19948 &[b"HGET", b"h", b"a"],
19949 &[b"HGET", b"h", b"nope"],
19950 &[b"HMGET", b"h", b"a", b"nope"],
19951 &[b"HLEN", b"h"],
19952 &[b"HEXISTS", b"h", b"a"],
19953 &[b"HSTRLEN", b"h", b"a"],
19954 &[b"HGETALL", b"h"],
19955 &[b"HKEYS", b"h"],
19956 &[b"HVALS", b"h"],
19957 &[b"HINCRBY", b"h", b"a", b"5"],
19958 &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
19959 &[b"HSCAN", b"h", b"0"],
19960 &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
19961 &[b"HSCAN", b"h", b"0", b"NOVALUES"],
19962 &[b"HDEL", b"h", b"d"],
19963 &[b"HSET", b"one", b"f", b"v"],
19964 &[b"HRANDFIELD", b"one"],
19965 &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
19966 &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
19968 &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
19969 &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
19970 &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
19971 &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
19972 &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
19973 &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
19974 &[b"HGET", b"h", b"b"],
19975 &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
19977 &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
19978 &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
19979 &[b"HGET", b"h", b"e"],
19980 &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
19982 &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
19983 &[b"HGETALL", b"imp"],
19984 &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
19985 &[b"HIMPORT", b"DISCARD", b"fs"],
19986 &[b"SET", b"plain", b"v"],
19988 &[b"HSET", b"plain", b"a", b"1"],
19989 &[b"HGETALL", b"plain"],
19990 &[b"HGET", b"gone", b"a"],
19991 &[b"HINCRBY", b"h", b"a", b"nan"],
19992 ];
19993
19994 let mut one = Fixture::new();
19995 let mut many = Fixture::striped(8);
19996 one.server.set_clock_ms(1_700_000_000_000);
20000 many.server.set_clock_ms(1_700_000_000_000);
20001 for parts in script {
20002 let a = one.run(parts);
20003 let b = many.run(parts);
20004 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20005 }
20006 }
20007
20008 #[test]
20010 fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20011 let script: &[&[&[u8]]] = &[
20012 &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20013 &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20014 &[b"ARGET", b"a", b"1"],
20015 &[b"ARGET", b"a", b"99"],
20016 &[b"ARMGET", b"a", b"0", b"5", b"99"],
20017 &[b"ARGETRANGE", b"a", b"0", b"7"],
20018 &[b"ARLEN", b"a"],
20019 &[b"ARCOUNT", b"a"],
20020 &[b"ARINSERT", b"a", b"m", b"n"],
20021 &[b"ARSCAN", b"a", b"0", b"20"],
20022 &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20023 &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20024 &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20025 &[b"ARLASTITEMS", b"a", b"2"],
20026 &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20027 &[b"ARNEXT", b"a"],
20028 &[b"ARSEEK", b"a", b"3"],
20029 &[b"AROP", b"a", b"0", b"20", b"USED"],
20030 &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20031 &[b"ARINFO", b"a"],
20032 &[b"ARINFO", b"a", b"FULL"],
20033 &[b"ARDEL", b"a", b"0"],
20034 &[b"ARDELRANGE", b"a", b"1", b"2"],
20035 &[b"ARCOUNT", b"a"],
20036 &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20037 &[b"ARGETRANGE", b"r", b"0", b"9"],
20038 &[b"SET", b"plain", b"v"],
20040 &[b"ARGET", b"plain", b"0"],
20041 &[b"ARSET", b"plain", b"0", b"v"],
20042 &[b"ARGET", b"gone", b"0"],
20043 &[b"ARSET", b"a", b"bad", b"v"],
20044 ];
20045
20046 let mut one = Fixture::new();
20047 let mut many = Fixture::striped(8);
20048 for parts in script {
20049 let a = one.run(parts);
20050 let b = many.run(parts);
20051 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20052 }
20053 }
20054
20055 #[test]
20061 fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20062 let script: &[&[&[u8]]] = &[
20063 &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20064 &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20065 &[b"G.NADD", b"g", b"n3"],
20066 &[b"G.NGET", b"g", b"n1"],
20067 &[b"G.NGET", b"g", b"gone"],
20068 &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20069 &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20070 &[b"G.OUT", b"g", b"n1", b"knows"],
20071 &[b"G.IN", b"g", b"n2", b"knows"],
20072 &[b"G.DEG", b"g", b"n1", b"knows"],
20073 &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20074 &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20075 &[b"G.PATH", b"g", b"n1", b"n3"],
20076 &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20077 &[b"G.NDEL", b"g", b"n3"],
20078 &[b"G.NGET", b"g", b"n3"],
20079 &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20081 &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20082 &[b"VCARD", b"v"],
20083 &[b"VDIM", b"v"],
20084 &[b"VEMB", b"v", b"e1"],
20085 &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20086 &[b"VSIM", b"v", b"ELE", b"e1"],
20087 &[b"VISMEMBER", b"v", b"e1"],
20088 &[b"VISMEMBER", b"v", b"gone"],
20089 &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20090 &[b"VGETATTR", b"v", b"e1"],
20091 &[b"VRANGE", b"v", b"-", b"+"],
20092 &[b"VLINKS", b"v", b"e1"],
20093 &[b"VINFO", b"v"],
20094 &[b"VREM", b"v", b"e2"],
20095 &[b"VCARD", b"v"],
20096 &[b"SET", b"plain", b"v"],
20098 &[b"G.NGET", b"plain", b"n1"],
20099 &[b"VCARD", b"plain"],
20100 &[b"G.NADD", b"gone2", b"n"],
20101 &[b"VEMB", b"gone3", b"e"],
20102 ];
20103
20104 let mut one = Fixture::new();
20105 let mut many = Fixture::striped(8);
20106 for parts in script {
20107 let a = one.run(parts);
20108 let b = many.run(parts);
20109 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20110 }
20111 }
20112
20113 #[test]
20116 fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20117 let script: &[&[&[u8]]] = &[
20118 &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20120 &[b"BF.ADD", b"bf", b"a"],
20121 &[b"BF.ADD", b"bf", b"a"],
20122 &[b"BF.MADD", b"bf", b"b", b"c"],
20123 &[b"BF.EXISTS", b"bf", b"a"],
20124 &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20125 &[b"BF.CARD", b"bf"],
20126 &[b"BF.INFO", b"bf"],
20127 &[b"BF.INFO", b"bf", b"CAPACITY"],
20128 &[b"BF.DEBUG", b"bf"],
20129 &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20130 &[b"BF.EXISTS", b"made", b"x"],
20131 &[b"BF.SCANDUMP", b"bf", b"0"],
20132 &[b"CF.RESERVE", b"cf", b"100"],
20134 &[b"CF.ADD", b"cf", b"a"],
20135 &[b"CF.ADDNX", b"cf", b"a"],
20136 &[b"CF.COUNT", b"cf", b"a"],
20137 &[b"CF.EXISTS", b"cf", b"a"],
20138 &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20139 &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20140 &[b"CF.DEL", b"cf", b"a"],
20141 &[b"CF.COMPACT", b"cf"],
20142 &[b"CF.INFO", b"cf"],
20143 &[b"CF.DEBUG", b"cf"],
20144 &[b"CF.SCANDUMP", b"cf", b"0"],
20145 &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20147 &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20148 &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20149 &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20150 &[b"CMS.INFO", b"cms"],
20151 &[b"TOPK.RESERVE", b"tk", b"3"],
20153 &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20154 &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20155 &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20156 &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20157 &[b"TOPK.LIST", b"tk"],
20158 &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20159 &[b"TOPK.INFO", b"tk"],
20160 &[b"TDIGEST.CREATE", b"td"],
20162 &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20163 &[b"TDIGEST.MIN", b"td"],
20164 &[b"TDIGEST.MAX", b"td"],
20165 &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20166 &[b"TDIGEST.CDF", b"td", b"3"],
20167 &[b"TDIGEST.RANK", b"td", b"3"],
20168 &[b"TDIGEST.REVRANK", b"td", b"3"],
20169 &[b"TDIGEST.BYRANK", b"td", b"0"],
20170 &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20171 &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20172 &[b"TDIGEST.INFO", b"td"],
20173 &[b"TDIGEST.RESET", b"td"],
20174 &[b"TDIGEST.MIN", b"td"],
20175 &[b"SET", b"plain", b"v"],
20177 &[b"BF.ADD", b"plain", b"a"],
20178 &[b"CF.ADD", b"plain", b"a"],
20179 &[b"CMS.QUERY", b"plain", b"a"],
20180 &[b"TOPK.ADD", b"plain", b"a"],
20181 &[b"TDIGEST.ADD", b"plain", b"1"],
20182 &[b"CMS.INFO", b"gone"],
20183 &[b"TOPK.INFO", b"gone"],
20184 &[b"TDIGEST.INFO", b"gone"],
20185 ];
20186
20187 let mut one = Fixture::new();
20188 let mut many = Fixture::striped(8);
20189 for parts in script {
20190 let a = one.run(parts);
20191 let b = many.run(parts);
20192 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20193 }
20194 }
20195
20196 #[test]
20203 fn a_sketch_merge_across_stripes_reads_every_source() {
20204 let mut many = Fixture::striped(8);
20205 let other = apart(&mut many, "s1");
20206 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20207 let mut one = Fixture::new();
20208 let mut both = |parts: &[&[u8]]| {
20209 let a = one.run(parts);
20210 let b = many.run(parts);
20211 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20212 a
20213 };
20214
20215 for key in [b"cd".as_slice(), s1, s2] {
20219 both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20220 }
20221 both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20222 both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20223 assert_eq!(
20224 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20225 "+OK\r\n",
20226 "the merge took both sources"
20227 );
20228 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20229 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20231 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20232 both(&[b"SET", b"plain", b"v"]);
20234 assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20235 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20236
20237 both(&[b"FLUSHALL"]);
20240 both(&[b"TDIGEST.CREATE", b"td"]);
20241 both(&[b"TDIGEST.CREATE", s1]);
20242 both(&[b"TDIGEST.CREATE", s2]);
20243 both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20244 both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20245 assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20246 assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20247 assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20248 }
20249
20250 #[test]
20257 fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20258 let script: &[&[&[u8]]] = &[
20259 &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20260 &[b"SORT", b"l"],
20261 &[b"SORT", b"l", b"DESC"],
20262 &[b"SORT", b"l", b"ALPHA"],
20263 &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20264 &[b"SORT_RO", b"l"],
20265 &[
20268 b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20269 ],
20270 &[b"SORT", b"l", b"BY", b"w_*"],
20271 &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20272 &[b"DEL", b"w_2"],
20273 &[b"SORT", b"l", b"BY", b"w_*"],
20274 &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20277 &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20278 &[b"HSET", b"h_1", b"f", b"9"],
20280 &[b"HSET", b"h_2", b"f", b"8"],
20281 &[b"HSET", b"h_3", b"f", b"7"],
20282 &[b"HSET", b"h_10", b"f", b"6"],
20283 &[b"SORT", b"l", b"BY", b"h_*->f"],
20284 &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20285 &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20287 &[b"LRANGE", b"out", b"0", b"-1"],
20288 &[b"SORT", b"l", b"STORE", b"l"],
20289 &[b"LRANGE", b"l", b"0", b"-1"],
20290 &[b"SORT", b"missing", b"STORE", b"out"],
20293 &[b"EXISTS", b"out"],
20294 &[b"SADD", b"s", b"c", b"a", b"b"],
20297 &[b"SORT", b"s", b"ALPHA"],
20298 &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20299 &[b"LRANGE", b"out", b"0", b"-1"],
20300 &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20301 &[b"SORT", b"z", b"BY", b"nosort"],
20302 &[b"SORT", b"z", b"ALPHA", b"DESC"],
20303 &[b"SET", b"str", b"v"],
20306 &[b"SORT", b"str"],
20307 &[b"RPUSH", b"words", b"one", b"two"],
20308 &[b"SORT", b"words"],
20309 &[b"SORT_RO", b"l", b"STORE", b"out"],
20310 ];
20311
20312 let mut one = Fixture::new();
20313 let mut many = Fixture::striped(8);
20314 for parts in script {
20315 let a = one.run(parts);
20316 let b = many.run(parts);
20317 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20318 }
20319 }
20320
20321 #[test]
20328 fn a_sort_across_stripes_reads_every_pattern_key() {
20329 let mut f = Fixture::striped(8);
20330 let out = apart(&mut f, "l");
20331 let (list, dest) = (b"l".as_slice(), out.as_bytes());
20332
20333 f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20334 f.run(&[
20335 b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20336 ]);
20337 f.run(&[
20338 b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20339 ]);
20340
20341 let db = f.server.striped(0);
20344 let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20345 .iter()
20346 .map(|k| db.stripe_of(k.as_slice()))
20347 .collect();
20348 assert!(
20349 weights.iter().any(|s| *s != weights[0]),
20350 "the four weight keys all landed on one stripe, so this proves nothing"
20351 );
20352
20353 assert_eq!(
20354 f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
20355 "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
20356 "the order came off the weights and the answer off the data keys"
20357 );
20358 assert_eq!(
20359 f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20360 ":4\r\n"
20361 );
20362 assert_eq!(
20363 f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20364 "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20365 "the destination is on a stripe of its own and got the whole answer"
20366 );
20367 }
20368
20369 #[test]
20377 fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20378 let mut f = Fixture::striped(8);
20379 let other = apart(&mut f, "h");
20380 let (first, second) = (b"h".as_slice(), other.as_bytes());
20381
20382 assert_eq!(
20383 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
20384 "+OK\r\n"
20385 );
20386 assert_eq!(
20387 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
20388 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
20389 "the read comes off one stripe and has to answer for all of them"
20390 );
20391 for key in [first, second] {
20392 f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
20393 assert_eq!(
20394 f.run(&[b"OBJECT", b"ENCODING", key]),
20395 "$8\r\nlistpack\r\n",
20396 "two fields is still under the ladder"
20397 );
20398 f.run(&[b"HSET", key, b"c", b"3"]);
20399 assert_eq!(
20400 f.run(&[b"OBJECT", b"ENCODING", key]),
20401 "$9\r\nhashtable\r\n",
20402 "three fields is over it, on whichever stripe the key is on"
20403 );
20404 }
20405
20406 assert_eq!(
20409 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
20410 "+OK\r\n"
20411 );
20412 let db = f.server.striped(0);
20413 assert!(
20414 db.stripes()
20415 .iter()
20416 .all(|s| s.policy().name() == "allkeys-lru"),
20417 "a stripe kept the old policy"
20418 );
20419 }
20420}