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::lock::Held;
95use yo_common::{Code, Error};
96use yo_kv::cold::Blocks;
97use yo_kv::{Clock, Db, Keyspace};
98use yo_search::Registry;
99
100pub const DATABASES: usize = 16;
107
108const ALL_DATABASES: u64 = if DATABASES == 64 {
115 u64::MAX
116} else {
117 (1u64 << DATABASES) - 1
118};
119const _: () = assert!(DATABASES <= 64);
120
121const EVICT_BUDGET: usize = 64;
133
134const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub enum Flow {
143 Continue,
145 Close,
147 Block,
154}
155
156#[derive(Debug, Clone, Copy, Default)]
162pub struct Stats {
163 pub clients: u64,
165 pub connections: u64,
167 pub commands: u64,
169}
170
171fn working_dir() -> PathBuf {
178 std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
179}
180
181#[derive(Debug, Clone, Copy, Default)]
190pub struct CommandStat {
191 pub calls: u64,
193 pub rejected: u64,
196 pub failed: u64,
198}
199
200impl CommandStat {
201 const fn seen(&self) -> bool {
207 self.calls != 0 || self.rejected != 0 || self.failed != 0
208 }
209}
210
211struct CommandStats(Box<[CommandStat]>);
218
219impl Default for CommandStats {
220 fn default() -> CommandStats {
221 CommandStats(vec![CommandStat::default(); table::count()].into_boxed_slice())
222 }
223}
224
225impl CommandStats {
226 fn at(&mut self, spec: &'static Spec) -> &mut CommandStat {
228 &mut self.0[table::index_of(spec)]
229 }
230}
231
232pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
238
239pub struct Server {
246 dbs: Vec<Db>,
247 width: usize,
253 clock: Clock,
254 started_ms: u64,
255 next_db: usize,
258 dirty: u64,
268 conn_bytes: usize,
270 maxmemory: u64,
275 store: Option<Box<StoreSource>>,
286 maxstore: Option<u64>,
302 used: usize,
314 evict_db: usize,
320 expire_db: usize,
327 expire_ms: u64,
330 waiters: Waiters,
332 peers: migrate::Peers,
337 pub stats: Stats,
339 cmdstats: CommandStats,
341 dir: PathBuf,
349 backup: backup::State,
354 search: Registry,
364 stopping: bool,
373}
374
375impl Server {
376 #[must_use]
378 pub fn new() -> Server {
379 let clock = Clock::system();
380 Server {
381 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
382 width: 1,
383 clock,
384 started_ms: clock.now_ms(),
385 next_db: 0,
386 dirty: ALL_DATABASES,
387 conn_bytes: 0,
388 maxmemory: 0,
389 store: None,
390 maxstore: None,
391 used: 0,
392 evict_db: 0,
393 expire_db: 0,
394 expire_ms: 0,
395 waiters: Waiters::default(),
396 peers: migrate::Peers::default(),
397 stats: Stats::default(),
398 cmdstats: CommandStats::default(),
399 dir: working_dir(),
400 backup: backup::State::default(),
401 search: Registry::new(),
402 stopping: false,
403 }
404 }
405
406 #[must_use]
417 pub fn with_width(width: usize) -> Server {
418 let clock = Clock::system();
419 let mut server = Server::new();
420 server.dbs = (0..DATABASES)
421 .map(|_| Db::with_clock(clock, width))
422 .collect();
423 server.width = server.dbs[0].width();
424 server
425 }
426
427 #[must_use]
429 pub fn with_clock(clock: Clock) -> Server {
430 Server {
431 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
432 width: 1,
433 clock,
434 started_ms: clock.now_ms(),
435 next_db: 0,
436 dirty: ALL_DATABASES,
437 conn_bytes: 0,
438 maxmemory: 0,
439 store: None,
440 maxstore: None,
441 used: 0,
442 evict_db: 0,
443 expire_db: 0,
444 expire_ms: 0,
445 waiters: Waiters::default(),
446 peers: migrate::Peers::default(),
447 stats: Stats::default(),
448 cmdstats: CommandStats::default(),
449 dir: working_dir(),
450 backup: backup::State::default(),
451 search: Registry::new(),
452 stopping: false,
453 }
454 }
455
456 pub fn striped(&mut self, i: usize) -> &mut Db {
474 self.dirty |= 1u64 << i;
475 &mut self.dbs[i]
476 }
477
478 fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
485 self.dbs
486 .iter()
487 .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
488 }
489
490 fn keyspaces_mut(&mut self) -> impl Iterator<Item = &mut Keyspace> {
492 self.dbs.iter_mut().flat_map(Db::stripes_mut)
493 }
494
495 const fn slots(&self) -> usize {
501 DATABASES * self.width
502 }
503
504 const fn slot_db(&self, i: usize) -> usize {
506 i / self.width
507 }
508
509 fn slot_mut(&mut self, i: usize) -> &mut Keyspace {
511 let (db, stripe) = (i / self.width, i % self.width);
512 self.dbs[db].stripe_mut(stripe)
513 }
514
515 fn slot(&self, i: usize) -> Held<'_, Keyspace> {
517 let (db, stripe) = (i / self.width, i % self.width);
518 self.dbs[db].hold_stripe(stripe)
519 }
520
521 #[must_use]
523 pub fn dir(&self) -> &Path {
524 &self.dir
525 }
526
527 pub fn set_dir(&mut self, dir: PathBuf) {
534 self.dir = dir;
535 }
536
537 pub fn backup_expire(&mut self) {
543 backup::expire(self);
544 }
545
546 pub fn stop(&mut self) {
552 self.stopping = true;
553 }
554
555 #[must_use]
561 pub fn stopping(&self) -> bool {
562 self.stopping
563 }
564
565 #[must_use]
572 pub fn striped_ref(&self, i: usize) -> &Db {
573 &self.dbs[i]
574 }
575
576 fn settings(&self) -> Held<'_, Keyspace> {
584 self.dbs[0].hold_stripe(0)
585 }
586
587 pub fn refresh_clock(&mut self) {
593 self.clock.refresh();
594 let now = self.clock.now_ms();
595 for db in &mut self.dbs {
596 db.set_clock_ms(now);
597 }
598 }
599
600 pub fn advance_clock_ms(&mut self, ms: u64) {
606 let now = self.clock.now_ms() + ms;
607 self.set_clock_ms(now);
608 }
609
610 pub fn set_clock_ms(&mut self, ms: u64) {
618 self.clock.set(ms);
619 for db in &mut self.dbs {
620 db.set_clock_ms(ms);
621 }
622 }
623
624 #[must_use]
626 pub fn uptime_secs(&self) -> u64 {
627 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
628 }
629
630 #[must_use]
638 pub fn memory_bytes(&self) -> usize {
639 self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes
640 }
641
642 #[must_use]
648 pub fn dataset_bytes(&self) -> usize {
649 self.keyspaces()
650 .map(|db| db.map().arena().live_bytes() as usize)
651 .sum()
652 }
653
654 #[must_use]
656 pub fn arena_bytes(&self) -> usize {
657 self.keyspaces()
658 .map(|db| db.map().arena().reserved_bytes() as usize)
659 .sum()
660 }
661
662 #[must_use]
664 pub fn index_bytes(&self) -> usize {
665 self.keyspaces()
666 .map(|db| db.map().index().memory_bytes())
667 .sum()
668 }
669
670 #[must_use]
677 pub fn compaction(&self) -> yo_kv::Compaction {
678 self.keyspaces().map(|db| db.map().compaction()).fold(
679 yo_kv::Compaction::default(),
680 |a, b| yo_kv::Compaction {
681 walked: a.walked + b.walked,
682 moved: a.moved + b.moved,
683 bytes: a.bytes + b.bytes,
684 },
685 )
686 }
687
688 #[must_use]
690 pub fn segment_count(&self) -> usize {
691 self.keyspaces()
692 .map(|db| db.map().arena().resident_segments())
693 .sum()
694 }
695
696 #[must_use]
698 pub const fn conn_bytes(&self) -> usize {
699 self.conn_bytes
700 }
701
702 pub fn note_conn_bytes(&mut self, delta: isize) {
710 self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
711 }
712
713 #[must_use]
715 pub fn expired_keys(&self) -> u64 {
716 self.keyspaces().map(|db| db.expired_keys()).sum()
717 }
718
719 #[must_use]
721 pub fn evicted_keys(&self) -> u64 {
722 self.keyspaces().map(|db| db.evicted_keys()).sum()
723 }
724
725 pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
731 self.cmdstats
732 .0
733 .iter()
734 .enumerate()
735 .filter(|(_, row)| row.seen())
736 .map(|(at, row)| (table::name_at(at), *row))
737 }
738
739 #[must_use]
741 pub const fn maxmemory(&self) -> u64 {
742 self.maxmemory
743 }
744
745 pub fn set_maxmemory(&mut self, bytes: u64) {
759 self.maxmemory = bytes;
760 for db in &mut self.dbs {
761 db.track_memory(bytes != 0);
762 }
763 self.used = self.settled_memory();
764 }
765
766 pub fn set_store_source(
776 &mut self,
777 source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
778 ) {
779 self.store = Some(Box::new(source));
780 }
781
782 #[must_use]
784 pub const fn has_store_source(&self) -> bool {
785 self.store.is_some()
786 }
787
788 fn attach_store(&mut self, at: usize) {
795 if self.slot(at).store_bytes().is_some() {
796 return;
797 }
798 let Some(source) = self.store.as_mut() else {
799 return;
800 };
801 if let Some(blocks) = source(at) {
802 self.slot_mut(at).attach(blocks);
803 }
804 }
805
806 #[must_use]
808 pub const fn maxstore(&self) -> Option<u64> {
809 self.maxstore
810 }
811
812 pub const fn set_maxstore(&mut self, bytes: Option<u64>) {
818 self.maxstore = bytes;
819 }
820
821 #[must_use]
827 pub fn store_bytes(&self) -> u64 {
828 self.keyspaces().filter_map(|db| db.store_bytes()).sum()
829 }
830
831 #[must_use]
846 pub fn cold_stats(&self) -> yo_kv::tier::Stats {
847 let mut total = yo_kv::tier::Stats::default();
848 for db in self.keyspaces() {
849 let Some(tier) = db.tier() else { continue };
850 let s = tier.stats();
851 total.demoted += s.demoted;
852 total.promoted += s.promoted;
853 total.faults += s.faults;
854 total.served += s.served;
855 total.bytes_out += s.bytes_out;
856 total.bytes_in += s.bytes_in;
857 }
858 total
859 }
860
861 #[must_use]
868 pub fn regime(&self) -> &'static str {
869 if (0..self.slots()).any(|at| self.migrates(at)) {
870 "migrate"
871 } else {
872 "evict"
873 }
874 }
875
876 fn migrates(&self, at: usize) -> bool {
888 if self.maxstore == Some(0) {
889 return false;
890 }
891 let bytes = self.slot(at).store_bytes();
895 match bytes {
896 Some(held) => self.maxstore.is_none_or(|cap| held < cap),
897 None => self.store.is_some(),
901 }
902 }
903
904 pub fn refresh_memory(&mut self) {
909 if self.maxmemory != 0 {
910 self.used = self.settled_memory();
911 }
912 }
913
914 fn settled_memory(&mut self) -> usize {
921 self.keyspaces_mut()
922 .map(Keyspace::settled_memory_bytes)
923 .sum::<usize>()
924 + self.conn_bytes
925 }
926
927 pub fn make_room(&mut self) -> bool {
961 if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
962 return true;
963 }
964 self.used = self.settled_memory();
969 let mut budget = EVICT_BUDGET;
970 while self.used as u64 > self.maxmemory {
971 let over = self.used - self.maxmemory as usize;
972 if !self.relieve_step(over) {
973 return false;
974 }
975 self.compact_hard_step();
976 self.used = self.settled_memory();
977 budget -= 1;
978 if budget == 0 {
979 break;
980 }
981 }
982 true
983 }
984
985 fn relieve_step(&mut self, over: usize) -> bool {
1002 for turn in 0..self.slots() {
1003 let i = (self.evict_db + turn) % self.slots();
1004 let used = !self.slot(i).is_empty();
1007 let gave = if used && self.migrates(i) {
1008 self.attach_store(i);
1009 self.slot_mut(i)
1014 .relieve(over)
1015 .is_ok_and(yo_kv::tier::Relief::made_room)
1016 } else {
1017 self.slot_mut(i).evict_one()
1018 };
1019 if gave {
1020 self.evict_db = (i + 1) % self.slots();
1021 self.dirty |= 1u64 << self.slot_db(i);
1022 return true;
1023 }
1024 }
1025 false
1026 }
1027
1028 pub fn expire_slice(&mut self, budget: usize) -> usize {
1043 let now = self.clock.now_ms();
1044 if now == self.expire_ms {
1045 return 0;
1046 }
1047 self.expire_ms = now;
1048 self.expire_step(budget)
1049 }
1050
1051 pub fn expire_step(&mut self, budget: usize) -> usize {
1067 let mut spent = 0;
1068 for turn in 0..self.slots() {
1069 if spent >= budget {
1070 break;
1071 }
1072 let i = (self.expire_db + turn) % self.slots();
1073 let c = self.slot_mut(i).expire_cycle(budget - spent);
1074 spent += c.examined;
1075 if c.expired > 0 {
1076 self.expire_db = (i + 1) % self.slots();
1077 self.dirty |= 1u64 << self.slot_db(i);
1078 }
1079 }
1080 spent
1081 }
1082
1083 fn compact_hard_step(&mut self) -> Option<usize> {
1089 for turn in 0..self.slots() {
1090 let i = (self.next_db + turn) % self.slots();
1091 if let Some(moved) = self.slot_mut(i).compact_hard() {
1092 self.next_db = (i + 1) % self.slots();
1093 return Some(moved);
1094 }
1095 }
1096 None
1097 }
1098
1099 pub fn compact_step(&mut self) -> Option<usize> {
1112 for turn in 0..self.slots() {
1113 let i = (self.next_db + turn) % self.slots();
1114 let at = self.slot_db(i);
1118 if self.dirty & (1 << at) == 0 {
1119 continue;
1120 }
1121 if let Some(moved) = self.slot_mut(i).compact_step() {
1122 self.next_db = (i + 1) % self.slots();
1123 return Some(moved);
1124 }
1125 if i % self.width == self.width - 1 {
1129 self.dirty &= !(1u64 << at);
1130 }
1131 }
1132 None
1133 }
1134}
1135
1136impl Default for Server {
1137 fn default() -> Server {
1138 Server::new()
1139 }
1140}
1141
1142pub struct Session {
1144 db: usize,
1145 id: u64,
1146 name: Vec<u8>,
1147 sets: himport::Fieldsets,
1153}
1154
1155impl Session {
1156 #[must_use]
1158 pub fn new(id: u64) -> Session {
1159 Session {
1160 db: 0,
1161 id,
1162 name: Vec::new(),
1163 sets: himport::Fieldsets::default(),
1164 }
1165 }
1166
1167 #[must_use]
1169 pub const fn id(&self) -> u64 {
1170 self.id
1171 }
1172
1173 #[must_use]
1175 pub const fn db(&self) -> usize {
1176 self.db
1177 }
1178
1179 #[must_use]
1181 pub fn name(&self) -> &[u8] {
1182 &self.name
1183 }
1184
1185 pub fn reset(&mut self) {
1190 self.db = 0;
1191 self.name.clear();
1192 self.sets.clear();
1196 }
1197
1198 fn set_name(&mut self, name: &[u8]) {
1200 yo_alloc::allow(|| {
1201 self.name.clear();
1202 self.name.extend_from_slice(name);
1203 });
1204 }
1205}
1206
1207pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1212 if args.is_empty() {
1215 return Flow::Continue;
1216 }
1217 resolved(server, session, lookup(args.name()), args, out)
1218}
1219
1220pub fn resolved(
1232 server: &mut Server,
1233 session: &mut Session,
1234 spec: Option<&'static Spec>,
1235 args: Args<'_>,
1236 out: &mut Out,
1237) -> Flow {
1238 if args.is_empty() {
1239 return Flow::Continue;
1240 }
1241 server.stats.commands += 1;
1242
1243 let Some(spec) = spec else {
1244 write_error(out, &args::unknown_command(args));
1245 return Flow::Continue;
1246 };
1247 if !arity_ok(spec, args.len()) {
1248 server.cmdstats.at(spec).rejected += 1;
1249 write_error(out, &args::wrong_arity(spec.name));
1250 return Flow::Continue;
1251 }
1252
1253 if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1263 server.cmdstats.at(spec).rejected += 1;
1264 out.error_line(b"OOM ", OOM);
1265 return Flow::Continue;
1266 }
1267
1268 server.dirty |= match spec.group {
1275 "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1276 | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1277 1u64 << session.db
1278 }
1279 _ => ALL_DATABASES,
1280 };
1281
1282 let mark = out.len();
1283 let done = if spec.flags.contains(&"blocking") {
1290 blocking::execute(server, session, spec, args, out)
1291 } else {
1292 match spec.group {
1293 "string" => {
1294 let db = session.db;
1295 strings::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1296 }
1297 "bitmap" => {
1301 let db = session.db;
1302 bits::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1303 }
1304 "hyperloglog" => {
1307 let db = session.db;
1308 hll::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1309 }
1310 "set" => {
1311 let db = session.db;
1312 sets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1313 }
1314 "hash" if spec.name == "himport" => {
1319 let db = session.db;
1320 himport::execute(&server.dbs[db], &mut session.sets, args, out)
1321 .map(|()| Flow::Continue)
1322 }
1323 "hash" => {
1324 let db = session.db;
1325 hashes::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1326 }
1327 "list" => {
1328 let db = session.db;
1329 lists::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1330 }
1331 "zset" => {
1332 let db = session.db;
1333 zsets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1334 }
1335 "geo" => {
1339 let db = session.db;
1340 geo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1341 }
1342 "array" => {
1343 let db = session.db;
1344 arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1345 }
1346 "graph" => {
1347 let db = session.db;
1348 graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1349 }
1350 "json" => {
1354 let db = session.db;
1355 json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1356 }
1357 "vector" => {
1358 let db = session.db;
1359 vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1360 }
1361 "bloom" => {
1362 let db = session.db;
1363 bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1364 }
1365 "cuckoo" => {
1366 let db = session.db;
1367 cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1368 }
1369 "cms" => {
1370 let db = session.db;
1371 cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1372 }
1373 "topk" => {
1374 let db = session.db;
1375 topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1376 }
1377 "tdigest" => {
1378 let db = session.db;
1379 tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1380 }
1381 "ts" => {
1382 let db = session.db;
1383 ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1384 }
1385 "stream" => {
1390 let db = session.db;
1391 let now = server.now_ms();
1392 streams::execute(&server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1393 }
1394 "keyspace" if spec.name == "migrate" => {
1398 migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1399 }
1400 "keyspace" => {
1403 keyspace::execute(&server.dbs, session.db, spec, args, out).map(|()| Flow::Continue)
1404 }
1405 "search" => {
1408 search::execute(&mut server.search, spec, args, out).map(|()| Flow::Continue)
1409 }
1410 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1411 _ => server::execute(server, session, spec, args, out),
1412 }
1413 };
1414 let flow = match done {
1415 Ok(flow) => flow,
1416 Err(e) => {
1417 out.truncate(mark);
1418 write_error(out, &e);
1419 Flow::Continue
1420 }
1421 };
1422
1423 let row = server.cmdstats.at(spec);
1434 row.calls += 1;
1435 if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1436 row.failed += 1;
1437 }
1438 flow
1439}
1440
1441fn write_error(out: &mut Out, e: &Error) {
1451 let prefix: &[u8] = match e.code() {
1452 Code::WrongType => b"WRONGTYPE ",
1453 Code::Corrupt => b"INVALIDOBJ ",
1457 _ => b"ERR ",
1458 };
1459 out.error_line(prefix, e.message().as_bytes());
1460}
1461
1462#[cfg(test)]
1463mod tests {
1464 use super::*;
1465 use crate::proto::{Limits, Proto};
1466 use crate::request::Argv;
1467
1468 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1473 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1474 for p in parts {
1475 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1476 wire.extend_from_slice(p);
1477 wire.extend_from_slice(b"\r\n");
1478 }
1479 wire
1480 }
1481
1482 struct Fixture {
1484 server: Server,
1485 session: Session,
1486 argv: Argv,
1487 out: Out,
1488 }
1489
1490 impl Fixture {
1491 fn new() -> Fixture {
1492 Fixture::on(Server::new())
1493 }
1494
1495 fn striped(width: usize) -> Fixture {
1497 Fixture::on(Server::with_width(width))
1498 }
1499
1500 fn on(server: Server) -> Fixture {
1501 Fixture {
1502 server,
1503 session: Session::new(7),
1504 argv: Argv::new(),
1505 out: Out::new(Proto::Resp2),
1506 }
1507 }
1508
1509 fn run(&mut self, parts: &[&[u8]]) -> String {
1511 self.flow(parts).1
1512 }
1513
1514 fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1520 let wire = encode(parts);
1521 self.argv.decode(&wire, &Limits::default()).unwrap();
1522 self.out.clear();
1523 execute(
1524 &mut self.server,
1525 &mut self.session,
1526 Args::new(&self.argv, &wire),
1527 &mut self.out,
1528 );
1529 self.out.as_slice().to_vec()
1530 }
1531
1532 fn advance(&mut self, ms: u64) {
1534 self.server.advance_clock_ms(ms);
1535 }
1536
1537 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1539 let wire = encode(parts);
1540 self.argv.decode(&wire, &Limits::default()).unwrap();
1541 self.out.clear();
1542 let flow = execute(
1543 &mut self.server,
1544 &mut self.session,
1545 Args::new(&self.argv, &wire),
1546 &mut self.out,
1547 );
1548 (
1549 flow,
1550 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1551 )
1552 }
1553 }
1554
1555 #[test]
1559 fn rewriting_the_same_keys_does_not_grow_the_server() {
1560 let mut f = Fixture::new();
1561 let val = vec![b'v'; 1024];
1562 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1563
1564 for k in &keys {
1565 f.run(&[b"SET", k, &val]);
1566 }
1567 f.server.compact_step();
1568 let after_first = f.server.memory_bytes();
1569
1570 for _ in 0..500 {
1575 for k in &keys {
1576 f.run(&[b"SET", k, &val]);
1577 }
1578 f.server.compact_step();
1579 }
1580
1581 assert!(
1582 f.server.memory_bytes() <= after_first * 2,
1583 "held {} after five hundred passes against {after_first} after one",
1584 f.server.memory_bytes()
1585 );
1586 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1587 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1588 }
1589
1590 #[test]
1604 fn a_database_nobody_started_on_is_still_collected() {
1605 let mut f = Fixture::new();
1606 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1607 let val = vec![b'v'; 1024];
1608 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1609
1610 for k in &keys {
1611 f.run(&[b"SET", k, &val]);
1612 }
1613 while f.server.compact_step().is_some() {}
1614 assert_eq!(
1615 f.server.dirty & (1 << 9),
1616 0,
1617 "database nine was drained and should not be asked again until it is written to"
1618 );
1619 let after_first = f.server.memory_bytes();
1620
1621 for _ in 0..500 {
1622 for k in &keys {
1623 f.run(&[b"SET", k, &val]);
1624 }
1625 f.server.compact_step();
1626 }
1627
1628 assert!(
1629 f.server.memory_bytes() <= after_first * 2,
1630 "held {} after five hundred passes against {after_first} after one",
1631 f.server.memory_bytes()
1632 );
1633 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1634 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1635 f.run(&[b"SELECT", b"0"]);
1637 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1638 }
1639
1640 #[test]
1641 fn a_command_goes_from_bytes_to_bytes() {
1642 let mut f = Fixture::new();
1643 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1644 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1645 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1646 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1647 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1649 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1650 }
1651
1652 #[test]
1653 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1654 let mut f = Fixture::new();
1655 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1656 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1659 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1660 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1661 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1663 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1664 }
1665
1666 #[test]
1667 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1668 let mut f = Fixture::new();
1669 f.run(&[b"SET", b"k", b"v"]);
1670 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1673 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1674 }
1675
1676 #[test]
1677 fn touch_counts_the_way_exists_counts() {
1678 let mut f = Fixture::new();
1679 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1680 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1681 assert_eq!(
1682 f.run(&[b"TOUCH", b"a", b"a"]),
1683 ":2\r\n",
1684 "twice counts twice"
1685 );
1686 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1687 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1688 }
1689
1690 #[test]
1691 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1692 let mut f = Fixture::new();
1693 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1694 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1695
1696 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1697 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1698 assert_eq!(
1699 f.run(&[b"TTL", b"b"]),
1700 ":100\r\n",
1701 "the source's and not b's"
1702 );
1703 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1704 }
1705
1706 #[test]
1707 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1708 let mut f = Fixture::new();
1709 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1710 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1713 }
1714
1715 #[test]
1716 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1717 let mut f = Fixture::new();
1718 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1719
1720 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1721 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1722 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1725 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1726 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1727 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1728 }
1729
1730 #[test]
1731 fn renaming_a_set_does_not_touch_a_member() {
1732 let mut f = Fixture::new();
1733 for i in 0..300 {
1734 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1735 }
1736 let before = f.server.memory_bytes();
1737
1738 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1739 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1740 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1741 assert!(
1742 f.server.memory_bytes().abs_diff(before) < 256,
1743 "the members were copied: {} against {before}",
1744 f.server.memory_bytes()
1745 );
1746 }
1747
1748 #[test]
1749 fn a_copy_is_a_second_value_and_not_a_second_name() {
1750 let mut f = Fixture::new();
1751 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1752
1753 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1754 f.run(&[b"SADD", b"t", b"m3"]);
1755 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1756 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1757 }
1758
1759 #[test]
1769 fn every_type_can_be_copied() {
1770 let mut f = Fixture::new();
1771 f.run(&[b"SET", b"str", b"v1"]);
1772 f.run(&[b"SADD", b"set", b"m1"]);
1773 f.run(&[b"HSET", b"hash", b"f", b"v"]);
1774 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1775 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1776
1777 for name in [
1778 &b"str"[..],
1779 &b"set"[..],
1780 &b"hash"[..],
1781 &b"list"[..],
1782 &b"zset"[..],
1783 ] {
1784 let dst = [name, b":copy"].concat();
1785 assert_eq!(
1786 f.run(&[b"COPY", name, &dst]),
1787 ":1\r\n",
1788 "copying {}",
1789 String::from_utf8_lossy(name)
1790 );
1791 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1792 }
1793
1794 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1795 let mut want = String::from("*2\r\n");
1796 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1797 want
1798 });
1799 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1800
1801 f.run(&[b"RPUSH", b"list:copy", b"c"]);
1803 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1804 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1805 }
1806
1807 #[test]
1808 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1809 let mut f = Fixture::new();
1810 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1811 f.run(&[b"SET", b"b", b"v2"]);
1812
1813 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1814 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1815 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1816 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1817 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1818 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1819 }
1820
1821 #[test]
1822 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1823 let mut f = Fixture::new();
1824 f.run(&[b"SET", b"a", b"v1"]);
1825
1826 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1829 f.run(&[b"SELECT", b"1"]);
1830 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1831 assert_eq!(
1832 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1833 ":0\r\n",
1834 "taken"
1835 );
1836 assert_eq!(
1837 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1838 ":1\r\n"
1839 );
1840 }
1841
1842 #[test]
1843 fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1844 let mut f = Fixture::new();
1845 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1846 assert_eq!(
1847 f.run(&[b"SORT", b"l"]),
1848 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1849 );
1850 assert_eq!(
1852 f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1853 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1854 );
1855 assert_eq!(
1856 f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1857 "*1\r\n$1\r\n2\r\n"
1858 );
1859 }
1860
1861 #[test]
1862 fn sort_reads_a_key_per_element_for_by_and_for_get() {
1863 let mut f = Fixture::new();
1864 f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1865 f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1866 assert_eq!(
1869 f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1870 "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1871 );
1872 }
1873
1874 #[test]
1875 fn sort_store_writes_a_list_and_answers_its_length() {
1876 let mut f = Fixture::new();
1877 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1878 assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1879 assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1880 assert_eq!(
1881 f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1882 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1883 );
1884 assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1887 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1888 }
1889
1890 #[test]
1891 fn sort_ro_does_not_know_the_word_store() {
1892 let mut f = Fixture::new();
1893 f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1894 assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1895 assert_eq!(
1896 f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1897 "-ERR syntax error\r\n"
1898 );
1899 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1900 }
1901
1902 #[test]
1903 fn sort_refuses_what_it_cannot_sort() {
1904 let mut f = Fixture::new();
1905 assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1906 f.run(&[b"SET", b"s", b"x"]);
1907 assert_eq!(
1908 f.run(&[b"SORT", b"s"]),
1909 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1910 );
1911 f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1912 assert_eq!(
1913 f.run(&[b"SORT", b"words"]),
1914 "-ERR One or more scores can't be converted into double\r\n"
1915 );
1916 assert_eq!(
1917 f.run(&[b"SORT", b"words", b"ALPHA"]),
1918 "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1919 );
1920 assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1921 }
1922
1923 #[test]
1924 fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1925 let mut f = Fixture::new();
1926 assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1927 assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1928 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1929 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1930 assert_eq!(
1931 f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1932 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1933 );
1934 assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1937 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1938 }
1939
1940 #[test]
1941 fn move_answers_zero_when_either_end_says_no() {
1942 let mut f = Fixture::new();
1943 assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1944 assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1945 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1946 assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1947 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1948 assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1951 assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1952 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1953 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1954 }
1955
1956 #[test]
1957 fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1958 let mut f = Fixture::new();
1959 assert_eq!(
1960 f.run(&[b"MOVE", b"a", b"0"]),
1961 "-ERR source and destination objects are the same\r\n"
1962 );
1963 assert_eq!(
1964 f.run(&[b"MOVE", b"a", b"99"]),
1965 "-ERR DB index is out of range\r\n"
1966 );
1967 assert_eq!(
1968 f.run(&[b"MOVE", b"a", b"-1"]),
1969 "-ERR DB index is out of range\r\n"
1970 );
1971 assert_eq!(
1972 f.run(&[b"MOVE", b"a", b"x"]),
1973 "-ERR value is not an integer or out of range\r\n"
1974 );
1975 }
1976
1977 #[test]
1978 fn swapdb_swaps_what_two_connections_would_see() {
1979 let mut f = Fixture::new();
1980 assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1981 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1982 assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1983 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1984
1985 assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1986 assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1988 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1989 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1990 assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1992 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1993 }
1994
1995 #[test]
1996 fn swapdb_says_which_index_it_could_not_read() {
1997 let mut f = Fixture::new();
1998 assert_eq!(
1999 f.run(&[b"SWAPDB", b"x", b"1"]),
2000 "-ERR invalid first DB index\r\n"
2001 );
2002 assert_eq!(
2003 f.run(&[b"SWAPDB", b"0", b"y"]),
2004 "-ERR invalid second DB index\r\n"
2005 );
2006 assert_eq!(
2010 f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2011 "-ERR invalid first DB index\r\n"
2012 );
2013 assert_eq!(
2014 f.run(&[b"SWAPDB", b"0", b"99"]),
2015 "-ERR DB index is out of range\r\n"
2016 );
2017 assert_eq!(
2018 f.run(&[b"SWAPDB", b"-1", b"0"]),
2019 "-ERR DB index is out of range\r\n"
2020 );
2021 }
2022
2023 #[test]
2024 fn wait_answers_zero_replicas_without_waiting() {
2025 let mut f = Fixture::new();
2026 assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2027 assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2028 assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2031 assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2034 assert_eq!(
2035 f.run(&[b"WAIT", b"x", b"0"]),
2036 "-ERR value is not an integer or out of range\r\n"
2037 );
2038 assert_eq!(
2039 f.run(&[b"WAIT", b"0", b"-1"]),
2040 "-ERR timeout is negative\r\n"
2041 );
2042 assert_eq!(
2043 f.run(&[b"WAIT", b"0", b"1.5"]),
2044 "-ERR timeout is not an integer or out of range\r\n"
2045 );
2046 }
2047
2048 #[test]
2049 fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2050 let mut f = Fixture::new();
2051 assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2052 assert_eq!(
2053 f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2054 "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2055 );
2056 assert_eq!(
2057 f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2058 "-ERR value is out of range, value must between 0 and 1\r\n"
2059 );
2060 assert_eq!(
2061 f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2062 "-ERR value is out of range, must be positive\r\n"
2063 );
2064 assert_eq!(
2067 f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2068 "-ERR timeout is negative\r\n"
2069 );
2070 }
2071
2072 fn payload(reply: &[u8]) -> Vec<u8> {
2076 let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2077 reply[head + 2..reply.len() - 2].to_vec()
2078 }
2079
2080 #[test]
2081 fn a_value_survives_a_dump_and_a_restore() {
2082 let mut f = Fixture::new();
2083 f.run(&[b"SET", b"s", b"hello"]);
2084 f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2085 f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2086 f.run(&[b"SADD", b"u", b"x", b"y"]);
2087 f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2088 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2089
2090 for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2091 let mut copy = key.to_vec();
2092 copy.push(b'2');
2093 let bytes = payload(&f.raw(&[b"DUMP", key]));
2094 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
2095 assert_eq!(f.run(&[b"TYPE", ©]), f.run(&[b"TYPE", key]));
2096 }
2097
2098 assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2099 assert_eq!(
2100 f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2101 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2102 );
2103 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2104 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2105 assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2106 assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2107 assert_eq!(
2110 f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2111 f.run(&[b"OBJECT", b"ENCODING", b"t"])
2112 );
2113 }
2114
2115 #[test]
2116 fn a_dumped_hash_keeps_its_field_deadlines() {
2117 let mut f = Fixture::new();
2118 f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2119 assert_eq!(
2120 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2121 "*1\r\n:1\r\n"
2122 );
2123 let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2124 assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2125 assert_eq!(
2126 f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2127 "*2\r\n:-1\r\n:100\r\n"
2128 );
2129 }
2130
2131 #[test]
2132 fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2133 let mut f = Fixture::new();
2134 f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2135 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2136 assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2137 assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2138 assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2139 assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2140 assert_eq!(
2143 f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2144 "+OK\r\n"
2145 );
2146 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2147 }
2148
2149 #[test]
2150 fn dump_answers_nothing_for_a_key_that_is_not_there() {
2151 let mut f = Fixture::new();
2152 assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2153 f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2154 f.advance(50);
2155 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2156 }
2157
2158 #[test]
2159 fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2160 let mut f = Fixture::new();
2161 f.run(&[b"SET", b"a", b"first"]);
2162 f.run(&[b"SET", b"b", b"second"]);
2163 let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2164 assert_eq!(
2165 f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2166 "-BUSYKEY Target key name already exists.\r\n"
2167 );
2168 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2169 assert_eq!(
2170 f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2171 "+OK\r\n"
2172 );
2173 assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2174 }
2175
2176 #[test]
2180 fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2181 let mut f = Fixture::new();
2182 f.run(&[b"SET", b"a", b"v"]);
2183 assert_eq!(
2184 f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2185 "-BUSYKEY Target key name already exists.\r\n"
2186 );
2187 assert_eq!(
2190 f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2191 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2192 );
2193 }
2194
2195 #[test]
2196 fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2197 let mut f = Fixture::new();
2198 f.run(&[b"SET", b"a", b"hello"]);
2199 let good = payload(&f.raw(&[b"DUMP", b"a"]));
2200
2201 let mut flipped = good.clone();
2202 flipped[2] ^= 0x40;
2203 assert_eq!(
2204 f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2205 "-ERR DUMP payload version or checksum are wrong\r\n"
2206 );
2207 assert_eq!(
2208 f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2209 "-ERR DUMP payload version or checksum are wrong\r\n"
2210 );
2211 let mut truncated = good[..1].to_vec();
2215 truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2216 let crc = yo_common::crc::crc64(0, &truncated);
2217 truncated.extend_from_slice(&crc.to_le_bytes());
2218 assert_eq!(
2219 f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2220 "-ERR Bad data format\r\n"
2221 );
2222 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2223 }
2224
2225 #[test]
2226 fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2227 let mut f = Fixture::new();
2228 f.run(&[b"SET", b"a", b"v"]);
2229 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2230 assert_eq!(
2231 f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2232 "-ERR Invalid TTL value, must be >= 0\r\n"
2233 );
2234 assert_eq!(
2235 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2236 "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2237 );
2238 assert_eq!(
2239 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2240 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2241 );
2242 assert_eq!(
2244 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2245 "+OK\r\n"
2246 );
2247 assert_eq!(
2248 f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2249 "+OK\r\n"
2250 );
2251 }
2252
2253 #[test]
2257 fn restore_takes_idletime_or_freq_and_not_both() {
2258 let mut f = Fixture::new();
2259 f.run(&[b"SET", b"a", b"v"]);
2260 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2261 assert_eq!(
2262 f.run(&[
2263 b"RESTORE",
2264 b"b",
2265 b"0",
2266 &bytes,
2267 b"IDLETIME",
2268 b"1",
2269 b"FREQ",
2270 b"2"
2271 ]),
2272 "-ERR syntax error\r\n"
2273 );
2274 assert_eq!(
2275 f.run(&[
2276 b"RESTORE",
2277 b"b",
2278 b"0",
2279 &bytes,
2280 b"FREQ",
2281 b"2",
2282 b"IDLETIME",
2283 b"1"
2284 ]),
2285 "-ERR syntax error\r\n"
2286 );
2287 assert_eq!(
2288 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2289 "-ERR syntax error\r\n"
2290 );
2291 assert_eq!(
2292 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2293 "-ERR syntax error\r\n"
2294 );
2295 }
2296
2297 #[test]
2298 fn copy_checks_its_options_before_it_looks_for_anything() {
2299 let mut f = Fixture::new();
2300 assert_eq!(
2303 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2304 "-ERR DB index is out of range\r\n"
2305 );
2306 assert_eq!(
2307 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2308 "-ERR DB index is out of range\r\n"
2309 );
2310 assert_eq!(
2311 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2312 "-ERR value is not an integer or out of range\r\n"
2313 );
2314 assert_eq!(
2315 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2316 "-ERR syntax error\r\n"
2317 );
2318 assert_eq!(
2319 f.run(&[b"COPY", b"a", b"a"]),
2320 "-ERR source and destination objects are the same\r\n"
2321 );
2322 assert_eq!(
2324 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2325 ":0\r\n"
2326 );
2327 }
2328
2329 #[test]
2330 fn time_is_two_bulk_strings_and_moves() {
2331 let mut f = Fixture::new();
2332 let first = f.run(&[b"TIME"]);
2333 assert!(first.starts_with("*2\r\n$"), "got {first}");
2334 let parts: Vec<&str> = first.split("\r\n").collect();
2335 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2336 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2337 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2338 assert!((0..1_000_000).contains(µs), "got {micros}");
2339 assert_ne!(first, f.run(&[b"TIME"]));
2343 }
2344
2345 #[test]
2346 fn a_keyspace_scan_walks_every_key_once() {
2347 let mut f = Fixture::new();
2348 for i in 0..500 {
2349 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2350 }
2351
2352 let mut seen: Vec<String> = Vec::new();
2353 let mut cursor = "0".to_owned();
2354 let mut calls = 0;
2355 loop {
2356 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2357 seen.extend(keys);
2358 cursor = next;
2359 calls += 1;
2360 assert!(calls < 10_000, "the cursor is not advancing");
2361 if cursor == "0" {
2362 break;
2363 }
2364 }
2365
2366 seen.sort();
2367 seen.dedup();
2368 assert_eq!(seen.len(), 500, "every key once and only once");
2369 assert!(calls > 1, "500 keys came back in one batch");
2372 }
2373
2374 #[test]
2375 fn a_scan_narrows_by_pattern_and_by_type() {
2376 let mut f = Fixture::new();
2377 f.run(&[b"SET", b"str", b"v"]);
2378 f.run(&[b"SADD", b"members", b"a"]);
2379 f.run(&[b"HSET", b"fields", b"f", b"v"]);
2380
2381 let all = |f: &mut Fixture, args: &[&[u8]]| {
2382 let mut out: Vec<String> = Vec::new();
2383 let mut cursor = "0".to_owned();
2384 loop {
2385 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2386 line.extend_from_slice(args);
2387 let (next, keys) = scan_reply(&f.run(&line));
2388 out.extend(keys);
2389 cursor = next;
2390 if cursor == "0" {
2391 break;
2392 }
2393 }
2394 out.sort();
2395 out
2396 };
2397
2398 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2399 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2400 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2401 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2403 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2405 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2406 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2408 }
2409
2410 #[test]
2411 fn a_scan_says_what_is_wrong_with_it() {
2412 let mut f = Fixture::new();
2413 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2414 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2415 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2416 assert_eq!(
2417 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2418 "-ERR syntax error\r\n"
2419 );
2420 assert_eq!(
2421 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2422 "-ERR value is not an integer or out of range\r\n"
2423 );
2424 assert_eq!(
2425 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2426 "-ERR syntax error\r\n"
2427 );
2428 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2433 }
2434
2435 #[test]
2436 fn keys_and_randomkey_look_at_the_whole_database() {
2437 let mut f = Fixture::new();
2438 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2439 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2440
2441 for name in ["one", "two", "three"] {
2442 f.run(&[b"SET", name.as_bytes(), b"v"]);
2443 }
2444 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2445 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2446 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2447
2448 for _ in 0..50 {
2449 let got = f.run(&[b"RANDOMKEY"]);
2450 assert!(
2451 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2452 "got {got}"
2453 );
2454 }
2455 }
2456
2457 #[test]
2458 fn a_walk_does_not_answer_keys_that_have_expired() {
2459 let mut f = Fixture::new();
2460 f.run(&[b"SET", b"alive", b"v"]);
2461 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2462 f.server.advance_clock_ms(2);
2463 assert_eq!(
2464 f.run(&[b"DBSIZE"]),
2465 ":2\r\n",
2466 "nothing has collected it yet"
2467 );
2468
2469 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2470 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2471 assert_eq!(keys, ["alive"]);
2472 for _ in 0..20 {
2473 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2474 }
2475 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2478 }
2479
2480 #[test]
2481 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2482 let mut f = Fixture::new();
2483 f.run(&[b"SET", b"k", b"v"]);
2484 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2485 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2486
2487 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2488 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2489 let ms = int(&f.run(&[b"PTTL", b"k"]));
2490 assert!((99_000..=100_000).contains(&ms), "got {ms}");
2491
2492 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2494 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2495 assert_eq!(at, (at_ms + 500) / 1000);
2496 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2497
2498 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2499 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2500 assert_eq!(
2501 f.run(&[b"PERSIST", b"k"]),
2502 ":0\r\n",
2503 "nothing to take off the second time"
2504 );
2505 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2506 assert_eq!(
2507 f.run(&[b"GET", b"k"]),
2508 "$1\r\nv\r\n",
2509 "and the value went through all of that untouched"
2510 );
2511 }
2512
2513 #[test]
2514 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2515 let mut f = Fixture::new();
2516 f.run(&[b"SET", b"str", b"v"]);
2517 f.run(&[b"SADD", b"set", b"a", b"b"]);
2518 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2519
2520 for key in [b"str".as_slice(), b"set", b"hash"] {
2521 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2522 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2523 }
2524 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2527 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2528 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2529 }
2530
2531 #[test]
2532 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2533 let mut f = Fixture::new();
2534 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2535 f.run(&[b"SET", key, b"v"]);
2536 }
2537 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2541 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2542 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2543 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2544 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2545 assert_eq!(
2546 f.run(&[b"EXPIRE", b"a", b"100"]),
2547 ":0\r\n",
2548 "and the key really went, so there is nothing to put a deadline on"
2549 );
2550 }
2551
2552 #[test]
2553 fn the_four_conditions_decide_whether_the_deadline_moves() {
2554 let mut f = Fixture::new();
2555 f.run(&[b"SET", b"k", b"v"]);
2556
2557 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2558 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2559 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2560 assert_eq!(
2561 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2562 ":1\r\n",
2563 "no deadline reads as infinitely far away, so LT passes where GT fails"
2564 );
2565
2566 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2567 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2568 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2569 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2570 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2571 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2572
2573 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2576 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2577 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2578 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2579 }
2580
2581 #[test]
2582 fn the_conditions_are_a_set_and_not_a_keyword() {
2583 let mut f = Fixture::new();
2584 f.run(&[b"SET", b"k", b"v"]);
2585
2586 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2587 assert_eq!(
2588 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2589 ":0\r\n",
2590 "the same keyword twice means it once, and NX now has a deadline to fail on"
2591 );
2592
2593 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2596 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2597 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2598 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2599 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2600 f.run(&[b"PERSIST", b"k"]);
2601 assert_eq!(
2602 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2603 ":0\r\n",
2604 "where LT on its own would have taken it"
2605 );
2606 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2607 }
2608
2609 #[test]
2610 fn a_key_is_gone_once_its_moment_passes() {
2611 let mut f = Fixture::new();
2612 f.run(&[b"SET", b"k", b"v"]);
2613 f.run(&[b"EXPIRE", b"k", b"100"]);
2614
2615 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2616 f.server.set_clock_ms(at as u64 + 1);
2617 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2618 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2619 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2620 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2621 }
2622
2623 #[test]
2624 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2625 let mut f = Fixture::new();
2626 f.run(&[b"SET", b"k", b"v"]);
2627 for (bad, want) in [
2628 (
2629 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2630 "-ERR value is not an integer or out of range\r\n",
2631 ),
2632 (
2633 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2634 "-ERR Unsupported option MAYBE\r\n",
2635 ),
2636 (
2637 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2638 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2639 ),
2640 (
2641 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2642 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2643 ),
2644 (
2645 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2646 "-ERR GT and LT options at the same time are not compatible\r\n",
2647 ),
2648 (
2651 &[b"EXPIRE", b"k", b"9223372036854775807"],
2652 "-ERR invalid expire time in 'expire' command\r\n",
2653 ),
2654 (
2655 &[b"EXPIREAT", b"k", b"9223372036854775807"],
2656 "-ERR invalid expire time in 'expireat' command\r\n",
2657 ),
2658 (
2659 &[b"PEXPIRE", b"k", b"9223372036854775807"],
2660 "-ERR invalid expire time in 'pexpire' command\r\n",
2661 ),
2662 ] {
2663 assert_eq!(f.run(bad), want, "for {bad:?}");
2664 }
2665 assert_eq!(
2666 f.run(&[b"TTL", b"k"]),
2667 ":-1\r\n",
2668 "and none of those put a deadline on anything"
2669 );
2670
2671 assert_eq!(
2675 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2676 ":1\r\n"
2677 );
2678 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2679 }
2680
2681 #[test]
2682 fn flushing_empties_this_database_or_every_one_of_them() {
2683 let mut f = Fixture::new();
2684 f.run(&[b"SELECT", b"0"]);
2685 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2686 f.run(&[b"SELECT", b"1"]);
2687 f.run(&[b"SET", b"c", b"3"]);
2688 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2689 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2692 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2693 f.run(&[b"SELECT", b"0"]);
2695 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2696 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2697 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2698 f.run(&[b"SELECT", b"1"]);
2699 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2700 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2703 assert_eq!(
2704 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2705 "-ERR syntax error\r\n"
2706 );
2707 }
2708
2709 #[test]
2710 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2711 let mut f = Fixture::new();
2712 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2713 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2714 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2715 assert_eq!(
2718 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2719 "*2\r\n:0\r\n:0\r\n"
2720 );
2721 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2722 assert_eq!(
2723 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2724 "*0\r\n"
2725 );
2726 assert_eq!(
2727 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2728 "-ERR Library not found\r\n"
2729 );
2730
2731 assert_eq!(
2734 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2735 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2736 );
2737 assert_eq!(
2738 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2739 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2740 );
2741 assert_eq!(
2744 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2745 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2746 );
2747 assert_eq!(
2748 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2749 "-ERR Unknown argument bogus\r\n"
2750 );
2751 assert_eq!(
2752 f.run(&[b"SCRIPT", b"EXISTS"]),
2753 "-ERR wrong number of arguments for 'script|exists' command\r\n"
2754 );
2755
2756 assert_eq!(
2759 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2760 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2761 );
2762 assert_eq!(
2763 f.run(&[b"FUNCTION", b"STATS"]),
2764 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2765 );
2766 }
2767
2768 #[test]
2769 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2770 let mut f = Fixture::new();
2771 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2772 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2773 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2774 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2777 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2778 f.run(&[b"SET", b"k", b"hello"]);
2781 assert_eq!(
2782 f.run(&[b"INCR", b"k"]),
2783 "-ERR value is not an integer or out of range\r\n"
2784 );
2785 assert_eq!(
2786 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2787 "-ERR increment would produce NaN or Infinity\r\n"
2788 );
2789 }
2790
2791 #[test]
2796 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2797 let mut f = Fixture::new();
2798 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2799 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2802 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2803 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2804 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2805 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2806 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2807 assert_eq!(
2808 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2809 "*2\r\n:1\r\n:0\r\n",
2810 "a refused increment reports the value it left alone and applied nothing"
2811 );
2812 assert_eq!(
2813 f.run(&[
2814 b"INCREX",
2815 b"n",
2816 b"BYINT",
2817 b"5",
2818 b"UBOUND",
2819 b"3",
2820 b"SATURATE"
2821 ]),
2822 "*2\r\n:3\r\n:2\r\n"
2823 );
2824 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2825 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2826 }
2827
2828 #[test]
2829 fn the_same_answers_come_out_in_resp3_spelling() {
2830 let mut f = Fixture::new();
2831 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2832 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2833 assert_eq!(
2836 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2837 "*2\r\n,1.5\r\n,1.5\r\n"
2838 );
2839 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2840 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2843 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2844 }
2845
2846 #[test]
2847 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2848 let mut f = Fixture::new();
2849 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2850 assert_eq!(flow, Flow::Continue);
2851 assert_eq!(
2852 reply,
2853 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2854 );
2855 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2858 assert_eq!(reply.matches("\r\n").count(), 1);
2859 }
2860
2861 #[test]
2862 fn arity_is_checked_before_the_command_is() {
2863 let mut f = Fixture::new();
2864 assert_eq!(
2865 f.run(&[b"GET"]),
2866 "-ERR wrong number of arguments for 'get' command\r\n"
2867 );
2868 assert_eq!(
2869 f.run(&[b"MSET", b"k"]),
2870 "-ERR wrong number of arguments for 'mset' command\r\n"
2871 );
2872 assert_eq!(
2876 f.run(&[b"PING", b"a", b"b"]),
2877 "-ERR wrong number of arguments for 'ping' command\r\n"
2878 );
2879 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2880 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2881 assert_eq!(
2883 f.run(&[b"DELEX", b"k", b"IFEQ"]),
2884 "-ERR wrong number of arguments for 'delex' command\r\n"
2885 );
2886 }
2887
2888 #[test]
2892 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2893 let mut f = Fixture::new();
2894 let syntax = "-ERR syntax error\r\n";
2895 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2896 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2897 assert_eq!(
2898 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2899 syntax
2900 );
2901 assert_eq!(
2902 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2903 syntax
2904 );
2905 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2906 assert_eq!(
2908 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2909 "+OK\r\n"
2910 );
2911 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2912 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2913 assert_eq!(
2915 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2916 syntax
2917 );
2918 assert_eq!(
2919 f.run(&[b"INCREX", b"n", b"ENX"]),
2920 "-ERR ENX flag requires an expiration\r\n"
2921 );
2922 assert_eq!(
2923 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2924 "-ERR UBOUND is not an integer or out of range\r\n"
2925 );
2926 assert_eq!(
2927 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2928 "-ERR LBOUND can't be greater than UBOUND\r\n"
2929 );
2930 assert_eq!(
2931 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2932 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2933 );
2934 }
2935
2936 #[test]
2940 fn the_expiry_rules_are_redis_own() {
2941 let mut f = Fixture::new();
2942 let bad = "-ERR invalid expire time in 'set' command\r\n";
2943 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2944 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2945 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2946 assert_eq!(
2947 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2948 bad
2949 );
2950 assert_eq!(
2951 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2952 "-ERR value is not an integer or out of range\r\n"
2953 );
2954 assert_eq!(
2955 f.run(&[b"SETEX", b"k", b"0", b"v"]),
2956 "-ERR invalid expire time in 'setex' command\r\n"
2957 );
2958 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2959 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2960 assert_eq!(
2961 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2962 "-ERR syntax error\r\n",
2963 "the option list is still checked before the key is looked up"
2964 );
2965 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2967 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2968 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2969 }
2970
2971 #[test]
2972 fn mset_takes_its_pairs_from_the_read_buffer() {
2973 let mut f = Fixture::new();
2974 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2975 assert_eq!(
2976 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2977 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2978 );
2979 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2980 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2981 assert_eq!(
2982 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2983 "-ERR wrong number of key-value pairs\r\n"
2984 );
2985 assert_eq!(
2986 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2987 "-ERR invalid numkeys value\r\n"
2988 );
2989 assert_eq!(
2990 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2991 "-ERR invalid numkeys value\r\n"
2992 );
2993 }
2994
2995 #[test]
2996 fn lcs_answers_the_length_the_string_and_the_runs() {
2997 let mut f = Fixture::new();
2998 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2999 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
3000 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
3001 assert_eq!(
3002 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
3003 "*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"
3004 );
3005 assert_eq!(
3008 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3009 "$6\r\nmytext\r\n"
3010 );
3011 }
3012
3013 #[test]
3014 fn select_moves_the_connection_and_the_databases_stay_apart() {
3015 let mut f = Fixture::new();
3016 f.run(&[b"SET", b"k", b"zero"]);
3017 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3018 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3019 f.run(&[b"SET", b"k", b"four"]);
3020 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3021 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3022 assert_eq!(
3023 f.run(&[b"SELECT", b"99"]),
3024 "-ERR DB index is out of range\r\n"
3025 );
3026 assert_eq!(
3027 f.run(&[b"SELECT", b"-1"]),
3028 "-ERR DB index is out of range\r\n"
3029 );
3030 assert_eq!(
3031 f.run(&[b"SELECT", b"abc"]),
3032 "-ERR value is not an integer or out of range\r\n"
3033 );
3034 f.run(&[b"SELECT", b"4"]);
3036 f.run(&[b"RESET"]);
3037 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3038 }
3039
3040 #[test]
3041 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3042 let mut f = Fixture::new();
3043 let reply = f.run(&[b"HELLO"]);
3044 assert!(reply.starts_with("*14\r\n"), "{reply}");
3045 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3046 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3047 assert!(
3048 reply.contains(":7\r\n"),
3049 "the connection id is in there: {reply}"
3050 );
3051 assert_eq!(
3052 f.run(&[b"HELLO", b"4"]),
3053 "-NOPROTO unsupported protocol version\r\n"
3054 );
3055 assert_eq!(
3056 f.run(&[b"HELLO", b"abc"]),
3057 "-ERR Protocol version is not an integer or out of range\r\n"
3058 );
3059 assert_eq!(
3060 f.run(&[b"HELLO", b"3", b"SETNAME"]),
3061 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3062 );
3063 assert!(
3064 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3065 .starts_with("%7\r\n")
3066 );
3067 assert_eq!(f.session.name(), b"bob");
3068 f.run(&[b"RESET"]);
3069 assert_eq!(f.session.name(), b"");
3070 }
3071
3072 #[test]
3073 fn command_describes_this_server_in_the_shape_a_driver_reads() {
3074 let mut f = Fixture::new();
3075 let count = format!(":{}\r\n", COMMANDS.len());
3076 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3077 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3078 assert_eq!(
3079 info,
3080 "*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\
3081 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3082 );
3083 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3085 assert_eq!(
3086 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3087 "*1\r\n$8\r\ngetrange\r\n"
3088 );
3089 assert_eq!(
3090 f.run(&[b"COMMAND", b"NOPE"]),
3091 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3092 );
3093 }
3094
3095 #[test]
3099 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3100 let mut f = Fixture::new();
3101 assert_eq!(
3102 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3103 "*1\r\n$1\r\nk\r\n"
3104 );
3105 assert_eq!(
3106 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3107 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3108 );
3109 assert_eq!(
3110 f.run(&[
3111 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3112 ]),
3113 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3114 );
3115 assert_eq!(
3116 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3117 "-ERR The command has no key arguments\r\n"
3118 );
3119 assert_eq!(
3120 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3121 "-ERR Invalid number of arguments specified for command\r\n"
3122 );
3123 }
3124
3125 #[test]
3126 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3127 let mut f = Fixture::new();
3128 assert_eq!(
3129 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3130 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3131 );
3132 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3135 assert!(both.starts_with("*6\r\n"), "{both}");
3136 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3137 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3138 assert_eq!(
3139 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3140 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3141 );
3142 assert_eq!(
3143 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3144 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3145 );
3146 assert_eq!(
3147 f.run(&[b"CONFIG", b"GET"]),
3148 "-ERR wrong number of arguments for 'config|get' command\r\n"
3149 );
3150 assert_eq!(
3154 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3155 "-ERR wrong number of arguments for 'config|set' command\r\n"
3156 );
3157 assert_eq!(
3158 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3159 "-ERR syntax error\r\n"
3160 );
3161 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3162 assert_eq!(
3163 f.run(&[b"CONFIG", b"REWRITE"]),
3164 "-ERR The server is running without a config file\r\n"
3165 );
3166 }
3167
3168 #[test]
3169 fn the_eviction_policy_reads_back_what_was_written_to_it() {
3170 let mut f = Fixture::new();
3171 assert_eq!(
3172 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3173 "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3174 );
3175 assert_eq!(
3176 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3177 "+OK\r\n",
3178 "the name is matched without regard to case, like every other one"
3179 );
3180 assert_eq!(
3181 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3182 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3183 );
3184 assert!(
3186 f.run(&[b"INFO", b"memory"])
3187 .contains("maxmemory_policy:allkeys-lfu"),
3188 "INFO and CONFIG disagree about the policy"
3189 );
3190 assert_eq!(
3194 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3195 "-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"
3196 );
3197 assert_eq!(
3200 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3201 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3202 );
3203 f.run(&[
3204 b"CONFIG",
3205 b"SET",
3206 b"hash-max-listpack-entries",
3207 b"7",
3208 b"maxmemory-policy",
3209 b"nonsense",
3210 ]);
3211 assert_eq!(
3212 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3213 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3214 );
3215 }
3216
3217 #[test]
3218 fn the_three_eviction_numbers_read_back_too() {
3219 let mut f = Fixture::new();
3220 for (name, default, set) in [
3221 ("maxmemory-samples", "5", "12"),
3222 ("lfu-log-factor", "10", "3"),
3223 ("lfu-decay-time", "1", "60"),
3224 ] {
3225 let get = || {
3226 format!(
3227 "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3228 name.len(),
3229 default.len()
3230 )
3231 };
3232 assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3233 assert_eq!(
3234 f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3235 "+OK\r\n"
3236 );
3237 assert_eq!(
3238 f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3239 format!(
3240 "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3241 name.len(),
3242 set.len()
3243 )
3244 );
3245 assert_eq!(
3248 f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3249 format!(
3250 "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3251 )
3252 );
3253 }
3254 }
3255
3256 #[test]
3257 fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3258 let mut f = Fixture::new();
3259 assert_eq!(
3260 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3261 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3262 "no limit is the default"
3263 );
3264 for (typed, bytes) in [
3267 (&b"1024"[..], "1024"),
3268 (b"1k", "1000"),
3269 (b"1kb", "1024"),
3270 (b"1M", "1000000"),
3271 (b"1Mb", "1048576"),
3272 (b"1gb", "1073741824"),
3273 (b"100mb", "104857600"),
3274 ] {
3275 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3276 assert_eq!(
3277 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3278 format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3279 "set {}",
3280 String::from_utf8_lossy(typed)
3281 );
3282 }
3283 assert!(
3284 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3285 "the report agrees with the setting"
3286 );
3287
3288 for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3291 assert_eq!(
3292 f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3293 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3294 "refused {}",
3295 String::from_utf8_lossy(bad)
3296 );
3297 }
3298 assert!(
3299 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3300 "and the refusal left the old one alone"
3301 );
3302 }
3303
3304 #[test]
3305 fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3306 let mut f = Fixture::new();
3307 f.run(&[b"SET", b"here", b"already"]);
3308 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3312 assert_eq!(
3313 f.run(&[b"SET", b"k", b"v"]),
3314 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3315 );
3316 assert_eq!(
3317 f.run(&[b"LPUSH", b"l", b"v"]),
3318 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3319 );
3320 assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3322 assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3323 assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3324
3325 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3327 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3328 }
3329
3330 #[test]
3331 fn an_allkeys_policy_makes_room_instead_of_refusing() {
3332 let mut f = Fixture::new();
3333 let val = vec![b'v'; 256];
3334 for i in 0..24000u32 {
3335 let k = format!("key:{i:08}");
3336 f.run(&[b"SET", k.as_bytes(), &val]);
3337 }
3338 let full = f.server.memory_bytes();
3339 assert!(
3340 full > 3 * 1024 * 1024,
3341 "the arena is several segments: {full}"
3342 );
3343
3344 let limit = full - 2 * 1024 * 1024;
3348 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3349 f.run(&[
3350 b"CONFIG",
3351 b"SET",
3352 b"maxmemory",
3353 limit.to_string().as_bytes(),
3354 ]);
3355
3356 for i in 0..2000u32 {
3360 let k = format!("new:{i:08}");
3361 assert_eq!(
3362 f.run(&[b"SET", k.as_bytes(), &val]),
3363 "+OK\r\n",
3364 "write {i} was refused"
3365 );
3366 f.server.refresh_memory();
3367 if f.server.memory_bytes() <= limit {
3368 break;
3369 }
3370 }
3371 assert!(
3372 f.server.memory_bytes() <= limit,
3373 "it never got under: {} against {limit}",
3374 f.server.memory_bytes()
3375 );
3376 let info = f.run(&[b"INFO", b"stats"]);
3377 assert!(!info.contains("evicted_keys:0"), "{info}");
3378 assert!(
3379 f.run(&[b"DBSIZE"]) != ":0\r\n",
3380 "and it did not empty the database to get there"
3381 );
3382 }
3383
3384 #[test]
3385 fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3386 let mut f = Fixture::new();
3393 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3394 let big = vec![b'v'; 200];
3395
3396 for i in 0..400u32 {
3397 let n = i.to_string();
3398 let n = n.as_bytes();
3399 f.run(&[b"SADD", b"s", n]);
3400 f.run(&[b"SADD", b"s2", &big]);
3401 f.run(&[b"HSET", b"h", n, &big]);
3402 f.run(&[b"RPUSH", b"l", &big]);
3403 f.run(&[b"ZADD", b"z", n, n]);
3404 f.run(&[b"ARSET", b"a", n, &big]);
3405 if i % 7 == 0 {
3406 f.run(&[b"SREM", b"s", n]);
3407 f.run(&[b"HDEL", b"h", n]);
3408 f.run(&[b"LPOP", b"l"]);
3409 f.run(&[b"ZREM", b"z", n]);
3410 f.run(&[b"ARDEL", b"a", n]);
3411 }
3412 if i % 53 == 0 {
3413 f.run(&[b"DEL", b"s2"]);
3416 }
3417 assert_eq!(
3418 f.server.settled_memory(),
3419 f.server.memory_bytes(),
3420 "after round {i}"
3421 );
3422 }
3423
3424 assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3427 assert!(
3428 f.server.memory_bytes() > 512 * 1024,
3429 "{}",
3430 f.server.memory_bytes()
3431 );
3432
3433 f.run(&[b"FLUSHALL"]);
3435 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3436 }
3437
3438 #[test]
3439 fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3440 let mut f = Fixture::new();
3445 for i in 0..200u32 {
3446 let n = i.to_string();
3447 f.run(&[b"SADD", b"s", n.as_bytes()]);
3448 f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3449 }
3450 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3451 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3452
3453 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3454 for i in 200..400u32 {
3455 let n = i.to_string();
3456 f.run(&[b"SADD", b"s", n.as_bytes()]);
3457 }
3458 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3459 assert_eq!(
3460 f.server.settled_memory(),
3461 f.server.memory_bytes(),
3462 "the writes it was not watching are in the number it started from"
3463 );
3464 }
3465
3466 #[test]
3467 fn evicted_keys_and_expired_keys_are_different_numbers() {
3468 let mut f = Fixture::new();
3469 f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3472 f.server.advance_clock_ms(20);
3473 f.run(&[b"GET", b"gone"]);
3474 let info = f.run(&[b"INFO", b"stats"]);
3475 assert!(info.contains("expired_keys:1"), "{info}");
3476 assert!(info.contains("evicted_keys:0"), "{info}");
3477 }
3478
3479 #[test]
3480 fn the_object_subcommands_follow_the_policy() {
3481 let mut f = Fixture::new();
3482 f.run(&[b"SET", b"s", b"v"]);
3483 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3487 assert!(
3488 f.run(&[b"OBJECT", b"FREQ", b"s"])
3489 .starts_with("-ERR An LFU maxmemory policy is not selected"),
3490 );
3491
3492 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3493 assert!(
3494 f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3495 .starts_with("-ERR An LFU maxmemory policy is selected"),
3496 );
3497 assert!(
3502 f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3503 "FREQ should answer under an LFU policy"
3504 );
3505 }
3506
3507 #[test]
3508 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3509 let mut f = Fixture::new();
3510 f.run(&[b"SET", b"s", b"hello"]);
3511 f.run(&[b"SET", b"n", b"123"]);
3512 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3513 f.run(&[b"SADD", b"ss", b"a", b"b"]);
3514 f.run(&[b"HSET", b"h", b"f", b"v"]);
3515 for (key, want) in [
3516 (b"s".as_slice(), "embstr"),
3517 (b"n", "int"),
3518 (b"si", "intset"),
3519 (b"ss", "listpack"),
3520 (b"h", "listpack"),
3521 ] {
3522 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3523 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3524 }
3525
3526 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3529 assert_eq!(
3530 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3531 "$10\r\nlistpackex\r\n"
3532 );
3533
3534 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3535 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3536 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3537 }
3538
3539 #[test]
3540 fn object_answers_nil_for_a_key_that_is_not_there() {
3541 let mut f = Fixture::new();
3542 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3543 assert_eq!(
3544 f.run(&[b"OBJECT", sub, b"nokey"]),
3545 "$-1\r\n",
3546 "a nil and not an error, which is what 8.10.1 does"
3547 );
3548 }
3549 f.run(&[b"SET", b"s", b"v"]);
3552 assert!(
3553 f.run(&[b"OBJECT", b"FREQ", b"s"])
3554 .starts_with("-ERR An LFU maxmemory policy is not"),
3555 );
3556 assert_eq!(
3557 f.run(&[b"OBJECT", b"NOPE", b"s"]),
3558 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3559 );
3560 assert_eq!(
3561 f.run(&[b"OBJECT", b"ENCODING"]),
3562 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3563 );
3564 assert_eq!(
3565 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3566 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3567 );
3568 assert_eq!(
3569 f.run(&[b"OBJECT"]),
3570 "-ERR wrong number of arguments for 'object' command\r\n"
3571 );
3572 }
3573
3574 #[test]
3575 fn config_moves_the_ladder_and_object_encoding_agrees() {
3576 let mut f = Fixture::new();
3577 assert_eq!(
3578 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3579 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3580 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3581 );
3582 assert_eq!(
3585 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3586 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3587 );
3588 assert!(
3589 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3590 .starts_with("*8\r\n")
3591 );
3592 assert!(
3593 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3594 .starts_with("*6\r\n")
3595 );
3596
3597 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3598 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3599
3600 assert_eq!(
3601 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3602 "+OK\r\n",
3603 "written under the old name and read back under the new one"
3604 );
3605 assert_eq!(
3606 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3607 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3608 );
3609 assert_eq!(
3610 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3611 "$8\r\nlistpack\r\n",
3612 "the hash that already exists is left exactly where it was"
3613 );
3614 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3615 assert_eq!(
3616 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3617 "$9\r\nhashtable\r\n",
3618 "and the next one built goes straight to a table"
3619 );
3620
3621 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3623 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3624 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3625 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3626 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3627 assert_eq!(
3628 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3629 "$9\r\nhashtable\r\n"
3630 );
3631 }
3632
3633 #[test]
3634 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3635 let mut f = Fixture::new();
3636 assert_eq!(
3637 f.run(&[
3638 b"CONFIG",
3639 b"SET",
3640 b"hash-max-listpack-entries",
3641 b"7",
3642 b"set-max-listpack-entries",
3643 b"abc"
3644 ]),
3645 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3646 );
3647 assert_eq!(
3648 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3649 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3650 "the pair in front of the bad one did not go in"
3651 );
3652 assert_eq!(
3655 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3656 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3657 );
3658 assert_eq!(
3659 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3660 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3661 );
3662 assert_eq!(
3665 f.run(&[
3666 b"CONFIG",
3667 b"SET",
3668 b"set-max-intset-entries",
3669 b"99999999999999999999"
3670 ]),
3671 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3672 );
3673 assert_eq!(
3674 f.run(&[
3675 b"CONFIG",
3676 b"SET",
3677 b"set-max-intset-entries",
3678 b"9223372036854775807"
3679 ]),
3680 "+OK\r\n"
3681 );
3682 }
3683
3684 #[test]
3685 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3686 let mut f = Fixture::new();
3687 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3688 f.run(&[b"SELECT", b"3"]);
3689 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3690 assert_eq!(
3691 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3692 "$9\r\nhashtable\r\n",
3693 "these are one server wide number in Redis, whatever a Keyspace carries"
3694 );
3695 }
3696
3697 #[test]
3698 fn info_reports_the_numbers_it_can_stand_behind() {
3699 let mut f = Fixture::new();
3700 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3701 let all = f.run(&[b"INFO"]);
3702 assert!(all.contains("redis_version:8.8.0"), "{all}");
3703 assert!(
3704 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3705 "{all}"
3706 );
3707 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3708 assert!(all.contains("role:master"), "{all}");
3709 let clients = f.run(&[b"INFO", b"clients"]);
3711 assert!(clients.contains("connected_clients:0"), "{clients}");
3712 assert!(!clients.contains("redis_version"), "{clients}");
3713 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3714 }
3715
3716 #[test]
3723 fn commandstats_is_asked_for_and_replication_is_not() {
3724 let mut f = Fixture::new();
3725 for arg in ["", "all", "default", "everything"] {
3726 let info = if arg.is_empty() {
3727 f.run(&[b"INFO"])
3728 } else {
3729 f.run(&[b"INFO", arg.as_bytes()])
3730 };
3731 assert!(info.contains("redis_version"), "{arg}: {info}");
3732 assert!(info.contains("used_cpu_user"), "{arg}: {info}");
3733 assert!(info.contains("used_memory"), "{arg}: {info}");
3734 assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
3735 let asked = arg == "all" || arg == "everything";
3736 assert_eq!(
3737 info.contains("rejected_calls"),
3738 asked,
3739 "{arg} should{} carry the command counters: {info}",
3740 if asked { "" } else { " not" }
3741 );
3742 }
3743
3744 let cpu = f.run(&[b"INFO", b"cpu"]);
3745 assert!(cpu.contains("used_cpu_user"), "{cpu}");
3746 assert!(!cpu.contains("used_memory"), "{cpu}");
3747
3748 let stats = f.run(&[b"INFO", b"commandSTATS"]);
3751 assert!(!stats.contains("used_memory"), "{stats}");
3752 assert!(stats.contains("rejected_calls"), "{stats}");
3753
3754 let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
3756 assert!(pair.contains("used_cpu_user"), "{pair}");
3757 assert!(!pair.contains("master_repl_offset"), "{pair}");
3758
3759 let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
3760 assert!(with_all.contains("used_memory"), "{with_all}");
3761 assert!(with_all.contains("master_repl_offset"), "{with_all}");
3762 assert!(with_all.contains("rejected_calls"), "{with_all}");
3763 assert_eq!(
3765 with_all.matches("used_cpu_user_children").count(),
3766 1,
3767 "{with_all}"
3768 );
3769
3770 let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
3771 assert!(with_default.contains("used_memory"), "{with_default}");
3772 assert!(
3773 with_default.contains("master_repl_offset"),
3774 "{with_default}"
3775 );
3776 assert!(!with_default.contains("rejected_calls"), "{with_default}");
3777 assert_eq!(
3778 with_default.matches("used_cpu_user_children").count(),
3779 1,
3780 "{with_default}"
3781 );
3782 }
3783
3784 #[test]
3793 fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
3794 let mut f = Fixture::new();
3795 let info = f.run(&[b"INFO", b"memory"]);
3796 for field in [
3797 "total_system_memory:",
3798 "mem_cgroup_limit:",
3799 "mem_limit:",
3800 "mem_budget:",
3801 ] {
3802 assert!(info.contains(field), "no {field} in {info}");
3803 }
3804
3805 let field = |name: &str| -> u64 {
3806 info.lines()
3807 .find_map(|l| l.strip_prefix(name))
3808 .unwrap_or_else(|| panic!("no {name} in {info}"))
3809 .trim()
3810 .parse()
3811 .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
3812 };
3813 let limit = field("mem_limit:");
3814 assert_eq!(field("mem_budget:"), limit / 4, "{info}");
3815 if limit != 0 {
3818 let host = field("total_system_memory:");
3819 let cgroup = field("mem_cgroup_limit:");
3820 assert!(
3821 limit == host || limit == cgroup,
3822 "the limit came from neither number: {info}"
3823 );
3824 }
3825 }
3826
3827 #[test]
3836 fn a_command_counts_what_it_did_separately_from_what_it_refused() {
3837 let mut f = Fixture::new();
3838 f.run(&[b"SET", b"k", b"v"]);
3839 f.run(&[b"SET", b"k", b"w"]);
3840 f.run(&[b"LPUSH", b"k", b"x"]);
3842 f.run(&[b"LPUSH", b"k"]);
3844
3845 let stats = f.run(&[b"INFO", b"commandstats"]);
3846 assert!(
3847 stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
3848 "{stats}"
3849 );
3850 assert!(
3851 stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
3852 "{stats}"
3853 );
3854 assert!(
3855 !stats.contains("cmdstat_zadd"),
3856 "a command nobody has sent has no row: {stats}"
3857 );
3858 }
3859
3860 #[test]
3864 fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3865 let mut f = Fixture::new();
3866 for i in 0..3_000u32 {
3867 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3868 }
3869 for i in 0..1_000u32 {
3870 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3871 }
3872 assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3873 f.advance(100);
3874 assert_eq!(
3875 f.run(&[b"DBSIZE"]),
3876 ":4000\r\n",
3877 "DBSIZE counts records and nothing has read past the dead ones yet"
3878 );
3879
3880 let mut spent = 0;
3882 for _ in 0..2_000 {
3883 spent += f.server.expire_step(4096);
3884 if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3885 break;
3886 }
3887 }
3888 assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3889 assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3890 for i in 0..1_000u32 {
3891 assert_eq!(
3892 f.run(&[b"GET", format!("k{i}").as_bytes()]),
3893 "$1\r\nv\r\n",
3894 "it took a key that had no deadline"
3895 );
3896 }
3897 }
3898
3899 #[test]
3900 fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3901 let mut f = Fixture::new();
3902 for i in 0..2_000u32 {
3903 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3904 }
3905 assert_eq!(f.server.expire_step(4096), 0);
3906 f.run(&[b"SELECT", b"3"]);
3908 f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3909 f.advance(100);
3910 for _ in 0..64 {
3911 f.server.expire_step(4096);
3912 }
3913 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3914 f.run(&[b"SELECT", b"0"]);
3915 assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3916 assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3917 }
3918
3919 #[test]
3922 fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3923 let mut f = Fixture::new();
3924 for i in 0..500u32 {
3925 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3926 }
3927 f.advance(100);
3928 let at = f.server.striped(0).now_ms();
3929 f.server.set_clock_ms(at);
3930 assert!(f.server.expire_slice(8) > 0, "the first one works");
3934 for _ in 0..1_000 {
3935 assert_eq!(
3936 f.server.expire_slice(8),
3937 0,
3938 "the millisecond has not moved and neither should this"
3939 );
3940 }
3941 assert!(
3942 f.server.striped(0).expires() > 400,
3943 "there is plenty left to take"
3944 );
3945 f.server.set_clock_ms(at + 1);
3946 assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3947 }
3948
3949 #[test]
3952 fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3953 let mut f = Fixture::new();
3954 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3955 assert!(
3956 f.run(&[b"INFO", b"keyspace"])
3957 .contains("db0:keys=3,expires=0"),
3958 "none of them has one yet"
3959 );
3960 f.run(&[b"EXPIRE", b"a", b"1000"]);
3961 f.run(&[b"EXPIRE", b"b", b"1000"]);
3962 let two = f.run(&[b"INFO", b"keyspace"]);
3963 assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3964 f.run(&[b"PERSIST", b"a"]);
3965 f.run(&[b"DEL", b"b"]);
3966 let none = f.run(&[b"INFO", b"keyspace"]);
3967 assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3968
3969 f.run(&[b"SELECT", b"1"]);
3971 f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3972 let both = f.run(&[b"INFO", b"keyspace"]);
3973 assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3974 assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3975 }
3976
3977 #[cfg(unix)]
3978 #[test]
3979 fn info_cpu_reports_processor_time_that_was_really_measured() {
3980 let mut f = Fixture::new();
3981 let cpu = f.run(&[b"INFO", b"cpu"]);
3982 assert!(cpu.contains("# CPU"), "{cpu}");
3983 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3985 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3986 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3987 assert!(!cpu.contains("redis_version"), "{cpu}");
3988
3989 let before = used_cpu_user(&cpu);
3993 let mut n = 0u64;
3994 let mut rounds = 0;
3995 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3996 for i in 0..1_000_000u64 {
3997 n = n.wrapping_add(i.wrapping_mul(i));
3998 }
3999 rounds += 1;
4000 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
4004 }
4005 }
4006
4007 #[cfg(unix)]
4009 fn used_cpu_user(info: &str) -> f64 {
4010 info.lines()
4011 .find_map(|l| l.strip_prefix("used_cpu_user:"))
4012 .expect("no used_cpu_user in the reply")
4013 .trim()
4014 .parse()
4015 .expect("used_cpu_user is not a number")
4016 }
4017
4018 #[test]
4024 fn a_command_that_fails_leaves_nothing_half_written() {
4025 let mut f = Fixture::new();
4026 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4027 assert_eq!(reply, "-ERR offset is out of range\r\n");
4028 assert!(!reply.contains(':'), "no integer went out in front of it");
4029 }
4030
4031 #[test]
4032 fn quit_answers_first_and_closes_after() {
4033 let mut f = Fixture::new();
4034 let (flow, reply) = f.flow(&[b"QUIT"]);
4035 assert_eq!(reply, "+OK\r\n");
4036 assert_eq!(flow, Flow::Close);
4037 }
4038
4039 #[test]
4046 fn shutdown_writes_nothing_and_sets_the_flag() {
4047 let mut f = Fixture::new();
4048 assert!(!f.server.stopping(), "nobody has asked yet");
4049
4050 let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4051 assert_eq!(reply, "");
4052 assert_eq!(flow, Flow::Close);
4053 assert!(f.server.stopping());
4054 }
4055
4056 #[test]
4064 fn shutdown_takes_the_flags_redis_takes() {
4065 for flags in [
4066 &[b"NOSAVE".as_slice()][..],
4067 &[b"SAVE"],
4068 &[b"NOW"],
4069 &[b"FORCE"],
4070 &[b"nosave"],
4071 &[b"NOW", b"NOW"],
4072 &[b"SAVE", b"SAVE"],
4073 &[b"NOSAVE", b"NOW", b"FORCE"],
4074 ] {
4075 let mut f = Fixture::new();
4076 let mut parts = vec![b"SHUTDOWN".as_slice()];
4077 parts.extend_from_slice(flags);
4078 let (flow, reply) = f.flow(&parts);
4079 assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4080 assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4081 assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4082 }
4083
4084 for flags in [
4085 &[b"BOGUS".as_slice()][..],
4086 &[b"SAVE", b"NOSAVE"],
4087 &[b"NOSAVE", b"SAVE"],
4088 &[b"ABORT", b"NOW"],
4089 &[b"NOSAVE", b"ABORT"],
4090 &[b"NOW", b"FORCE", b"ABORT"],
4091 ] {
4092 let mut f = Fixture::new();
4093 let mut parts = vec![b"SHUTDOWN".as_slice()];
4094 parts.extend_from_slice(flags);
4095 assert_eq!(
4096 f.run(&parts),
4097 "-ERR syntax error\r\n",
4098 "SHUTDOWN {flags:?} was accepted"
4099 );
4100 assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4101 }
4102 }
4103
4104 #[test]
4112 fn shutdown_abort_never_has_anything_to_abort() {
4113 let mut f = Fixture::new();
4114 for parts in [
4115 &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4116 &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4117 ] {
4118 assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4119 assert!(!f.server.stopping(), "an abort stopped the server");
4120 }
4121 }
4122
4123 struct Backups {
4131 f: Fixture,
4132 dir: PathBuf,
4133 }
4134
4135 impl Backups {
4136 fn new(name: &str) -> Backups {
4137 let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4138 let _ = std::fs::remove_dir_all(&dir);
4139 std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4140 let mut f = Fixture::new();
4141 f.server.set_dir(dir.clone());
4142 Backups { f, dir }
4143 }
4144
4145 fn run(&mut self, parts: &[&[u8]]) -> String {
4146 self.f.run(parts)
4147 }
4148
4149 fn files(&self) -> Vec<String> {
4151 let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4152 Ok(entries) => entries
4153 .filter_map(|e| e.ok())
4154 .map(|e| e.file_name().to_string_lossy().into_owned())
4155 .collect(),
4156 Err(_) => Vec::new(),
4157 };
4158 names.sort();
4159 names
4160 }
4161
4162 fn read(&self, name: &str) -> Vec<u8> {
4163 std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4164 }
4165 }
4166
4167 impl Drop for Backups {
4168 fn drop(&mut self) {
4169 let _ = std::fs::remove_dir_all(&self.dir);
4170 }
4171 }
4172
4173 #[test]
4176 fn backup_walks_the_states_the_reference_walks() {
4177 let mut b = Backups::new("states");
4178 let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4179
4180 assert!(status(&mut b).contains("idle"));
4181 assert!(b.files().is_empty(), "an idle server has written a backup");
4182
4183 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4184 assert!(status(&mut b).contains("incrementing"));
4185 assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4186
4187 assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4188 assert!(status(&mut b).contains("sealed"));
4189 assert_eq!(
4190 b.files(),
4191 [
4192 "appendonly.aof.1.base.rdb",
4193 "appendonly.aof.1.incr.aof",
4194 "appendonly.aof.manifest",
4195 ]
4196 );
4197
4198 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4199 assert!(status(&mut b).contains("idle"));
4200 assert!(b.files().is_empty(), "cleanup left something behind");
4201 }
4202
4203 #[test]
4205 fn backup_refuses_the_moves_the_reference_refuses() {
4206 let mut b = Backups::new("refusals");
4207
4208 assert_eq!(
4209 b.run(&[b"BACKUP", b"SEAL"]),
4210 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4211 );
4212 assert_eq!(
4213 b.run(&[b"BACKUP", b"ABORT"]),
4214 "-ERR No backup in progress\r\n"
4215 );
4216 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4219
4220 b.run(&[b"BACKUP", b"START"]);
4221 assert_eq!(
4222 b.run(&[b"BACKUP", b"START"]),
4223 "-ERR A backup is already in progress, ABORT it first\r\n"
4224 );
4225 assert_eq!(
4226 b.run(&[b"BACKUP", b"CLEANUP"]),
4227 "-ERR Backup is in progress\r\n"
4228 );
4229
4230 b.run(&[b"BACKUP", b"SEAL"]);
4231 assert_eq!(
4232 b.run(&[b"BACKUP", b"START"]),
4233 "-ERR A sealed backup exists, CLEANUP it first\r\n"
4234 );
4235 assert_eq!(
4236 b.run(&[b"BACKUP", b"SEAL"]),
4237 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4238 );
4239 assert_eq!(
4240 b.run(&[b"BACKUP", b"ABORT"]),
4241 "-ERR No backup in progress\r\n"
4242 );
4243 }
4244
4245 #[test]
4252 fn backup_abort_removes_the_file_and_says_who_did_it() {
4253 let mut b = Backups::new("abort");
4254 b.run(&[b"BACKUP", b"START"]);
4255 assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4256
4257 let status = b.run(&[b"BACKUP", b"STATUS"]);
4258 assert!(status.contains("failed"), "{status}");
4259 assert!(status.contains("aborted by user"), "{status}");
4260 assert!(b.files().is_empty(), "abort left the base file behind");
4261 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4262
4263 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4265 assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4266 let status = b.run(&[b"BACKUP", b"STATUS"]);
4267 assert!(status.contains("incrementing"), "{status}");
4268 assert!(!status.contains("aborted"), "the old error was kept");
4269 }
4270
4271 #[test]
4273 fn backup_list_names_the_files_that_are_pinned_so_far() {
4274 let mut b = Backups::new("list");
4275 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4276
4277 b.run(&[b"BACKUP", b"START"]);
4278 let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4279 let base = base.to_string_lossy().into_owned();
4280 assert_eq!(
4281 b.run(&[b"BACKUP", b"LIST"]),
4282 format!("*1\r\n${}\r\n{base}\r\n", base.len())
4283 );
4284
4285 b.run(&[b"BACKUP", b"SEAL"]);
4286 let listed = b.run(&[b"BACKUP", b"LIST"]);
4287 assert!(listed.starts_with("*3\r\n"), "{listed}");
4288 let names: Vec<&str> = listed
4291 .lines()
4292 .filter(|l| l.starts_with('/') || l.contains(":\\"))
4293 .collect();
4294 assert_eq!(names.len(), 3, "{listed}");
4295 assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4296 assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4297 assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4298 }
4299
4300 #[test]
4308 fn a_backup_holds_the_dataset_as_it_was_at_start() {
4309 let mut b = Backups::new("contents");
4310 b.run(&[b"SET", b"bk", b"v1"]);
4311 b.run(&[b"BACKUP", b"START"]);
4312 b.run(&[b"SET", b"bk", b"v2"]);
4313 b.run(&[b"BACKUP", b"SEAL"]);
4314
4315 let base = b.read("appendonly.aof.1.base.rdb");
4316 assert!(base.starts_with(b"REDIS"), "not an RDB file");
4317 assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4318 assert!(
4319 !base.windows(2).any(|w| w == b"v2"),
4320 "the base file moved on after START"
4321 );
4322 let at = base
4327 .windows(8)
4328 .position(|w| w == b"aof-base")
4329 .expect("no aof-base aux field");
4330 assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4331
4332 assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4333 assert_eq!(
4334 String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4335 "file appendonly.aof.1.base.rdb seq 1 type b\n\
4336 file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4337 );
4338 }
4339
4340 #[test]
4343 fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4344 let mut b = Backups::new("status");
4345 b.f.server.set_clock_ms(1_700_000_000_000);
4346
4347 assert_eq!(
4348 b.run(&[b"BACKUP", b"STATUS"]),
4349 "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4350 $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4351 );
4352
4353 b.f.out = Out::new(Proto::Resp3);
4354 b.run(&[b"BACKUP", b"START"]);
4355 assert_eq!(
4356 b.run(&[b"BACKUP", b"STATUS"]),
4357 "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4358 $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4359 );
4360
4361 b.run(&[b"BACKUP", b"SEAL"]);
4362 let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4363 assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4364 }
4365
4366 #[test]
4369 fn a_sealed_backup_is_swept_away_after_the_timeout() {
4370 let mut b = Backups::new("ttl");
4371 b.f.server.set_clock_ms(1_000_000);
4372 assert_eq!(
4373 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4374 "+OK\r\n"
4375 );
4376 b.run(&[b"BACKUP", b"START"]);
4377 b.run(&[b"BACKUP", b"SEAL"]);
4378
4379 b.f.server.set_clock_ms(1_000_000 + 59_000);
4381 b.f.server.backup_expire();
4382 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4383 assert_eq!(b.files().len(), 3);
4384
4385 b.f.server.set_clock_ms(1_000_000 + 60_000);
4386 b.f.server.backup_expire();
4387 let status = b.run(&[b"BACKUP", b"STATUS"]);
4388 assert!(status.contains("idle"), "{status}");
4389 assert!(b.files().is_empty(), "the timeout left the files behind");
4390
4391 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4393 b.run(&[b"BACKUP", b"START"]);
4394 b.run(&[b"BACKUP", b"SEAL"]);
4395 b.f.server.set_clock_ms(9_000_000_000);
4396 b.f.server.backup_expire();
4397 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4398 }
4399
4400 #[test]
4403 fn the_backup_settings_behave_the_way_the_reference_does() {
4404 let mut b = Backups::new("config");
4405 let dir = b.dir.to_string_lossy().into_owned();
4406
4407 assert_eq!(
4408 b.run(&[b"CONFIG", b"GET", b"dir"]),
4409 format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4410 );
4411 assert_eq!(
4412 b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4413 "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4414 );
4415 assert_eq!(
4416 b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4417 "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4418 );
4419
4420 assert_eq!(
4423 b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4424 "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4425 );
4426 assert_eq!(
4427 b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4428 "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4429 );
4430 assert!(
4431 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4432 .contains("argument couldn't be parsed into an integer")
4433 );
4434 assert!(
4435 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4436 .contains("argument must be between 0 and 9223372036854775807 inclusive")
4437 );
4438 }
4439
4440 #[test]
4442 fn backup_help_is_the_text_the_reference_sends() {
4443 let mut f = Fixture::new();
4444 let help = f.run(&[b"BACKUP", b"HELP"]);
4445 assert!(help.starts_with("*17\r\n"), "{help}");
4446 assert!(
4447 help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4448 );
4449 assert!(help.contains("+ Start a new backup into the configured 'backupdirname'.\r\n"));
4450 assert!(help.contains("+ Freeze the current backup (BASE + INCR + manifest).\r\n"));
4451 assert!(help.contains("+ Return this help.\r\n+HELP\r\n+ Print this help.\r\n"));
4452 }
4453
4454 #[test]
4462 fn backup_refuses_what_it_cannot_read() {
4463 let mut f = Fixture::new();
4464 assert_eq!(
4465 f.run(&[b"BACKUP"]),
4466 "-ERR wrong number of arguments for 'backup' command\r\n"
4467 );
4468 assert_eq!(
4469 f.run(&[b"BACKUP", b"START", b"x"]),
4470 "-ERR wrong number of arguments for 'backup' command\r\n"
4471 );
4472 assert_eq!(
4473 f.run(&[b"BACKUP", b"NOPE"]),
4474 "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4475 );
4476 }
4477
4478 #[test]
4479 fn the_command_counter_counts_every_command_including_the_bad_ones() {
4480 let mut f = Fixture::new();
4481 f.run(&[b"PING"]);
4482 f.run(&[b"NOPE"]);
4483 f.run(&[b"GET"]);
4484 assert_eq!(f.server.stats.commands, 3);
4485 }
4486
4487 #[test]
4488 fn a_set_goes_from_bytes_to_bytes() {
4489 let mut f = Fixture::new();
4490 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4491 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4492 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4493 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4494 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4495 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4496 assert_eq!(
4497 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4498 "*3\r\n:1\r\n:0\r\n:1\r\n"
4499 );
4500 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4501 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4502 }
4503
4504 #[test]
4505 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4506 let mut f = Fixture::new();
4507 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4508 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4509 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4510 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4511 assert_eq!(
4512 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4513 "*2\r\n:0\r\n:0\r\n"
4514 );
4515 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4516 }
4517
4518 #[test]
4519 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
4520 let mut f = Fixture::new();
4524 f.run(&[b"SADD", b"s", b"one"]);
4525 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
4526
4527 f.run(&[b"HELLO", b"3"]);
4528 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
4529 }
4530
4531 #[test]
4532 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
4533 let mut f = Fixture::new();
4536 f.run(&[b"SADD", b"s", b"42"]);
4537 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
4538 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
4539 assert_eq!(
4540 f.run(&[b"SISMEMBER", b"s", b"042"]),
4541 ":0\r\n",
4542 "the member is the bytes and not the number they parse to"
4543 );
4544 }
4545
4546 #[test]
4547 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
4548 let mut f = Fixture::new();
4549 f.run(&[b"SET", b"str", b"v"]);
4550 f.run(&[b"SADD", b"set", b"a"]);
4551
4552 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4553 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
4554 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
4555 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
4556 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
4557 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
4558 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
4559 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
4560 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
4561
4562 assert_eq!(
4565 f.run(&[b"MGET", b"str", b"set", b"nope"]),
4566 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
4567 );
4568 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
4570 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
4571 }
4572
4573 #[test]
4574 fn a_wrongtype_leaves_nothing_half_written() {
4575 let mut f = Fixture::new();
4579 f.run(&[b"SET", b"k", b"v"]);
4580 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
4581 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
4582 assert!(!reply.contains('*'), "an array header went out in front");
4583 }
4584
4585 #[test]
4586 fn emptying_a_set_takes_the_key_with_it() {
4587 let mut f = Fixture::new();
4588 f.run(&[b"SADD", b"s", b"a", b"b"]);
4589 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4590 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
4591 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4592 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
4593 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4594 }
4595
4596 fn split_scan(reply: &str) -> (String, Vec<String>) {
4602 let mut lines = reply.split("\r\n");
4603 assert_eq!(lines.next(), Some("*2"), "got {reply}");
4604 lines.next().expect("the cursor header");
4605 let cursor = lines.next().expect("the cursor").to_owned();
4606 let header = lines.next().expect("the member header");
4607 let n: usize = header[1..].parse().expect("a member count");
4608 let mut members = Vec::with_capacity(n);
4609 for _ in 0..n {
4610 lines.next().expect("a member header");
4611 members.push(lines.next().expect("a member").to_owned());
4612 }
4613 (cursor, members)
4614 }
4615
4616 #[test]
4617 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
4618 let mut f = Fixture::new();
4619 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
4620
4621 let one = f.run(&[b"SPOP", b"s"]);
4622 assert!(
4623 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
4624 "got {one}"
4625 );
4626 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4627
4628 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
4630 assert!(rest.starts_with("*3\r\n"), "got {rest}");
4631 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4632 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
4634 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
4635 }
4636
4637 #[test]
4638 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
4639 let mut f = Fixture::new();
4644 f.run(&[b"HELLO", b"3"]);
4645 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
4646
4647 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
4648 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
4650
4651 f.run(&[b"SADD", b"one", b"z"]);
4655 assert_eq!(
4656 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
4657 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
4658 );
4659 }
4660
4661 #[test]
4662 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
4663 let mut f = Fixture::new();
4664 f.run(&[b"SADD", b"s", b"only"]);
4665 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4666 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4667 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
4668
4669 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
4670 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
4673 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
4674 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
4676 }
4677
4678 #[test]
4679 fn a_pop_count_that_is_not_a_positive_number_says_so() {
4680 let mut f = Fixture::new();
4681 f.run(&[b"SADD", b"s", b"a"]);
4682 let bad = "-ERR value is out of range, must be positive\r\n";
4683 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
4684 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
4685 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
4686 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
4688 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
4689 }
4690
4691 #[test]
4692 fn a_scan_walks_a_set_of_any_size_exactly_once() {
4693 let mut f = Fixture::new();
4694 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
4695 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
4696 .into_iter()
4697 .chain(members.iter().map(Vec::as_slice))
4698 .collect();
4699 f.run(&args);
4700
4701 let mut seen = Vec::new();
4702 let mut cursor = "0".to_owned();
4703 loop {
4704 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
4705 let (next, got) = split_scan(&reply);
4706 seen.extend(got);
4707 cursor = next;
4708 if cursor == "0" {
4709 break;
4710 }
4711 }
4712 seen.sort();
4713 seen.dedup();
4714 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
4715
4716 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
4719 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
4720 assert_eq!(cursor, "0");
4721 assert_eq!(got.len(), 3);
4722 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
4724 }
4725
4726 #[test]
4727 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
4728 let mut f = Fixture::new();
4729 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
4730
4731 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
4732 let mut got = got;
4733 got.sort();
4734 assert_eq!(got, ["aa", "ab"]);
4735
4736 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
4739 let mut got = got;
4740 got.sort();
4741 assert_eq!(got, ["12", "13"]);
4742
4743 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
4744 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
4745 assert_eq!(
4746 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
4747 "-ERR syntax error\r\n"
4748 );
4749 assert_eq!(
4752 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
4753 "-ERR syntax error\r\n"
4754 );
4755 }
4756
4757 #[test]
4758 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
4759 let mut f = Fixture::new();
4760 f.run(&[b"SADD", b"src", b"a", b"b"]);
4761 f.run(&[b"SADD", b"dst", b"c"]);
4762
4763 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
4764 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
4765 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
4766 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
4768 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
4769
4770 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
4773 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
4774 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
4775 }
4776
4777 #[test]
4778 fn moving_checks_the_types_in_the_order_redis_checks_them() {
4779 let mut f = Fixture::new();
4783 f.run(&[b"SET", b"str", b"v"]);
4784 f.run(&[b"SADD", b"set", b"a"]);
4785
4786 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4787 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
4788 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
4789 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
4790 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
4791 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
4792 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
4793 assert_eq!(
4794 f.run(&[b"SISMEMBER", b"set", b"a"]),
4795 ":1\r\n",
4796 "and none of that moved anything"
4797 );
4798 }
4799
4800 #[test]
4801 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4802 let mut f = Fixture::new();
4805 f.run(&[b"SADD", b"s", b"a"]);
4806 for bad in [
4807 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
4808 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
4809 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
4810 ] {
4811 let reply = f.run(bad);
4812 assert!(reply.starts_with("-ERR"), "got {reply}");
4813 assert!(!reply.contains('*'), "an array header went out in front");
4814 }
4815 }
4816
4817 #[test]
4818 fn a_hash_writes_reads_and_deletes_its_fields() {
4819 let mut f = Fixture::new();
4820 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
4821 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
4822 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4823 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
4824 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
4825 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
4826 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
4827 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
4828 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
4829 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
4830
4831 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
4834
4835 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
4836 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
4837 assert_eq!(
4838 f.run(&[b"EXISTS", b"h"]),
4839 ":0\r\n",
4840 "and losing the last field lost the key"
4841 );
4842 }
4843
4844 #[test]
4845 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
4846 let mut f = Fixture::new();
4847 f.run(&[b"HSET", b"h", b"a", b"1"]);
4848 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
4849 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
4850 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
4851 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
4852 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
4853
4854 f.run(&[b"HELLO", b"3"]);
4855 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
4856 assert_eq!(
4857 f.run(&[b"HGETALL", b"nokey"]),
4858 "%0\r\n",
4859 "a missing key is the empty hash and never a nil"
4860 );
4861 assert_eq!(
4862 f.run(&[b"HKEYS", b"h"]),
4863 "*1\r\n$1\r\na\r\n",
4864 "and the two that answer one side stay arrays"
4865 );
4866 }
4867
4868 #[test]
4869 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
4870 let mut f = Fixture::new();
4871 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
4872 assert_eq!(
4873 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
4874 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
4875 "the reply is positional, so b is a nil and not a gap"
4876 );
4877 assert_eq!(
4878 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
4879 "*2\r\n$-1\r\n$-1\r\n",
4880 "and a missing key is all nils rather than an empty array"
4881 );
4882
4883 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
4884 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
4885 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4886 }
4887
4888 #[test]
4889 fn a_hash_counts_up_and_says_so_when_it_cannot() {
4890 let mut f = Fixture::new();
4891 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
4892 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
4893 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
4894 assert_eq!(
4895 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
4896 "$4\r\n10.5\r\n",
4897 "a bulk string and not a double, on both protocols"
4898 );
4899
4900 f.run(&[b"HSET", b"h", b"s", b"words"]);
4901 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
4902 assert!(
4903 bad.starts_with("-ERR hash value is not an integer"),
4904 "{bad}"
4905 );
4906 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
4907 assert!(
4908 bad.starts_with("-ERR value is not an integer"),
4909 "a bad argument is not yet a hash value, {bad}"
4910 );
4911 assert_eq!(
4912 f.run(&[b"HGET", b"h", b"s"]),
4913 "$5\r\nwords\r\n",
4914 "and neither of them wrote anything"
4915 );
4916 }
4917
4918 #[test]
4919 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
4920 let mut f = Fixture::new();
4921 for i in 0..500 {
4922 let field = format!("field-{i}");
4923 let value = format!("value-{i}");
4924 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
4925 }
4926
4927 let mut seen: Vec<String> = Vec::new();
4928 let mut cursor = "0".to_owned();
4929 loop {
4930 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
4931 let (next, items) = scan_reply(&reply);
4932 assert_eq!(items.len() % 2, 0, "a pair went out half written");
4933 for pair in items.chunks(2) {
4934 assert_eq!(
4935 pair[0].strip_prefix("field-"),
4936 pair[1].strip_prefix("value-"),
4937 "a field came back with someone else's value"
4938 );
4939 seen.push(pair[0].clone());
4940 }
4941 cursor = next;
4942 if cursor == "0" {
4943 break;
4944 }
4945 }
4946 seen.sort();
4947 seen.dedup();
4948 assert_eq!(seen.len(), 500, "every field once and only once");
4949
4950 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
4951 assert!(
4952 items.iter().all(|s| s.starts_with("field-")),
4953 "NOVALUES still sent the values"
4954 );
4955
4956 let (_, one) = scan_reply(&f.run(&[
4957 b"HSCAN",
4958 b"h",
4959 b"0",
4960 b"MATCH",
4961 b"field-499",
4962 b"COUNT",
4963 b"1000",
4964 ]));
4965 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
4966 }
4967
4968 #[test]
4969 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
4970 let mut f = Fixture::new();
4971 f.run(&[b"HSET", b"h", b"a", b"1"]);
4972 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
4973 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
4974 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
4975 assert_eq!(
4976 f.run(&[b"HRANDFIELD", b"h", b"3"]),
4977 "*1\r\n$1\r\na\r\n",
4978 "a positive count is capped at the size of the hash"
4979 );
4980 assert_eq!(
4981 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
4982 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
4983 "and a negative one repeats itself"
4984 );
4985 assert_eq!(
4986 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4987 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4988 "flat on RESP2"
4989 );
4990
4991 f.run(&[b"HELLO", b"3"]);
4992 assert_eq!(
4993 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4994 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4995 "and nested on RESP3, but still an array and never a map"
4996 );
4997 }
4998
4999 #[test]
5000 fn every_hash_command_says_wrongtype_and_writes_nothing() {
5001 let mut f = Fixture::new();
5002 f.run(&[b"SET", b"str", b"v"]);
5003 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5004
5005 for cmd in [
5006 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
5007 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
5008 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5009 &[b"HGET".as_slice(), b"str", b"f"][..],
5010 &[b"HMGET".as_slice(), b"str", b"f"][..],
5011 &[b"HDEL".as_slice(), b"str", b"f"][..],
5012 &[b"HLEN".as_slice(), b"str"][..],
5013 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5014 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5015 &[b"HGETALL".as_slice(), b"str"][..],
5016 &[b"HKEYS".as_slice(), b"str"][..],
5017 &[b"HVALS".as_slice(), b"str"][..],
5018 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5019 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5020 &[b"HRANDFIELD".as_slice(), b"str"][..],
5021 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5022 &[b"HSCAN".as_slice(), b"str", b"0"][..],
5023 ] {
5024 let reply = f.run(cmd);
5025 assert_eq!(reply, wrong, "{:?}", cmd[0]);
5026 }
5027 assert_eq!(
5028 f.run(&[b"GET", b"str"]),
5029 "$1\r\nv\r\n",
5030 "and none of them touched the value"
5031 );
5032 }
5033
5034 #[test]
5035 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5036 let mut f = Fixture::new();
5037 f.run(&[b"HSET", b"h", b"f", b"v"]);
5038 for bad in [
5039 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5040 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5041 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5042 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5043 ] {
5044 let reply = f.run(bad);
5045 assert!(reply.starts_with("-ERR"), "got {reply}");
5046 assert!(!reply.contains('*'), "an array header went out in front");
5047 }
5048 }
5049
5050 #[test]
5051 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5052 let mut f = Fixture::new();
5053 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5054 assert_eq!(
5055 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5056 "*1\r\n:1\r\n"
5057 );
5058 assert_eq!(
5059 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5060 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5061 "one answer per field, and the two sentinels are TTL's own"
5062 );
5063
5064 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5067 assert!((99_000..=100_000).contains(&ms), "got {ms}");
5068 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5069 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5070 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5071 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5072
5073 assert_eq!(
5074 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5075 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5076 "one for the deadline taken off, and it does not say what it was"
5077 );
5078 assert_eq!(
5079 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5080 "*1\r\n:-1\r\n"
5081 );
5082 assert_eq!(
5083 f.run(&[b"HGET", b"h", b"a"]),
5084 "$1\r\n1\r\n",
5085 "and the field is still there with the value it had"
5086 );
5087 }
5088
5089 #[test]
5090 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5091 let mut f = Fixture::new();
5092 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5093 assert_eq!(
5094 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5095 "*1\r\n:2\r\n",
5096 "two, and not one, because nothing was stored"
5097 );
5098 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5099 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5100
5101 assert_eq!(
5102 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5103 "*1\r\n:2\r\n"
5104 );
5105 assert_eq!(
5106 f.run(&[b"EXISTS", b"h"]),
5107 ":0\r\n",
5108 "and the last field going took the key with it"
5109 );
5110
5111 f.run(&[b"HSET", b"h", b"a", b"1"]);
5114 assert_eq!(
5115 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5116 "*1\r\n:2\r\n"
5117 );
5118 }
5119
5120 #[test]
5121 fn a_field_is_gone_once_its_moment_passes() {
5122 let mut f = Fixture::new();
5123 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5124 assert_eq!(
5125 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5126 "*1\r\n:1\r\n"
5127 );
5128 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5129
5130 f.server.advance_clock_ms(60);
5134 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5135 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5136 assert_eq!(
5137 f.run(&[b"HGETALL", b"h"]),
5138 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5139 "and the walks do not hand back a field that has expired"
5140 );
5141 }
5142
5143 #[test]
5144 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5145 let mut f = Fixture::new();
5146 for cmd in [
5147 &[
5148 b"HEXPIRE".as_slice(),
5149 b"nokey",
5150 b"100",
5151 b"FIELDS",
5152 b"2",
5153 b"a",
5154 b"b",
5155 ][..],
5156 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5157 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5158 &[
5159 b"HEXPIRETIME".as_slice(),
5160 b"nokey",
5161 b"FIELDS",
5162 b"2",
5163 b"a",
5164 b"b",
5165 ][..],
5166 &[
5167 b"HPERSIST".as_slice(),
5168 b"nokey",
5169 b"FIELDS",
5170 b"2",
5171 b"a",
5172 b"b",
5173 ][..],
5174 ] {
5175 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5176 }
5177 }
5178
5179 #[test]
5180 fn writing_a_field_clears_the_deadline_that_was_on_it() {
5181 let mut f = Fixture::new();
5182 f.run(&[b"HSET", b"h", b"a", b"1"]);
5183 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5184 f.run(&[b"HSET", b"h", b"a", b"2"]);
5185 assert_eq!(
5186 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5187 "*1\r\n:-1\r\n",
5188 "Redis has done this since 7.4, and it is why HGETEX exists"
5189 );
5190 }
5191
5192 #[test]
5193 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5194 let mut f = Fixture::new();
5195 f.run(&[b"HSET", b"h", b"a", b"1"]);
5196 assert_eq!(
5197 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5198 "*1\r\n:0\r\n",
5199 "XX on a field with no deadline changes nothing"
5200 );
5201 assert_eq!(
5202 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5203 "*1\r\n:1\r\n"
5204 );
5205 assert_eq!(
5206 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5207 "*1\r\n:0\r\n",
5208 "and NX will not move one that is already there"
5209 );
5210 assert_eq!(
5211 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5212 "*1\r\n:0\r\n"
5213 );
5214 assert_eq!(
5215 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5216 "*1\r\n:1\r\n"
5217 );
5218 assert_eq!(
5219 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5220 "*1\r\n:1\r\n"
5221 );
5222 assert_eq!(
5223 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5224 "*1\r\n:50\r\n"
5225 );
5226 }
5227
5228 #[test]
5229 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5230 let mut f = Fixture::new();
5231 f.run(&[b"HSET", b"h", b"a", b"1"]);
5232 for (bad, want) in [
5233 (
5234 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5235 "-ERR invalid expire time, must be >= 0",
5236 ),
5237 (
5238 &[
5239 b"HEXPIRE".as_slice(),
5240 b"h",
5241 b"9999999999999999",
5242 b"FIELDS",
5243 b"1",
5244 b"a",
5245 ][..],
5246 "-ERR invalid expire time in 'hexpire' command",
5247 ),
5248 (
5249 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5250 "-ERR wrong number of arguments for 'hexpire' command",
5251 ),
5252 (
5253 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5254 "-ERR Parameter `numFields` should be greater than 0",
5255 ),
5256 (
5257 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5258 "-ERR wrong number of arguments",
5259 ),
5260 (
5261 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5262 "-ERR wrong number of arguments",
5263 ),
5264 ] {
5265 let reply = f.run(bad);
5266 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5267 assert!(!reply.contains('*'), "an array header went out in front");
5268 }
5269 assert_eq!(
5270 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5271 "*1\r\n:-1\r\n",
5272 "and not one of them put a deadline on anything"
5273 );
5274 }
5275
5276 #[test]
5277 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5278 let mut f = Fixture::new();
5279 f.run(&[b"SET", b"str", b"v"]);
5280 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5281
5282 for cmd in [
5283 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5284 &[
5285 b"HPEXPIRE".as_slice(),
5286 b"str",
5287 b"100",
5288 b"FIELDS",
5289 b"1",
5290 b"f",
5291 ][..],
5292 &[
5293 b"HEXPIREAT".as_slice(),
5294 b"str",
5295 b"9999999999",
5296 b"FIELDS",
5297 b"1",
5298 b"f",
5299 ][..],
5300 &[
5301 b"HPEXPIREAT".as_slice(),
5302 b"str",
5303 b"9999999999999",
5304 b"FIELDS",
5305 b"1",
5306 b"f",
5307 ][..],
5308 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5309 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5310 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5311 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5312 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5313 ] {
5314 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5315 }
5316 assert_eq!(
5317 f.run(&[b"GET", b"str"]),
5318 "$1\r\nv\r\n",
5319 "and none of them touched the value"
5320 );
5321 }
5322
5323 #[test]
5324 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5325 let mut f = Fixture::new();
5326 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5327 assert_eq!(
5328 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5329 "*2\r\n$1\r\n1\r\n$-1\r\n",
5330 "positional, so the field that was not there is a nil in its place"
5331 );
5332 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5333 assert_eq!(
5334 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5335 "*1\r\n$-1\r\n"
5336 );
5337 assert_eq!(
5338 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5339 "*1\r\n$1\r\n2\r\n"
5340 );
5341 assert_eq!(
5342 f.run(&[b"EXISTS", b"h"]),
5343 ":0\r\n",
5344 "and the last field took the key"
5345 );
5346 }
5347
5348 #[test]
5349 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5350 let mut f = Fixture::new();
5351 f.run(&[b"HSET", b"h", b"a", b"1"]);
5352 assert_eq!(
5353 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5354 "*1\r\n$1\r\n1\r\n"
5355 );
5356 assert_eq!(
5357 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5358 "*1\r\n:-1\r\n",
5359 "no option means leave it alone, which is the one place this is not GETEX"
5360 );
5361
5362 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5363 assert_eq!(
5364 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5365 "*1\r\n:100\r\n"
5366 );
5367 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5368 assert_eq!(
5369 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5370 "*1\r\n:100\r\n",
5371 "and a plain read really does leave it alone"
5372 );
5373 assert_eq!(
5374 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5375 "*1\r\n$1\r\n1\r\n"
5376 );
5377 assert_eq!(
5378 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5379 "*1\r\n:-1\r\n"
5380 );
5381
5382 assert_eq!(
5383 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5384 "*1\r\n$1\r\n1\r\n",
5385 "the value goes out before the deadline that has already gone is applied"
5386 );
5387 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5388 assert_eq!(
5389 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5390 "*1\r\n$-1\r\n"
5391 );
5392 }
5393
5394 #[test]
5395 fn hsetex_writes_all_of_it_or_none_of_it() {
5396 let mut f = Fixture::new();
5397 assert_eq!(
5398 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5399 ":1\r\n"
5400 );
5401 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5402 assert_eq!(
5403 f.run(&[
5404 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5405 ]),
5406 ":0\r\n",
5407 "FNX wants every field named to be missing"
5408 );
5409 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5410 assert_eq!(
5411 f.run(&[b"HEXISTS", b"h", b"new"]),
5412 ":0\r\n",
5413 "and none of the list was written"
5414 );
5415 assert_eq!(
5416 f.run(&[
5417 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5418 ]),
5419 ":0\r\n",
5420 "and FXX wants every one of them to be there"
5421 );
5422 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5423 assert_eq!(
5424 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5425 ":1\r\n"
5426 );
5427 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5428
5429 assert_eq!(
5430 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5431 ":0\r\n"
5432 );
5433 assert_eq!(
5434 f.run(&[b"EXISTS", b"gone"]),
5435 ":0\r\n",
5436 "a key with no fields cannot meet FXX and is not created trying"
5437 );
5438 }
5439
5440 #[test]
5441 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5442 let mut f = Fixture::new();
5443 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5444 assert_eq!(
5445 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5446 "*1\r\n:100\r\n"
5447 );
5448
5449 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5450 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5451 assert_eq!(
5452 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5453 "*1\r\n:100\r\n",
5454 "KEEPTTL put back what the write cleared"
5455 );
5456
5457 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5458 assert_eq!(
5459 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5460 "*1\r\n:-1\r\n",
5461 "and without it a write clears the deadline the way HSET does"
5462 );
5463
5464 assert_eq!(
5467 f.run(&[
5468 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5469 ]),
5470 ":1\r\n"
5471 );
5472 assert_eq!(
5473 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5474 "*1\r\n:100\r\n"
5475 );
5476
5477 assert_eq!(
5478 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5479 ":1\r\n",
5480 "written, and not the separate code the HEXPIRE family has for this"
5481 );
5482 assert_eq!(
5483 f.run(&[b"EXISTS", b"h"]),
5484 ":0\r\n",
5485 "and storing it and then removing it emptied the hash"
5486 );
5487 }
5488
5489 #[test]
5490 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5491 let mut f = Fixture::new();
5492 f.run(&[b"HSET", b"h", b"a", b"1"]);
5493 for (bad, want) in [
5494 (
5496 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5497 "-ERR Number of fields must be a positive integer",
5498 ),
5499 (
5500 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5501 "-ERR The `numfields` parameter must match the number of arguments",
5502 ),
5503 (
5504 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5505 "-ERR Mandatory argument FIELDS is missing or not at the right position",
5506 ),
5507 (
5509 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5510 "-ERR invalid number of fields",
5511 ),
5512 (
5513 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5514 "-ERR wrong number of arguments",
5515 ),
5516 (
5517 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5518 "-ERR unknown argument: FIELD",
5519 ),
5520 (
5521 &[
5522 b"HGETEX".as_slice(),
5523 b"h",
5524 b"KEEPTTL",
5525 b"FIELDS",
5526 b"1",
5527 b"a",
5528 ][..],
5529 "-ERR unknown argument: KEEPTTL",
5530 ),
5531 (
5532 &[
5533 b"HGETEX".as_slice(),
5534 b"h",
5535 b"EX",
5536 b"100",
5537 b"PERSIST",
5538 b"FIELDS",
5539 b"1",
5540 b"a",
5541 ][..],
5542 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
5543 ),
5544 (
5545 &[
5546 b"HSETEX".as_slice(),
5547 b"h",
5548 b"EX",
5549 b"1",
5550 b"KEEPTTL",
5551 b"FIELDS",
5552 b"1",
5553 b"a",
5554 b"1",
5555 ][..],
5556 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
5557 ),
5558 (
5559 &[
5560 b"HSETEX".as_slice(),
5561 b"h",
5562 b"FNX",
5563 b"FXX",
5564 b"FIELDS",
5565 b"1",
5566 b"a",
5567 b"1",
5568 ][..],
5569 "-ERR Only one of FXX or FNX arguments can be specified",
5570 ),
5571 (
5572 &[
5573 b"HSETEX".as_slice(),
5574 b"h",
5575 b"FIELDS",
5576 b"2",
5577 b"a",
5578 b"1",
5579 b"b",
5580 ][..],
5581 "-ERR wrong number of arguments",
5582 ),
5583 (
5584 &[
5585 b"HGETEX".as_slice(),
5586 b"h",
5587 b"EX",
5588 b"-1",
5589 b"FIELDS",
5590 b"1",
5591 b"a",
5592 ][..],
5593 "-ERR invalid expire time, must be >= 0",
5594 ),
5595 (
5596 &[
5597 b"HGETEX".as_slice(),
5598 b"h",
5599 b"PXAT",
5600 b"99999999999999",
5601 b"FIELDS",
5602 b"1",
5603 b"a",
5604 ][..],
5605 "-ERR invalid expire time in 'hgetex' command",
5606 ),
5607 (
5608 &[
5609 b"HSETEX".as_slice(),
5610 b"h",
5611 b"EX",
5612 b"abc",
5613 b"FIELDS",
5614 b"1",
5615 b"a",
5616 b"1",
5617 ][..],
5618 "-ERR value is not an integer or out of range",
5619 ),
5620 ] {
5621 let reply = f.run(bad);
5622 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5623 assert!(!reply.contains('*'), "an array header went out in front");
5624 }
5625 assert_eq!(
5626 f.run(&[b"HGET", b"h", b"a"]),
5627 "$1\r\n1\r\n",
5628 "and not one of them wrote anything"
5629 );
5630 assert_eq!(
5631 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5632 "*1\r\n:-1\r\n"
5633 );
5634 }
5635
5636 #[test]
5637 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
5638 let mut f = Fixture::new();
5639 f.run(&[b"SET", b"str", b"v"]);
5640 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5641 for cmd in [
5642 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5643 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5644 &[
5645 b"HGETEX".as_slice(),
5646 b"str",
5647 b"EX",
5648 b"100",
5649 b"FIELDS",
5650 b"1",
5651 b"f",
5652 ][..],
5653 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
5654 ] {
5655 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5656 }
5657 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5658 }
5659
5660 #[test]
5668 fn himport_writes_declared_values_into_sorted_fields() {
5669 let mut f = Fixture::new();
5670 assert_eq!(
5671 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
5672 "+OK\r\n"
5673 );
5674 assert_eq!(
5675 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
5676 "+OK\r\n"
5677 );
5678 assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
5679 assert_eq!(
5680 f.run(&[b"HGETALL", b"k"]),
5681 bulks(&["a", "3", "b", "1", "aa", "2"])
5682 );
5683 }
5684
5685 #[test]
5688 fn himport_set_replaces_the_whole_key() {
5689 let mut f = Fixture::new();
5690 f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
5691 f.run(&[b"EXPIRE", b"k", b"100"]);
5692 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5693 assert_eq!(
5694 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5695 "+OK\r\n"
5696 );
5697 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5698 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
5699 }
5700
5701 #[test]
5704 fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
5705 let mut f = Fixture::new();
5706 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
5707 f.run(&[b"SELECT", b"1"]);
5708 assert_eq!(
5709 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5710 "+OK\r\n"
5711 );
5712 f.run(&[b"SELECT", b"0"]);
5713 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
5714 assert_eq!(
5715 f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
5716 "-ERR no such fieldset\r\n"
5717 );
5718 }
5719
5720 #[test]
5727 fn himport_complains_in_the_order_a_real_server_does() {
5728 let mut f = Fixture::new();
5729 f.run(&[b"SET", b"str", b"v"]);
5730 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5731 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5732 assert_eq!(
5733 f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
5734 wrong,
5735 "the type beats a missing fieldset"
5736 );
5737 assert_eq!(
5738 f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
5739 wrong,
5740 "and it beats a value count that does not fit"
5741 );
5742 assert_eq!(
5743 f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
5744 "-ERR no such fieldset\r\n"
5745 );
5746 for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
5748 let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
5749 line.extend_from_slice(values);
5750 assert_eq!(
5751 f.run(&line),
5752 "-ERR value count does not match fieldset field count\r\n",
5753 "{} values into two fields",
5754 values.len()
5755 );
5756 }
5757 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5758 }
5759
5760 #[test]
5762 fn himport_checks_each_subcommand_count_under_its_own_name() {
5763 let mut f = Fixture::new();
5764 assert_eq!(
5765 f.run(&[b"HIMPORT"]),
5766 "-ERR wrong number of arguments for 'himport' command\r\n"
5767 );
5768 for (rest, name) in [
5769 (&["PREPARE"][..], "prepare"),
5770 (&["PREPARE", "fs"][..], "prepare"),
5771 (&["SET"][..], "set"),
5772 (&["SET", "k"][..], "set"),
5773 (&["SET", "k", "fs"][..], "set"),
5774 (&["DISCARD"][..], "discard"),
5775 (&["DISCARD", "a", "b"][..], "discard"),
5776 (&["DISCARDALL", "x"][..], "discardall"),
5777 ] {
5778 let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
5779 line.extend(rest.iter().map(|a| a.as_bytes()));
5780 assert_eq!(
5781 f.run(&line),
5782 format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
5783 "HIMPORT {}",
5784 rest.join(" ")
5785 );
5786 }
5787 assert_eq!(
5788 f.run(&[b"HIMPORT", b"NOPE", b"x"]),
5789 "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
5790 );
5791 }
5792
5793 #[test]
5796 fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
5797 let mut f = Fixture::new();
5798 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5799 assert_eq!(
5800 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
5801 "-ERR duplicate field name in fieldset\r\n"
5802 );
5803 assert_eq!(
5804 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5805 "+OK\r\n"
5806 );
5807 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5808 }
5809
5810 #[test]
5813 fn himport_prepare_replaces_and_the_discards_count() {
5814 let mut f = Fixture::new();
5815 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5816 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
5817 assert_eq!(
5818 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5819 "+OK\r\n"
5820 );
5821 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
5822
5823 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
5824 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
5825 f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
5826 f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
5827 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
5828 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
5829 }
5830
5831 fn int(reply: &str) -> i64 {
5837 let body = reply
5838 .strip_prefix(':')
5839 .and_then(|s| s.strip_suffix("\r\n"))
5840 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
5841 body.parse().expect("an integer")
5842 }
5843
5844 fn int_reply(reply: &str) -> i64 {
5845 let body = reply
5846 .strip_prefix("*1\r\n:")
5847 .and_then(|s| s.strip_suffix("\r\n"))
5848 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
5849 body.parse().expect("an integer")
5850 }
5851
5852 fn scan_reply(reply: &str) -> (String, Vec<String>) {
5854 let mut lines = reply.split("\r\n");
5855 assert_eq!(lines.next(), Some("*2"), "got {reply}");
5856 lines.next().expect("the cursor header");
5857 let cursor = lines.next().expect("a cursor").to_owned();
5858 let header = lines.next().expect("an item count");
5859 let n: usize = header[1..].parse().expect("a count");
5860 let mut items = Vec::with_capacity(n);
5861 for _ in 0..n {
5862 lines.next().expect("an item header");
5863 items.push(lines.next().expect("an item").to_owned());
5864 }
5865 (cursor, items)
5866 }
5867
5868 fn sorted(reply: &str) -> Vec<String> {
5871 let mut lines = reply.split("\r\n");
5872 let header = lines.next().expect("a header");
5873 assert!(
5874 header.starts_with('*') || header.starts_with('~'),
5875 "got {reply}"
5876 );
5877 let n: usize = header[1..].parse().expect("a member count");
5878 let mut got = Vec::with_capacity(n);
5879 for _ in 0..n {
5880 lines.next().expect("a member header");
5881 got.push(lines.next().expect("a member").to_owned());
5882 }
5883 got.sort();
5884 got
5885 }
5886
5887 #[test]
5888 fn the_algebra_answers_what_the_sets_share_and_do_not() {
5889 let mut f = Fixture::new();
5890 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5891 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5892 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
5893
5894 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
5895 assert_eq!(
5896 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
5897 ["1", "2", "3", "4", "5"]
5898 );
5899 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
5900 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
5901
5902 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
5905 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
5906 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
5907 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
5908 }
5909
5910 #[test]
5911 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
5912 let mut f = Fixture::new();
5913 f.run(&[b"SADD", b"a", b"x"]);
5914 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
5915 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
5916 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
5917
5918 f.run(&[b"HELLO", b"3"]);
5919 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
5920 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
5921 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
5922 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
5923 }
5924
5925 #[test]
5926 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
5927 let mut f = Fixture::new();
5928 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5929 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5930
5931 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
5932 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
5933 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
5934 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
5935 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
5936 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
5937
5938 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
5941 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5942 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
5943 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
5944
5945 f.run(&[b"SET", b"str", b"v"]);
5948 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
5949 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
5950 }
5951
5952 #[test]
5953 fn sintercard_counts_without_building_and_stops_at_a_limit() {
5954 let mut f = Fixture::new();
5955 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5956 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
5957
5958 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
5959 assert_eq!(
5960 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5961 ":2\r\n"
5962 );
5963 assert_eq!(
5964 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5965 ":3\r\n",
5966 "a limit of zero is no limit"
5967 );
5968 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
5969 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
5970
5971 assert_eq!(
5973 f.run(&[b"SINTERCARD", b"0", b"a"]),
5974 "-ERR numkeys should be greater than 0\r\n"
5975 );
5976 assert_eq!(
5977 f.run(&[b"SINTERCARD", b"abc", b"a"]),
5978 "-ERR numkeys should be greater than 0\r\n"
5979 );
5980 assert_eq!(
5981 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
5982 "-ERR Number of keys can't be greater than number of args\r\n"
5983 );
5984 assert_eq!(
5985 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
5986 "-ERR LIMIT can't be negative\r\n"
5987 );
5988 assert_eq!(
5989 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
5990 "-ERR syntax error\r\n"
5991 );
5992 f.run(&[b"SADD", b"LIMIT", b"2"]);
5994 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
5995 }
5996
5997 #[test]
6000 fn sunioncard_and_sdiffcard_count_without_building() {
6001 let mut f = Fixture::new();
6002 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6003 f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
6004
6005 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
6006 assert_eq!(
6007 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6008 ":2\r\n"
6009 );
6010 assert_eq!(
6011 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6012 ":6\r\n",
6013 "a limit of zero is no limit"
6014 );
6015 assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6016 assert_eq!(
6017 f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6018 ":4\r\n",
6019 "a missing key adds nothing to a union"
6020 );
6021
6022 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6023 assert_eq!(
6024 f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6025 ":1\r\n"
6026 );
6027 assert_eq!(
6028 f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6029 ":2\r\n",
6030 "a difference is not symmetric"
6031 );
6032 assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6033 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6034 assert_eq!(
6035 f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6036 ":0\r\n",
6037 "nothing taken away from nothing"
6038 );
6039
6040 for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6043 assert_eq!(
6044 f.run(&[name, b"0", b"a"]),
6045 "-ERR numkeys should be greater than 0\r\n"
6046 );
6047 assert_eq!(
6048 f.run(&[name, b"abc", b"a"]),
6049 "-ERR numkeys should be greater than 0\r\n"
6050 );
6051 assert_eq!(
6052 f.run(&[name, b"-1", b"a"]),
6053 "-ERR numkeys should be greater than 0\r\n"
6054 );
6055 assert_eq!(
6056 f.run(&[name, b"3", b"a", b"b"]),
6057 "-ERR Number of keys can't be greater than number of args\r\n"
6058 );
6059 assert_eq!(
6060 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6061 "-ERR LIMIT can't be negative\r\n"
6062 );
6063 assert_eq!(
6064 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6065 "-ERR LIMIT can't be negative\r\n",
6066 "a LIMIT that is not a number gets the negative message too"
6067 );
6068 assert_eq!(
6069 f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6070 "-ERR syntax error\r\n"
6071 );
6072 assert_eq!(
6073 f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6074 "-ERR syntax error\r\n"
6075 );
6076 assert_eq!(
6077 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6078 "-ERR syntax error\r\n"
6079 );
6080 }
6081
6082 f.run(&[b"SADD", b"LIMIT", b"2"]);
6084 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6085 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6086 }
6087
6088 #[test]
6089 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6090 let mut f = Fixture::new();
6091 f.run(&[b"SADD", b"a", b"1"]);
6092 f.run(&[b"SADD", b"d", b"old"]);
6093 f.run(&[b"SET", b"str", b"v"]);
6094
6095 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6096 for bad in [
6097 &[b"SINTER".as_slice(), b"a", b"str"][..],
6098 &[b"SUNION".as_slice(), b"str"][..],
6099 &[b"SDIFF".as_slice(), b"a", b"str"][..],
6100 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6101 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6102 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6103 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6104 ] {
6105 let reply = f.run(bad);
6106 assert_eq!(reply, wrong, "for {:?}", bad[0]);
6107 }
6108 assert_eq!(
6109 f.run(&[b"SMEMBERS", b"d"]),
6110 "*1\r\n$3\r\nold\r\n",
6111 "and the destination was left alone every time"
6112 );
6113 }
6114
6115 #[test]
6118 fn churning_sets_does_not_grow_the_server() {
6119 let mut f = Fixture::new();
6120 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6121 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6122 .chain(std::iter::once(&b"s"[..]))
6123 .chain(members.iter().map(Vec::as_slice))
6124 .collect();
6125
6126 f.run(&args);
6127 f.run(&[b"DEL", b"s"]);
6128 f.server.compact_step();
6129 let after_first = f.server.memory_bytes();
6130
6131 for _ in 0..200 {
6132 f.run(&args);
6133 f.run(&[b"DEL", b"s"]);
6134 f.server.compact_step();
6135 }
6136 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6137 assert!(
6138 f.server.memory_bytes() <= after_first * 2,
6139 "held {} after two hundred passes against {after_first} after one",
6140 f.server.memory_bytes()
6141 );
6142 }
6143
6144 #[test]
6152 fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6153 let mut f = Fixture::new();
6154 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6155 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6156 assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6157 assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6158 assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6159 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6160
6161 assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6163 assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6164 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6165
6166 f.run(&[b"SET", b"num", b"12345"]);
6167 assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6168 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6169 assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6170 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6171 assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6172 }
6173
6174 #[test]
6179 fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6180 let mut f = Fixture::new();
6181 f.run(&[b"SET", b"mykey", b"foobar"]);
6182 assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6183 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6184 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6185 assert_eq!(
6186 f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6187 ":6\r\n"
6188 );
6189 assert_eq!(
6190 f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6191 ":25\r\n"
6192 );
6193 assert_eq!(
6194 f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6195 ":17\r\n"
6196 );
6197 assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6198
6199 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6202
6203 assert_eq!(
6205 f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6206 "-ERR syntax error\r\n"
6207 );
6208 assert_eq!(
6209 f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6210 "-ERR syntax error\r\n"
6211 );
6212 }
6213
6214 #[test]
6220 fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6221 let mut f = Fixture::new();
6222 f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6223 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6224 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6225 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6226 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6227 assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6228
6229 f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6230 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6231 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6232 assert_eq!(
6233 f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6234 ":8\r\n"
6235 );
6236
6237 assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6240 assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6241 }
6242
6243 #[test]
6245 fn the_eight_combinations_write_what_a_real_server_writes() {
6246 let mut f = Fixture::new();
6247 f.run(&[b"SET", b"a", b"abc"]);
6248 f.run(&[b"SET", b"b", b"abd"]);
6249 let cases: &[(&[u8], &str)] = &[
6250 (b"AND", "ab`"),
6251 (b"OR", "abg"),
6252 (b"XOR", "\u{0}\u{0}\u{7}"),
6253 (b"DIFF", "\u{0}\u{0}\u{3}"),
6254 (b"DIFF1", "\u{0}\u{0}\u{4}"),
6255 (b"ANDOR", "ab`"),
6256 (b"ONE", "\u{0}\u{0}\u{7}"),
6257 ];
6258 for (op, want) in cases {
6259 assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6260 assert_eq!(
6261 f.run(&[b"GET", b"d"]),
6262 format!("$3\r\n{want}\r\n"),
6263 "{op:?}"
6264 );
6265 }
6266 assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6268 assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6269
6270 assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6273 assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6274
6275 f.run(&[b"SET", b"dest", b"x"]);
6278 assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6279 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6280 }
6281
6282 #[test]
6284 fn bitop_names_the_operation_in_its_own_complaints() {
6285 let mut f = Fixture::new();
6286 f.run(&[b"SET", b"a", b"abc"]);
6287 assert_eq!(
6288 f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6289 "-ERR syntax error\r\n"
6290 );
6291 assert_eq!(
6292 f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6293 "-ERR BITOP NOT must be called with a single source key.\r\n"
6294 );
6295 for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6296 assert_eq!(
6297 f.run(&[b"BITOP", op, b"d", b"a"]),
6298 format!(
6299 "-ERR BITOP {} must be called with at least two source keys.\r\n",
6300 String::from_utf8_lossy(op)
6301 )
6302 );
6303 }
6304 f.run(&[b"LPUSH", b"l", b"x"]);
6305 assert_eq!(
6306 f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6307 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6308 );
6309 }
6310
6311 #[test]
6313 fn bitfield_reads_and_writes_packed_fields() {
6314 let mut f = Fixture::new();
6315 assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6316 assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6317
6318 assert_eq!(
6319 f.run(&[
6320 b"BITFIELD",
6321 b"bf",
6322 b"INCRBY",
6323 b"u2",
6324 b"100",
6325 b"1",
6326 b"GET",
6327 b"u4",
6328 b"0"
6329 ]),
6330 "*2\r\n:1\r\n:0\r\n"
6331 );
6332 assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6335
6336 assert_eq!(
6338 f.run(&[
6339 b"BITFIELD",
6340 b"bf",
6341 b"SET",
6342 b"u8",
6343 b"#0",
6344 b"255",
6345 b"GET",
6346 b"u8",
6347 b"#0"
6348 ]),
6349 "*2\r\n:0\r\n:255\r\n"
6350 );
6351
6352 assert_eq!(
6353 f.run(&[
6354 b"BITFIELD",
6355 b"bf",
6356 b"OVERFLOW",
6357 b"SAT",
6358 b"INCRBY",
6359 b"i8",
6360 b"0",
6361 b"120",
6362 b"INCRBY",
6363 b"i8",
6364 b"0",
6365 b"120"
6366 ]),
6367 "*2\r\n:119\r\n:127\r\n"
6368 );
6369 assert_eq!(
6370 f.run(&[
6371 b"BITFIELD",
6372 b"bf2",
6373 b"OVERFLOW",
6374 b"FAIL",
6375 b"INCRBY",
6376 b"u2",
6377 b"0",
6378 b"5"
6379 ]),
6380 "*1\r\n$-1\r\n"
6381 );
6382 assert_eq!(
6383 f.run(&[
6384 b"BITFIELD",
6385 b"bf3",
6386 b"OVERFLOW",
6387 b"WRAP",
6388 b"INCRBY",
6389 b"u2",
6390 b"0",
6391 b"5"
6392 ]),
6393 "*1\r\n:1\r\n"
6394 );
6395 assert_eq!(
6396 f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6397 "*1\r\n:4611686018427387904\r\n"
6398 );
6399 }
6400
6401 #[test]
6407 fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6408 let mut f = Fixture::new();
6409 let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6410 assert_eq!(
6411 f.run(&[
6412 b"BITFIELD",
6413 b"bad",
6414 b"SET",
6415 b"u8",
6416 b"0",
6417 b"1",
6418 b"GET",
6419 b"u99",
6420 b"0"
6421 ]),
6422 bad_type
6423 );
6424 assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6425 assert_eq!(
6426 f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6427 bad_type
6428 );
6429 assert_eq!(
6430 f.run(&[b"BITFIELD", b"bad", b"GET"]),
6431 "-ERR syntax error\r\n"
6432 );
6433 assert_eq!(
6434 f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6435 "-ERR syntax error\r\n"
6436 );
6437 assert_eq!(
6438 f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6439 "-ERR syntax error\r\n"
6440 );
6441 assert_eq!(
6442 f.run(&[
6443 b"BITFIELD",
6444 b"bad",
6445 b"OVERFLOW",
6446 b"NOPE",
6447 b"GET",
6448 b"u8",
6449 b"0"
6450 ]),
6451 "-ERR Invalid OVERFLOW type specified\r\n"
6452 );
6453 assert_eq!(
6454 f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6455 "-ERR value is not an integer or out of range\r\n"
6456 );
6457 for at in [&b"#-1"[..], b"abc"] {
6458 assert_eq!(
6459 f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6460 "-ERR bit offset is not an integer or out of range\r\n"
6461 );
6462 }
6463 }
6464
6465 #[test]
6467 fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6468 let mut f = Fixture::new();
6469 f.run(&[b"SET", b"n", b"123"]);
6470 assert_eq!(
6471 f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6472 "*1\r\n:49\r\n"
6473 );
6474 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6476
6477 assert_eq!(
6479 f.run(&[
6480 b"BITFIELD_RO",
6481 b"n",
6482 b"OVERFLOW",
6483 b"SAT",
6484 b"GET",
6485 b"u8",
6486 b"0"
6487 ]),
6488 "*1\r\n:49\r\n"
6489 );
6490 for sub in [&b"SET"[..], b"INCRBY"] {
6491 assert_eq!(
6492 f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6493 "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6494 );
6495 }
6496
6497 assert_eq!(
6498 f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6499 "*1\r\n:0\r\n"
6500 );
6501 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6502 }
6503
6504 #[test]
6506 fn an_offset_off_the_end_of_the_world_is_refused() {
6507 let mut f = Fixture::new();
6508 let bad = "-ERR bit offset is not an integer or out of range\r\n";
6509 for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6510 assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6511 assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6512 }
6513 for arg in [&b"2"[..], b"-1"] {
6514 assert_eq!(
6515 f.run(&[b"BITPOS", b"k", arg]),
6516 "-ERR The bit argument must be 1 or 0.\r\n"
6517 );
6518 }
6519 assert_eq!(
6520 f.run(&[b"BITPOS", b"k", b"abc"]),
6521 "-ERR value is not an integer or out of range\r\n"
6522 );
6523 assert_eq!(
6524 f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
6525 "-ERR value is not an integer or out of range\r\n"
6526 );
6527 let bad_bit = "-ERR bit is not an integer or out of range\r\n";
6528 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
6529 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
6530 }
6531
6532 #[test]
6534 fn every_bitmap_command_says_wrongtype() {
6535 let mut f = Fixture::new();
6536 f.run(&[b"LPUSH", b"l", b"x"]);
6537 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6538 let cases: &[&[&[u8]]] = &[
6539 &[b"SETBIT", b"l", b"0", b"1"],
6540 &[b"GETBIT", b"l", b"0"],
6541 &[b"BITCOUNT", b"l"],
6542 &[b"BITPOS", b"l", b"1"],
6543 &[b"BITOP", b"AND", b"d", b"l"],
6544 &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
6545 &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
6546 ];
6547 for case in cases {
6548 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
6549 }
6550 }
6551
6552 #[test]
6555 fn a_sketch_is_added_to_and_counted() {
6556 let mut f = Fixture::new();
6557 assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
6559 assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
6560 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
6561 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
6562 assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
6565 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
6566
6567 assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
6568 assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
6569 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6570 }
6571
6572 #[test]
6573 fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
6574 let mut f = Fixture::new();
6575 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6576 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";
6578 let mut reply = b"$27\r\n".to_vec();
6579 reply.extend_from_slice(want);
6580 reply.extend_from_slice(b"\r\n");
6581 assert_eq!(f.raw(&[b"GET", b"h"]), reply);
6582 }
6583
6584 #[test]
6585 fn counting_several_keys_counts_their_union() {
6586 let mut f = Fixture::new();
6587 f.run(&[b"PFADD", b"a", b"x", b"y"]);
6588 f.run(&[b"PFADD", b"b", b"y", b"z"]);
6589 assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
6590 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
6591 assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
6594 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
6595 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6596 }
6597
6598 #[test]
6599 fn a_merge_keeps_what_the_destination_had() {
6600 let mut f = Fixture::new();
6601 f.run(&[b"PFADD", b"a", b"x", b"y"]);
6602 f.run(&[b"PFADD", b"b", b"z"]);
6603 assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
6604 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
6605 f.run(&[b"PFADD", b"c", b"w"]);
6607 assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
6608 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
6609 assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
6612 assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
6613 }
6614
6615 #[test]
6616 fn the_debug_forms_answer_four_different_shapes() {
6617 let mut f = Fixture::new();
6618 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6619 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
6620 assert_eq!(
6621 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6622 "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
6623 );
6624 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
6625 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
6626 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
6627 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
6628 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6629 assert_eq!(
6631 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6632 "-ERR HLL encoding is not sparse\r\n"
6633 );
6634
6635 let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
6637 assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
6638 assert_eq!(reply.matches(":0\r\n").count(), 16381);
6639 assert_eq!(reply.matches(":1\r\n").count(), 2);
6640 assert_eq!(reply.matches(":2\r\n").count(), 1);
6641
6642 assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
6643 }
6644
6645 #[test]
6646 fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
6647 let mut f = Fixture::new();
6648 f.run(&[b"SET", b"plain", b"not a sketch"]);
6649 let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
6650 assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
6651 assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
6652 assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
6653 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
6654
6655 f.run(&[b"RPUSH", b"l", b"x"]);
6658 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6659 assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
6660 assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
6661 assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
6662 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6663 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
6664 }
6665
6666 #[test]
6667 fn pfdebug_has_its_own_complaints() {
6668 let mut f = Fixture::new();
6669 f.run(&[b"PFADD", b"h", b"a"]);
6670 assert_eq!(
6673 f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
6674 "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
6675 );
6676 let gone = "-ERR The specified key does not exist\r\n";
6678 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
6679 assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
6680 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
6681 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
6682 assert_eq!(
6683 f.run(&[b"PFDEBUG"]),
6684 "-ERR wrong number of arguments for 'pfdebug' command\r\n"
6685 );
6686 assert_eq!(
6687 f.run(&[b"PFSELFTEST", b"x"]),
6688 "-ERR wrong number of arguments for 'pfselftest' command\r\n"
6689 );
6690 }
6691
6692 #[test]
6693 fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
6694 let mut f = Fixture::new();
6695 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6696 let reply = f.raw(&[b"GET", b"h"]);
6699 let short = reply[5..reply.len() - 3].to_vec();
6700 f.run(&[b"SET", b"h", &short]);
6701 assert_eq!(
6702 f.run(&[b"PFCOUNT", b"h"]),
6703 "-INVALIDOBJ Corrupted HLL object detected\r\n"
6704 );
6705 }
6706
6707 #[test]
6708 fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
6709 let mut f = Fixture::new();
6710 f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
6713 for i in 0..10_000u32 {
6714 let ele = format!("e{i}");
6715 f.run(&[b"PFADD", b"big", ele.as_bytes()]);
6716 }
6717 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
6718 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
6719
6720 for key in [&b"small"[..], b"big"] {
6721 let mut copy = key.to_vec();
6722 copy.push(b'2');
6723 let bytes = payload(&f.raw(&[b"DUMP", key]));
6724 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
6725 assert_eq!(f.raw(&[b"GET", ©]), f.raw(&[b"GET", key]));
6728 assert_eq!(
6729 f.run(&[b"PFDEBUG", b"ENCODING", ©]),
6730 f.run(&[b"PFDEBUG", b"ENCODING", key])
6731 );
6732 assert_eq!(f.run(&[b"PFCOUNT", ©]), f.run(&[b"PFCOUNT", key]));
6733 }
6734 assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
6735 assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
6736 }
6737
6738 fn bulk(s: &str) -> String {
6742 format!("${}\r\n{s}\r\n", s.len())
6743 }
6744
6745 fn bulks(parts: &[&str]) -> String {
6748 let mut s = format!("*{}\r\n", parts.len());
6749 for p in parts {
6750 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
6751 }
6752 s
6753 }
6754
6755 #[test]
6756 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
6757 let mut f = Fixture::new();
6758 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
6762 assert_eq!(
6763 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6764 bulks(&["c", "b", "a"])
6765 );
6766 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
6767 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
6768 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
6769 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
6770 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
6771 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
6772 }
6773
6774 #[test]
6775 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
6776 let mut f = Fixture::new();
6777 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
6778 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
6779 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6780 f.run(&[b"RPUSH", b"k", b"a"]);
6781 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
6782 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
6783 assert_eq!(
6784 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6785 bulks(&["z", "a", "y"])
6786 );
6787 }
6788
6789 #[test]
6792 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
6793 let mut f = Fixture::new();
6794 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
6795 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
6796 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
6797 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
6798 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6799 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
6802 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
6803 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
6805 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6806 }
6807
6808 #[test]
6809 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
6810 let mut f = Fixture::new();
6811 f.run(&[b"RPUSH", b"k", b"a"]);
6812 let range = "-ERR value is out of range, must be positive\r\n";
6813 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
6814 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
6815 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
6816 assert_eq!(
6819 f.run(&[b"LPOP", b"k", b"1", b"2"]),
6820 "-ERR wrong number of arguments for 'lpop' command\r\n"
6821 );
6822 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
6823 }
6824
6825 #[test]
6826 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
6827 let mut f = Fixture::new();
6828 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6829 assert_eq!(
6830 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6831 bulks(&["a", "b", "c"])
6832 );
6833 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
6834 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
6835 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
6836 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
6837 assert_eq!(
6838 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
6839 bulks(&["a", "b", "c"])
6840 );
6841 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
6844 assert_eq!(
6845 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
6846 "-ERR value is not an integer or out of range\r\n"
6847 );
6848 }
6849
6850 #[test]
6851 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
6852 let mut f = Fixture::new();
6853 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6854 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
6855 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
6856 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
6857 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
6858 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
6859 assert_eq!(
6860 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6861 bulks(&["a", "b", "z"])
6862 );
6863 assert_eq!(
6866 f.run(&[b"LSET", b"k", b"99", b"z"]),
6867 "-ERR index out of range\r\n"
6868 );
6869 assert_eq!(
6870 f.run(&[b"LSET", b"nope", b"0", b"z"]),
6871 "-ERR no such key\r\n"
6872 );
6873 }
6874
6875 #[test]
6876 fn linsert_says_three_things_with_one_signed_number() {
6877 let mut f = Fixture::new();
6878 assert_eq!(
6881 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
6882 ":0\r\n"
6883 );
6884 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6885 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
6886 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
6887 assert_eq!(
6888 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6889 bulks(&["X", "a", "b", "Y"])
6890 );
6891 assert_eq!(
6892 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
6893 ":-1\r\n"
6894 );
6895 assert_eq!(
6896 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
6897 "-ERR syntax error\r\n"
6898 );
6899 }
6900
6901 #[test]
6902 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
6903 let mut f = Fixture::new();
6904 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
6905 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
6906 assert_eq!(
6907 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6908 bulks(&["b", "c", "a"])
6909 );
6910 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
6911 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6912 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
6913 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
6914 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6915 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
6916 }
6917
6918 #[test]
6919 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
6920 let mut f = Fixture::new();
6921 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
6922 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
6923 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6924 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
6927 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6928 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
6929 }
6930
6931 #[test]
6932 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
6933 let mut f = Fixture::new();
6934 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
6935 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
6936 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
6937 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
6938 assert_eq!(
6939 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
6940 "*2\r\n:0\r\n:3\r\n"
6941 );
6942 assert_eq!(
6943 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
6944 "*3\r\n:6\r\n:3\r\n:0\r\n"
6945 );
6946 assert_eq!(
6949 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
6950 "*1\r\n:0\r\n"
6951 );
6952 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
6955 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
6956 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
6957 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
6958 }
6959
6960 #[test]
6961 fn lpos_words_its_three_mistakes_the_way_redis_does() {
6962 let mut f = Fixture::new();
6963 f.run(&[b"RPUSH", b"p", b"a"]);
6964 assert_eq!(
6967 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
6968 "-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"
6969 );
6970 assert_eq!(
6971 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
6972 "-ERR COUNT can't be negative\r\n"
6973 );
6974 assert_eq!(
6975 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
6976 "-ERR MAXLEN can't be negative\r\n"
6977 );
6978 assert_eq!(
6979 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
6980 "-ERR syntax error\r\n"
6981 );
6982 assert_eq!(
6983 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
6984 "-ERR syntax error\r\n"
6985 );
6986 }
6987
6988 #[test]
6989 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
6990 let mut f = Fixture::new();
6991 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6992 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
6993 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6994 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
6995 assert_eq!(
6996 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
6997 "$1\r\na\r\n"
6998 );
6999 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
7000 f.run(&[b"DEL", b"r"]);
7003 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
7004 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
7005 assert_eq!(
7006 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
7007 bulks(&["3", "1", "2"])
7008 );
7009 assert_eq!(
7010 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7011 "$-1\r\n"
7012 );
7013 assert_eq!(
7014 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7015 "-ERR syntax error\r\n"
7016 );
7017 }
7018
7019 #[test]
7020 fn a_move_checks_the_destination_before_it_takes_anything() {
7021 let mut f = Fixture::new();
7022 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7023 f.run(&[b"SET", b"str", b"v"]);
7024 assert_eq!(
7025 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7026 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7027 );
7028 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7030 }
7031
7032 #[test]
7033 fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7034 for (from, to, order, want) in [
7038 ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7039 ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7040 ("LEFT", "LEFT", "OBO", ["b", "a"]),
7041 ("LEFT", "LEFT", "BULK", ["a", "b"]),
7042 ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7043 ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7044 ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7045 ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7046 ] {
7047 let mut f = Fixture::new();
7048 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7049 let how = format!("{from} {to} {order}");
7050 let reply = f.run(&[
7051 b"LMOVEM",
7052 b"s",
7053 b"d",
7054 from.as_bytes(),
7055 to.as_bytes(),
7056 b"COUNT",
7057 b"2",
7058 order.as_bytes(),
7059 ]);
7060 assert_eq!(reply, bulks(&want), "the reply for {how}");
7061 assert_eq!(
7062 f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7063 bulks(&want),
7064 "the destination for {how}"
7065 );
7066 }
7067 }
7068
7069 #[test]
7070 fn a_block_move_of_one_needs_no_count_at_all() {
7071 let mut f = Fixture::new();
7072 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7073 assert_eq!(
7074 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7075 bulks(&["a"])
7076 );
7077 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7078 assert_eq!(
7081 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7082 "-ERR syntax error\r\n"
7083 );
7084 assert_eq!(
7085 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7086 "-ERR syntax error\r\n"
7087 );
7088 }
7089
7090 #[test]
7091 fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7092 let mut f = Fixture::new();
7093 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7094 assert_eq!(
7098 f.run(&[
7099 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7100 ]),
7101 "*-1\r\n"
7102 );
7103 assert_eq!(
7104 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7105 bulks(&["a", "b", "c"])
7106 );
7107 assert_eq!(
7109 f.run(&[
7110 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7111 ]),
7112 bulks(&["a", "b", "c"])
7113 );
7114 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7115 assert_eq!(
7116 f.run(&[
7117 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7118 ]),
7119 "*-1\r\n"
7120 );
7121 }
7122
7123 #[test]
7124 fn a_block_move_onto_itself_rotates_by_the_count() {
7125 let mut f = Fixture::new();
7126 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7127 assert_eq!(
7128 f.run(&[
7129 b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7130 ]),
7131 bulks(&["a", "b"])
7132 );
7133 assert_eq!(
7134 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7135 bulks(&["c", "a", "b"])
7136 );
7137 }
7138
7139 #[test]
7140 fn a_block_move_reads_the_count_before_the_ordering_word() {
7141 let mut f = Fixture::new();
7142 f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7143 f.run(&[b"SET", b"str", b"v"]);
7144 let count = "-ERR count should be greater than 0\r\n";
7145 assert_eq!(
7146 f.run(&[
7147 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7148 ]),
7149 count
7150 );
7151 assert_eq!(
7152 f.run(&[
7153 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7154 ]),
7155 count
7156 );
7157 assert_eq!(
7158 f.run(&[
7159 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7160 ]),
7161 "-ERR syntax error\r\n"
7162 );
7163 assert_eq!(
7164 f.run(&[
7165 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7166 ]),
7167 "-ERR syntax error\r\n"
7168 );
7169 assert_eq!(
7172 f.run(&[
7173 b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7174 ]),
7175 count
7176 );
7177 assert_eq!(
7178 f.run(&[
7179 b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7180 ]),
7181 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7182 );
7183 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7184 }
7185
7186 #[test]
7187 fn lmpop_answers_from_the_first_key_that_has_anything() {
7188 let mut f = Fixture::new();
7189 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7190 assert_eq!(
7193 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7194 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7195 );
7196 assert_eq!(
7197 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7198 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7199 );
7200 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7201 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7204 }
7205
7206 #[test]
7207 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7208 let mut f = Fixture::new();
7209 f.run(&[b"RPUSH", b"k", b"a"]);
7210 assert_eq!(
7211 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7212 "-ERR numkeys should be greater than 0\r\n"
7213 );
7214 assert_eq!(
7215 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7216 "-ERR numkeys should be greater than 0\r\n"
7217 );
7218 assert_eq!(
7219 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7220 "-ERR count should be greater than 0\r\n"
7221 );
7222 assert_eq!(
7225 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7226 "-ERR syntax error\r\n"
7227 );
7228 assert_eq!(
7229 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7230 "-ERR syntax error\r\n"
7231 );
7232 assert_eq!(
7233 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7234 "-ERR syntax error\r\n"
7235 );
7236 assert_eq!(
7237 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7238 "-ERR syntax error\r\n"
7239 );
7240 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7241 }
7242
7243 #[test]
7244 fn every_list_command_says_wrongtype_and_writes_nothing() {
7245 let mut f = Fixture::new();
7246 f.run(&[b"SET", b"str", b"v"]);
7247 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7248 for cmd in [
7249 &[b"LPUSH".as_slice(), b"str", b"a"][..],
7250 &[b"RPUSH", b"str", b"a"],
7251 &[b"LPUSHX", b"str", b"a"],
7252 &[b"RPUSHX", b"str", b"a"],
7253 &[b"LPOP", b"str"],
7254 &[b"LPOP", b"str", b"2"],
7255 &[b"RPOP", b"str"],
7256 &[b"LLEN", b"str"],
7257 &[b"LRANGE", b"str", b"0", b"-1"],
7258 &[b"LINDEX", b"str", b"0"],
7259 &[b"LSET", b"str", b"0", b"a"],
7260 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7261 &[b"LREM", b"str", b"0", b"a"],
7262 &[b"LTRIM", b"str", b"0", b"-1"],
7263 &[b"LPOS", b"str", b"a"],
7264 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7265 &[b"RPOPLPUSH", b"str", b"d"],
7266 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7267 &[b"LMPOP", b"1", b"str", b"LEFT"],
7268 ] {
7269 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7270 }
7271 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7272 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7273 }
7274
7275 #[test]
7279 fn a_timeout_has_three_ways_of_being_wrong() {
7280 let mut f = Fixture::new();
7281 let not_float = "-ERR timeout is not a float or out of range\r\n";
7282 let range = "-ERR timeout is out of range\r\n";
7283 for (bad, want) in [
7284 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7285 (&[b"BLPOP", b"k", b"nan"], not_float),
7286 (&[b"BLPOP", b"k", b""], not_float),
7287 (&[b"BLPOP", b"k", b" 1"], not_float),
7290 (&[b"BLPOP", b"k", b"1 "], not_float),
7291 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7292 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7293 (&[b"BLPOP", b"k", b"1e400"], range),
7296 (&[b"BLPOP", b"k", b"inf"], range),
7297 (&[b"BLPOP", b"k", b"9999999999999999"], range),
7298 (&[b"BRPOP", b"k", b"abc"], not_float),
7299 (
7300 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7301 not_float,
7302 ),
7303 (
7304 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7305 "-ERR timeout is negative\r\n",
7306 ),
7307 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7308 ] {
7309 assert_eq!(f.run(bad), want, "for {bad:?}");
7310 }
7311 }
7312
7313 #[test]
7316 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7317 let mut f = Fixture::new();
7318 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7319 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7320 assert_eq!(flow, Flow::Block, "for {timeout:?}");
7321 assert!(out.is_empty(), "for {timeout:?}");
7322 }
7323 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7327 assert_eq!(flow, Flow::Block);
7328 assert!(out.is_empty());
7329 }
7330
7331 #[test]
7332 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7333 let mut f = Fixture::new();
7334 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7335
7336 assert_eq!(
7339 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7340 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7341 );
7342 assert_eq!(
7343 f.run(&[b"BRPOP", b"L", b"0"]),
7344 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7345 );
7346 assert_eq!(
7347 f.run(&[
7348 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7349 ]),
7350 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7351 );
7352 assert_eq!(
7353 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7354 "$1\r\nd\r\n"
7355 );
7356 assert_eq!(
7357 f.run(&[b"EXISTS", b"L"]),
7358 ":0\r\n",
7359 "and the key went with it"
7360 );
7361 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7362 f.run(&[b"RPUSH", b"D", b"x"]);
7365 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7366 assert_eq!(
7367 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7368 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7369 );
7370 }
7371
7372 #[test]
7373 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7374 let mut f = Fixture::new();
7375 f.run(&[b"RPUSH", b"k", b"a"]);
7376 for (bad, want) in [
7377 (
7378 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7379 "-ERR numkeys should be greater than 0\r\n",
7380 ),
7381 (
7382 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7383 "-ERR numkeys should be greater than 0\r\n",
7384 ),
7385 (
7388 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7389 "-ERR syntax error\r\n",
7390 ),
7391 (
7392 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7393 "-ERR syntax error\r\n",
7394 ),
7395 (
7396 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7397 "-ERR syntax error\r\n",
7398 ),
7399 (
7400 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7401 "-ERR syntax error\r\n",
7402 ),
7403 (
7406 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7407 "-ERR count should be greater than 0\r\n",
7408 ),
7409 (
7410 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7411 "-ERR count should be greater than 0\r\n",
7412 ),
7413 ] {
7414 assert_eq!(f.run(bad), want, "for {bad:?}");
7415 }
7416 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7417 }
7418
7419 #[test]
7420 fn a_blocking_move_reads_its_directions_before_its_timeout() {
7421 let mut f = Fixture::new();
7422 assert_eq!(
7425 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7426 "-ERR syntax error\r\n"
7427 );
7428 assert_eq!(
7429 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7430 "-ERR syntax error\r\n"
7431 );
7432 }
7433
7434 #[test]
7438 fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7439 let mut f = Fixture::new();
7440 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7441 assert_eq!(
7442 f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7443 (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7444 );
7445 assert_eq!(
7446 f.run(&[
7447 b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7448 ]),
7449 bulks(&["e", "d"])
7450 );
7451 assert_eq!(
7452 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7453 bulks(&["a", "e", "d"])
7454 );
7455 assert_eq!(
7457 f.run(&[
7458 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7459 ]),
7460 bulks(&["b", "c"])
7461 );
7462 assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7463 }
7464
7465 #[test]
7469 fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7470 let mut f = Fixture::new();
7471 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7472 assert_eq!(
7474 f.flow(&[
7475 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7476 ]),
7477 (Flow::Continue, bulks(&["a", "b"]))
7478 );
7479
7480 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7481 assert_eq!(
7484 f.flow(&[
7485 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7486 ])
7487 .0,
7488 Flow::Block
7489 );
7490 assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7491 }
7492
7493 #[test]
7494 fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7495 let mut f = Fixture::new();
7496 let syntax = "-ERR syntax error\r\n";
7497 assert_eq!(
7499 f.run(&[
7500 b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7501 ]),
7502 syntax
7503 );
7504 assert_eq!(
7506 f.run(&[
7507 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7508 ]),
7509 "-ERR timeout is not a float or out of range\r\n"
7510 );
7511 assert_eq!(
7512 f.run(&[
7513 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7514 ]),
7515 "-ERR timeout is negative\r\n"
7516 );
7517 assert_eq!(
7519 f.run(&[
7520 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
7521 ]),
7522 "-ERR count should be greater than 0\r\n"
7523 );
7524 assert_eq!(
7525 f.run(&[
7526 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
7527 ]),
7528 syntax
7529 );
7530 assert_eq!(
7533 f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
7534 syntax
7535 );
7536 assert_eq!(
7537 f.run(&[
7538 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
7539 ]),
7540 syntax
7541 );
7542 }
7543
7544 #[test]
7547 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
7548 let mut f = Fixture::new();
7549 f.run(&[b"SET", b"S", b"v"]);
7550 f.run(&[b"RPUSH", b"D", b"x"]);
7551 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7552
7553 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
7554 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
7557 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
7558 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
7559 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
7560 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
7563 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
7564 assert_eq!(
7565 f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
7566 wrong
7567 );
7568 assert_eq!(
7569 f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
7570 wrong
7571 );
7572
7573 assert_eq!(
7577 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7578 .0,
7579 Flow::Block
7580 );
7581 assert_eq!(
7585 f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7586 .0,
7587 Flow::Block
7588 );
7589 f.run(&[b"RPUSH", b"E", b"1", b"2"]);
7590 assert_eq!(
7591 f.flow(&[
7592 b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
7593 ])
7594 .0,
7595 Flow::Block
7596 );
7597 }
7598
7599 #[test]
7603 fn churning_lists_does_not_grow_the_server() {
7604 let mut f = Fixture::new();
7605 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
7606 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
7607 .into_iter()
7608 .chain(vals.iter().map(Vec::as_slice))
7609 .collect();
7610
7611 f.run(&args);
7612 f.run(&[b"DEL", b"k"]);
7613 f.server.compact_step();
7614 let after_first = f.server.memory_bytes();
7615
7616 for _ in 0..200 {
7617 f.run(&args);
7618 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
7619 f.server.compact_step();
7620 }
7621 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
7622 assert!(
7623 f.server.memory_bytes() <= after_first * 2,
7624 "held {} after two hundred passes against {after_first} after one",
7625 f.server.memory_bytes()
7626 );
7627 }
7628
7629 #[test]
7632 fn a_sorted_set_takes_scores_and_gives_them_back() {
7633 let mut f = Fixture::new();
7634 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
7635 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
7636 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
7637 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
7638 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
7639 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
7640 assert_eq!(
7641 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
7642 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
7643 );
7644 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
7645 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
7646 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
7648 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7649 }
7650
7651 #[test]
7652 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
7653 let mut f = Fixture::new();
7654 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
7655 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
7656 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
7657 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
7658
7659 f.out = Out::new(Proto::Resp3);
7660 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
7661 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
7662 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
7663 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
7664 }
7665
7666 #[test]
7667 fn the_zadd_options_gate_what_gets_written() {
7668 let mut f = Fixture::new();
7669 f.run(&[b"ZADD", b"z", b"5", b"a"]);
7670 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
7672 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
7673 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
7674 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
7675 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
7677 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
7678 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
7679 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
7681 assert_eq!(
7682 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
7683 ":2\r\n"
7684 );
7685 }
7686
7687 #[test]
7688 fn zadd_incr_answers_a_score_or_nothing_at_all() {
7689 let mut f = Fixture::new();
7690 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
7691 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
7692 assert_eq!(
7695 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
7696 "$-1\r\n"
7697 );
7698 assert_eq!(
7699 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
7700 "$-1\r\n"
7701 );
7702 assert_eq!(
7703 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
7704 "$-1\r\n"
7705 );
7706 assert_eq!(
7707 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
7708 "$1\r\n8\r\n"
7709 );
7710 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
7711 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
7712 }
7713
7714 #[test]
7715 fn the_two_infinities_will_not_be_added_together() {
7716 let mut f = Fixture::new();
7717 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
7718 let nan = "-ERR resulting score is not a number (NaN)\r\n";
7719 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
7720 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
7721 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
7722 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
7724 }
7725
7726 #[test]
7727 fn zadd_says_its_mistakes_the_way_redis_says_them() {
7728 let mut f = Fixture::new();
7729 assert_eq!(
7732 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
7733 "-ERR syntax error\r\n"
7734 );
7735 assert_eq!(
7736 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
7737 "-ERR XX and NX options at the same time are not compatible\r\n"
7738 );
7739 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
7740 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
7741 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
7742 assert_eq!(
7743 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
7744 "-ERR INCR option supports a single increment-element pair\r\n"
7745 );
7746 assert_eq!(
7748 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
7749 "-ERR syntax error\r\n"
7750 );
7751 assert_eq!(
7753 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
7754 "-ERR value is not a valid float\r\n"
7755 );
7756 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7757 }
7758
7759 #[test]
7760 fn a_rank_says_where_a_member_sits_from_either_end() {
7761 let mut f = Fixture::new();
7762 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7763 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
7764 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
7765 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
7766 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
7767 assert_eq!(
7769 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
7770 "*2\r\n:1\r\n$1\r\n2\r\n"
7771 );
7772 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
7773 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
7774 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
7775 assert_eq!(
7778 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
7779 "-ERR syntax error\r\n"
7780 );
7781 assert_eq!(
7782 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
7783 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
7784 );
7785 }
7786
7787 #[test]
7788 fn the_two_counts_read_their_two_kinds_of_bound() {
7789 let mut f = Fixture::new();
7790 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7791 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
7792 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
7793 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
7794 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
7795 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
7796 assert_eq!(
7797 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
7798 "-ERR min or max is not a float\r\n"
7799 );
7800
7801 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
7802 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
7803 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
7804 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
7805 assert_eq!(
7808 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
7809 "-ERR min or max not valid string range item\r\n"
7810 );
7811 }
7812
7813 #[test]
7819 fn one_range_command_selects_by_rank_or_score_or_name() {
7820 let mut f = Fixture::new();
7821 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7822 assert_eq!(
7823 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7824 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7825 );
7826 assert_eq!(
7827 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
7828 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7829 );
7830 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
7831 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
7832 assert_eq!(
7835 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
7836 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7837 );
7838 assert_eq!(
7839 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
7840 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7841 );
7842 assert_eq!(
7845 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
7846 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7847 );
7848 assert_eq!(
7849 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
7850 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7851 );
7852 assert_eq!(
7853 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
7854 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7855 );
7856 }
7857
7858 #[test]
7861 fn the_older_range_spellings_name_their_high_end_first() {
7862 let mut f = Fixture::new();
7863 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7864 assert_eq!(
7865 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
7866 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7867 );
7868 assert_eq!(
7869 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
7870 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7871 );
7872 assert_eq!(
7873 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
7874 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7875 );
7876 assert_eq!(
7877 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
7878 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7879 );
7880 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
7884 assert_eq!(
7885 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
7886 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
7887 );
7888 assert_eq!(
7889 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
7890 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
7891 );
7892 for cmd in [
7895 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
7896 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
7897 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
7898 ] {
7899 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
7900 }
7901 }
7902
7903 #[test]
7906 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
7907 let mut f = Fixture::new();
7908 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7909 assert_eq!(
7910 f.run(&[
7911 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
7912 ]),
7913 "*1\r\n$1\r\nb\r\n"
7914 );
7915 assert_eq!(
7917 f.run(&[
7918 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
7919 ]),
7920 "*0\r\n"
7921 );
7922 assert_eq!(
7923 f.run(&[
7924 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
7925 ]),
7926 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7927 );
7928 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";
7930 assert_eq!(
7931 f.run(&[
7932 b"ZRANGEBYSCORE",
7933 b"z",
7934 b"1",
7935 b"3",
7936 b"WITHSCORES",
7937 b"LIMIT",
7938 b"0",
7939 b"2"
7940 ]),
7941 both
7942 );
7943 assert_eq!(
7944 f.run(&[
7945 b"ZRANGEBYSCORE",
7946 b"z",
7947 b"1",
7948 b"3",
7949 b"LIMIT",
7950 b"0",
7951 b"2",
7952 b"WITHSCORES"
7953 ]),
7954 both
7955 );
7956 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
7959 assert_eq!(
7960 f.run(&[
7961 b"ZREVRANGE",
7962 b"z",
7963 b"0",
7964 b"-1",
7965 b"WITHSCORES",
7966 b"LIMIT",
7967 b"0",
7968 b"1"
7969 ]),
7970 needs_by
7971 );
7972 assert_eq!(
7973 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
7974 needs_by
7975 );
7976 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
7977 assert_eq!(
7978 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
7979 not_bylex
7980 );
7981 assert_eq!(
7982 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
7983 not_bylex
7984 );
7985 for cmd in [
7988 &[
7989 b"ZRANGE".as_slice(),
7990 b"z",
7991 b"0",
7992 b"-1",
7993 b"BYSCORE",
7994 b"BYLEX",
7995 ][..],
7996 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
7997 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
7998 ] {
7999 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8000 }
8001 assert_eq!(
8002 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
8003 "-ERR min or max is not a float\r\n"
8004 );
8005 assert_eq!(
8006 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
8007 "-ERR min or max not valid string range item\r\n"
8008 );
8009 assert_eq!(
8010 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8011 "-ERR value is not an integer or out of range\r\n"
8012 );
8013 }
8014
8015 #[test]
8018 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8019 let mut f = Fixture::new();
8020 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8021 assert_eq!(
8022 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8023 "*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"
8024 );
8025 f.out = Out::new(Proto::Resp3);
8026 assert_eq!(
8027 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8028 "*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"
8029 );
8030 assert_eq!(
8031 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8032 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8033 );
8034 }
8035
8036 #[test]
8038 fn a_range_store_writes_the_window_into_another_key() {
8039 let mut f = Fixture::new();
8040 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8041 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8042 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8045 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8046 assert_eq!(
8047 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8048 ":2\r\n"
8049 );
8050 assert_eq!(
8051 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8052 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8053 );
8054 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8057 assert_eq!(
8058 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8059 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8060 );
8061 assert_eq!(
8064 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8065 "-ERR syntax error\r\n"
8066 );
8067 }
8068
8069 #[test]
8072 fn the_three_removals_share_their_window_with_the_reads() {
8073 let mut f = Fixture::new();
8074 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8075 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8076 assert_eq!(
8077 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8078 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8079 );
8080 assert_eq!(
8081 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8082 ":1\r\n"
8083 );
8084 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8085 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8087 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8088 assert_eq!(
8089 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8090 ":0\r\n"
8091 );
8092 assert_eq!(
8093 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8094 "-ERR value is not an integer or out of range\r\n"
8095 );
8096 }
8097
8098 #[test]
8100 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8101 let mut f = Fixture::new();
8102 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8103 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8104 assert_eq!(
8105 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8106 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8107 );
8108 assert_eq!(
8111 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8112 "*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"
8113 );
8114 assert_eq!(
8115 f.run(&[
8116 b"ZUNION",
8117 b"2",
8118 b"z",
8119 b"y",
8120 b"WEIGHTS",
8121 b"2",
8122 b"3",
8123 b"WITHSCORES"
8124 ]),
8125 "*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"
8126 );
8127 assert_eq!(
8128 f.run(&[
8129 b"ZUNION",
8130 b"2",
8131 b"z",
8132 b"y",
8133 b"AGGREGATE",
8134 b"MIN",
8135 b"WITHSCORES"
8136 ]),
8137 "*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"
8138 );
8139 assert_eq!(
8140 f.run(&[
8141 b"ZUNION",
8142 b"2",
8143 b"z",
8144 b"y",
8145 b"AGGREGATE",
8146 b"MAX",
8147 b"WITHSCORES"
8148 ]),
8149 "*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"
8150 );
8151 assert_eq!(
8152 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8153 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8154 );
8155 assert_eq!(
8156 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8157 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8158 );
8159 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8160 f.run(&[b"SADD", b"p", b"a", b"d"]);
8163 assert_eq!(
8164 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8165 "*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"
8166 );
8167 for cmd in [
8170 &[
8171 b"ZDIFF".as_slice(),
8172 b"2",
8173 b"z",
8174 b"y",
8175 b"WEIGHTS",
8176 b"1",
8177 b"1",
8178 ][..],
8179 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8180 ] {
8181 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8182 }
8183 }
8184
8185 #[test]
8187 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8188 let mut f = Fixture::new();
8189 f.run(&[b"ZADD", b"z", b"1", b"a"]);
8190 f.run(&[b"ZADD", b"y", b"2", b"b"]);
8191 assert_eq!(
8193 f.run(&[b"ZUNION", b"0", b"z"]),
8194 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8195 );
8196 assert_eq!(
8197 f.run(&[b"ZUNION", b"-1", b"z"]),
8198 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8199 );
8200 assert_eq!(
8201 f.run(&[b"ZINTERCARD", b"0", b"z"]),
8202 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8203 );
8204 assert_eq!(
8207 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8208 "-ERR syntax error\r\n"
8209 );
8210 assert_eq!(
8211 f.run(&[b"ZUNION", b"x", b"z"]),
8212 "-ERR value is not an integer or out of range\r\n"
8213 );
8214 assert_eq!(
8217 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8218 "-ERR syntax error\r\n"
8219 );
8220 assert_eq!(
8221 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8222 "-ERR weight value is not a float\r\n"
8223 );
8224 assert_eq!(
8225 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8226 "-ERR syntax error\r\n"
8227 );
8228 }
8229
8230 #[test]
8232 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8233 let mut f = Fixture::new();
8234 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8235 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8236 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8237 assert_eq!(
8238 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8239 "*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"
8240 );
8241 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8242 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8243 assert_eq!(
8246 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8247 ":0\r\n"
8248 );
8249 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8250 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8252 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8253 for cmd in [
8254 &[
8255 b"ZUNIONSTORE".as_slice(),
8256 b"d",
8257 b"2",
8258 b"z",
8259 b"y",
8260 b"WITHSCORES",
8261 ][..],
8262 &[
8263 b"ZDIFFSTORE",
8264 b"d",
8265 b"2",
8266 b"z",
8267 b"y",
8268 b"WEIGHTS",
8269 b"1",
8270 b"1",
8271 ],
8272 ] {
8273 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8274 }
8275 }
8276
8277 #[test]
8279 fn intercard_counts_and_stops_at_its_limit() {
8280 let mut f = Fixture::new();
8281 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8282 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8283 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8284 assert_eq!(
8286 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8287 ":2\r\n"
8288 );
8289 assert_eq!(
8290 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8291 ":1\r\n"
8292 );
8293 let bad = "-ERR LIMIT can't be negative\r\n";
8296 assert_eq!(
8297 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8298 bad
8299 );
8300 assert_eq!(
8301 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8302 bad
8303 );
8304 for cmd in [
8305 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8306 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8307 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8308 ] {
8309 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8310 }
8311 }
8312
8313 #[test]
8315 fn a_draw_answers_one_member_or_an_array_of_them() {
8316 let mut f = Fixture::new();
8317 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8318 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8321 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8322 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8323 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8324 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8327 assert!(all.starts_with("*3\r\n"), "{all}");
8328 for m in ["a", "b", "c"] {
8329 assert!(all.contains(m), "{all}");
8330 }
8331 assert!(
8334 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8335 "five draws with replacement"
8336 );
8337 assert!(
8338 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8339 .starts_with("*4\r\n"),
8340 "two pairs, flat on RESP2"
8341 );
8342 f.out = Out::new(Proto::Resp3);
8343 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8344 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8345 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8346 f.out = Out::new(Proto::Resp2);
8347 assert_eq!(
8348 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8349 "-ERR syntax error\r\n"
8350 );
8351 assert_eq!(
8352 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8353 "-ERR value is not an integer or out of range\r\n"
8354 );
8355 }
8356
8357 #[test]
8359 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8360 let mut f = Fixture::new();
8361 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8362 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";
8363 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8364 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8365 assert_eq!(
8366 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8367 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8368 );
8369 assert_eq!(
8370 f.run(&[b"ZSCAN", b"nokey", b"0"]),
8371 "*2\r\n$1\r\n0\r\n*0\r\n"
8372 );
8373 f.out = Out::new(Proto::Resp3);
8376 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8377 f.out = Out::new(Proto::Resp2);
8378 assert_eq!(
8379 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8380 "-ERR NOVALUES option can only be used in HSCAN\r\n"
8381 );
8382 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8383 assert_eq!(
8384 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8385 "-ERR syntax error\r\n"
8386 );
8387 }
8388
8389 #[test]
8391 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8392 let mut f = Fixture::new();
8393 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8394 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8396 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8397 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8398 assert_eq!(
8400 f.run(&[b"ZPOPMIN", b"z", b"2"]),
8401 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8402 );
8403 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8406 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8407 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8408 assert_eq!(
8410 f.run(&[b"ZPOPMIN", b"z", b"9"]),
8411 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8412 );
8413 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8414
8415 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8416 f.out = Out::new(Proto::Resp3);
8417 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8418 assert_eq!(
8419 f.run(&[b"ZPOPMIN", b"z", b"1"]),
8420 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8421 );
8422 f.out = Out::new(Proto::Resp2);
8423 let bad = "-ERR value is out of range, must be positive\r\n";
8426 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8427 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8428 assert_eq!(
8429 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8430 "-ERR syntax error\r\n"
8431 );
8432 }
8433
8434 #[test]
8436 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8437 let mut f = Fixture::new();
8438 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8439 assert_eq!(
8440 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8441 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8442 );
8443 assert_eq!(
8446 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8447 "*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"
8448 );
8449 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8451 f.out = Out::new(Proto::Resp3);
8452 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8453 f.out = Out::new(Proto::Resp2);
8454 let numkeys = "-ERR numkeys should be greater than 0\r\n";
8455 for bad in [
8456 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8457 &[b"ZMPOP", b"-1", b"z", b"MIN"],
8458 &[b"ZMPOP", b"x", b"z", b"MIN"],
8459 ] {
8460 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8461 }
8462 let count = "-ERR count should be greater than 0\r\n";
8463 for bad in [
8464 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8465 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8466 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8467 ] {
8468 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8469 }
8470 let syntax = "-ERR syntax error\r\n";
8471 for bad in [
8472 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8475 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8476 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8477 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8478 ] {
8479 assert_eq!(f.run(bad), syntax, "{bad:?}");
8480 }
8481 }
8482
8483 #[test]
8486 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8487 let mut f = Fixture::new();
8488 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8489 assert_eq!(
8490 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8491 (
8492 Flow::Continue,
8493 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8494 )
8495 );
8496 assert_eq!(
8497 f.run(&[b"BZPOPMAX", b"z", b"0"]),
8498 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8499 );
8500 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8501 assert_eq!(
8502 f.run(&[
8503 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8504 ]),
8505 "*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"
8506 );
8507 f.out = Out::new(Proto::Resp3);
8508 assert_eq!(
8509 f.run(&[b"BZPOPMIN", b"z", b"0"]),
8510 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8511 );
8512 f.out = Out::new(Proto::Resp2);
8513 assert_eq!(
8515 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8516 (Flow::Block, String::new())
8517 );
8518 assert_eq!(
8519 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
8520 (Flow::Block, String::new())
8521 );
8522 assert_eq!(
8525 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
8526 "-ERR timeout is not a float or out of range\r\n"
8527 );
8528 assert_eq!(
8529 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
8530 "-ERR numkeys should be greater than 0\r\n"
8531 );
8532 assert_eq!(
8533 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
8534 "-ERR timeout is negative\r\n"
8535 );
8536 }
8537
8538 #[test]
8542 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
8543 let mut f = Fixture::new();
8544 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
8545 assert_eq!(f.server.waiters().len(), 1);
8546 f.run(&[b"SET", b"z", b"v"]);
8549 let mut out = Out::new(Proto::Resp2);
8550 assert!(!f.server.serve_waiter(0, 0, &mut out));
8551 assert!(out.as_slice().is_empty());
8552 f.run(&[b"DEL", b"z"]);
8553 f.run(&[b"ZADD", b"z", b"5", b"m"]);
8554 assert!(f.server.serve_waiter(0, 0, &mut out));
8555 assert_eq!(
8556 core::str::from_utf8(out.as_slice()).expect("ascii"),
8557 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
8558 );
8559 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8562 }
8563
8564 #[test]
8565 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
8566 let mut f = Fixture::new();
8567 f.run(&[b"SET", b"s", b"v"]);
8568 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8569 for cmd in [
8570 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
8571 &[b"ZINCRBY", b"s", b"1", b"a"],
8572 &[b"ZCARD", b"s"],
8573 &[b"ZSCORE", b"s", b"a"],
8574 &[b"ZMSCORE", b"s", b"a"],
8575 &[b"ZREM", b"s", b"a"],
8576 &[b"ZRANK", b"s", b"a"],
8577 &[b"ZREVRANK", b"s", b"a"],
8578 &[b"ZCOUNT", b"s", b"1", b"2"],
8579 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
8580 &[b"ZRANGE", b"s", b"0", b"-1"],
8581 &[b"ZREVRANGE", b"s", b"0", b"-1"],
8582 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
8583 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
8584 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
8585 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
8586 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
8587 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
8588 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
8589 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
8590 &[b"ZUNION", b"1", b"s"],
8591 &[b"ZINTER", b"1", b"s"],
8592 &[b"ZDIFF", b"1", b"s"],
8593 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
8594 &[b"ZINTERSTORE", b"d", b"1", b"s"],
8595 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
8596 &[b"ZINTERCARD", b"1", b"s"],
8597 &[b"ZRANDMEMBER", b"s"],
8598 &[b"ZSCAN", b"s", b"0"],
8599 &[b"ZPOPMIN", b"s"],
8600 &[b"ZPOPMAX", b"s", b"2"],
8601 &[b"ZMPOP", b"1", b"s", b"MIN"],
8602 &[b"BZPOPMIN", b"s", b"0"],
8603 &[b"BZPOPMAX", b"s", b"0"],
8604 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
8605 ] {
8606 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
8607 }
8608 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
8609 }
8610
8611 #[test]
8615 fn churning_sorted_sets_does_not_grow_the_server() {
8616 let mut f = Fixture::new();
8617 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
8618 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
8619 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
8620 for i in 0..200 {
8621 args.push(&scores[i]);
8622 args.push(&members[i]);
8623 }
8624
8625 f.run(&args);
8626 f.run(&[b"DEL", b"z"]);
8627 f.server.compact_step();
8628 let after_first = f.server.memory_bytes();
8629
8630 for _ in 0..200 {
8631 f.run(&args);
8632 f.run(&[b"DEL", b"z"]);
8633 f.server.compact_step();
8634 }
8635 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8636 assert!(
8637 f.server.memory_bytes() <= after_first * 2,
8638 "held {} after two hundred passes against {after_first} after one",
8639 f.server.memory_bytes()
8640 );
8641 }
8642
8643 fn sicily(f: &mut Fixture) {
8651 f.run(&[
8652 b"GEOADD",
8653 b"Sicily",
8654 b"13.361389",
8655 b"38.115556",
8656 b"Palermo",
8657 b"15.087269",
8658 b"37.502669",
8659 b"Catania",
8660 ]);
8661 f.run(&[
8662 b"GEOADD",
8663 b"Sicily",
8664 b"13.583333",
8665 b"37.316667",
8666 b"Agrigento",
8667 ]);
8668 }
8669
8670 #[test]
8671 fn places_go_in_as_scores_and_come_back_as_positions() {
8672 let mut f = Fixture::new();
8673 assert_eq!(
8674 f.run(&[
8675 b"GEOADD",
8676 b"Sicily",
8677 b"13.361389",
8678 b"38.115556",
8679 b"Palermo",
8680 b"15.087269",
8681 b"37.502669",
8682 b"Catania"
8683 ]),
8684 ":2\r\n"
8685 );
8686 assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
8690 assert_eq!(
8691 f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
8692 "$16\r\n3479099956230698\r\n"
8693 );
8694 assert_eq!(
8695 f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
8696 "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
8697 );
8698 assert_eq!(
8699 f.run(&[
8700 b"GEOHASH",
8701 b"Sicily",
8702 b"Palermo",
8703 b"Catania",
8704 b"NonExisting"
8705 ]),
8706 "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
8707 );
8708 assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
8712 assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
8713 }
8714
8715 #[test]
8716 fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
8717 let mut f = Fixture::new();
8718 sicily(&mut f);
8719 assert_eq!(
8720 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
8721 "$11\r\n166274.1516\r\n"
8722 );
8723 assert_eq!(
8724 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
8725 "$8\r\n166.2742\r\n"
8726 );
8727 assert_eq!(
8728 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
8729 "$8\r\n103.3182\r\n"
8730 );
8731 assert_eq!(
8735 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
8736 "$-1\r\n"
8737 );
8738 assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
8739 assert_eq!(
8740 f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
8741 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
8742 );
8743 assert_eq!(
8744 f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
8745 "-ERR syntax error\r\n"
8746 );
8747 }
8748
8749 #[test]
8750 fn a_search_finds_what_is_inside_it_nearest_first() {
8751 let mut f = Fixture::new();
8752 sicily(&mut f);
8753 let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
8754 assert_eq!(
8755 f.run(&[
8756 b"GEOSEARCH",
8757 b"Sicily",
8758 b"FROMLONLAT",
8759 b"15",
8760 b"37",
8761 b"BYRADIUS",
8762 b"200",
8763 b"km",
8764 b"ASC"
8765 ]),
8766 all
8767 );
8768 assert_eq!(
8771 f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
8772 all
8773 );
8774 assert_eq!(
8775 f.run(&[
8776 b"GEORADIUS_RO",
8777 b"Sicily",
8778 b"15",
8779 b"37",
8780 b"200",
8781 b"km",
8782 b"ASC"
8783 ]),
8784 all
8785 );
8786 assert_eq!(
8789 f.run(&[
8790 b"GEORADIUS",
8791 b"Sicily",
8792 b"15",
8793 b"37",
8794 b"200",
8795 b"km",
8796 b"DESC",
8797 b"COUNT",
8798 b"1"
8799 ]),
8800 "*1\r\n$7\r\nPalermo\r\n"
8801 );
8802 assert_eq!(
8803 f.run(&[
8804 b"GEORADIUS",
8805 b"Sicily",
8806 b"15",
8807 b"37",
8808 b"200",
8809 b"km",
8810 b"COUNT",
8811 b"1"
8812 ]),
8813 "*1\r\n$7\r\nCatania\r\n"
8814 );
8815 let empty = "*0\r\n";
8818 assert_eq!(
8819 f.run(&[
8820 b"GEOSEARCH",
8821 b"Sicily",
8822 b"FROMLONLAT",
8823 b"15",
8824 b"37",
8825 b"BYRADIUS",
8826 b"1",
8827 b"km"
8828 ]),
8829 empty
8830 );
8831 assert_eq!(
8832 f.run(&[
8833 b"GEOSEARCH",
8834 b"nokey",
8835 b"FROMLONLAT",
8836 b"15",
8837 b"37",
8838 b"BYRADIUS",
8839 b"1",
8840 b"km"
8841 ]),
8842 empty
8843 );
8844 assert_eq!(
8845 f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
8846 empty
8847 );
8848 }
8849
8850 #[test]
8851 fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
8852 let mut f = Fixture::new();
8853 sicily(&mut f);
8854 assert_eq!(
8855 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
8856 "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
8857 );
8858 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";
8861 assert_eq!(
8862 f.run(&[
8863 b"GEORADIUSBYMEMBER_RO",
8864 b"Sicily",
8865 b"Agrigento",
8866 b"100",
8867 b"km",
8868 b"WITHDIST"
8869 ]),
8870 with_dist
8871 );
8872 assert_eq!(
8873 f.run(&[
8874 b"GEOSEARCH",
8875 b"Sicily",
8876 b"FROMMEMBER",
8877 b"Agrigento",
8878 b"BYRADIUS",
8879 b"100",
8880 b"km",
8881 b"ASC",
8882 b"WITHDIST"
8883 ]),
8884 with_dist
8885 );
8886 assert_eq!(
8887 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
8888 "-ERR could not decode requested zset member\r\n"
8889 );
8890 }
8891
8892 #[test]
8893 fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
8894 let mut f = Fixture::new();
8895 sicily(&mut f);
8896 assert_eq!(
8900 f.run(&[
8901 b"GEOSEARCH",
8902 b"Sicily",
8903 b"FROMLONLAT",
8904 b"15",
8905 b"37",
8906 b"BYBOX",
8907 b"400",
8908 b"400",
8909 b"km",
8910 b"ASC",
8911 b"WITHCOORD",
8912 b"WITHDIST",
8913 b"WITHHASH"
8914 ]),
8915 "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
8916 $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
8917 *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
8918 $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
8919 *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
8920 $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
8921 );
8922 }
8923
8924 #[test]
8925 fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
8926 let mut f = Fixture::new();
8927 sicily(&mut f);
8928 let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
8929 $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
8930 $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
8931 assert_eq!(
8932 f.run(&[
8933 b"GEOSEARCHSTORE",
8934 b"dst",
8935 b"Sicily",
8936 b"FROMLONLAT",
8937 b"15",
8938 b"37",
8939 b"BYRADIUS",
8940 b"200",
8941 b"km",
8942 b"ASC"
8943 ]),
8944 ":3\r\n"
8945 );
8946 assert_eq!(
8947 f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
8948 hashes
8949 );
8950 assert_eq!(
8953 f.run(&[
8954 b"GEORADIUS",
8955 b"Sicily",
8956 b"15",
8957 b"37",
8958 b"200",
8959 b"km",
8960 b"STORE",
8961 b"dst3"
8962 ]),
8963 ":3\r\n"
8964 );
8965 assert_eq!(
8966 f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
8967 hashes
8968 );
8969 assert_eq!(
8978 f.run(&[
8979 b"GEOSEARCHSTORE",
8980 b"dst2",
8981 b"Sicily",
8982 b"FROMLONLAT",
8983 b"15",
8984 b"37",
8985 b"BYRADIUS",
8986 b"200",
8987 b"km",
8988 b"ASC",
8989 b"STOREDIST"
8990 ]),
8991 ":3\r\n"
8992 );
8993 for (member, want) in [
8994 ("Catania", 56.441_257_870_158_19),
8995 ("Agrigento", 130.423_487_067_147_14),
8996 ("Palermo", 190.442_429_847_757_92),
8997 ] {
8998 let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
8999 let got: f64 = reply
9000 .trim_start_matches(|c: char| c != '\n')
9001 .trim()
9002 .parse()
9003 .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
9004 assert!(
9005 (got - want).abs() < 1e-9,
9006 "{member} scored {got} not {want}"
9007 );
9008 }
9009 assert_eq!(
9012 f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9013 "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9014 );
9015 assert_eq!(
9019 f.run(&[
9020 b"GEOSEARCHSTORE",
9021 b"dst",
9022 b"nokey",
9023 b"FROMLONLAT",
9024 b"15",
9025 b"37",
9026 b"BYRADIUS",
9027 b"200",
9028 b"km"
9029 ]),
9030 ":0\r\n"
9031 );
9032 assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9033 }
9034
9035 #[test]
9036 fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9037 let mut f = Fixture::new();
9038 sicily(&mut f);
9039 assert_eq!(
9042 f.run(&[
9043 b"GEOADD",
9044 b"Sicily",
9045 b"XX",
9046 b"CH",
9047 b"13.361389",
9048 b"38.115556",
9049 b"Palermo"
9050 ]),
9051 ":0\r\n"
9052 );
9053 assert_eq!(
9054 f.run(&[
9055 b"GEOADD",
9056 b"Sicily",
9057 b"NX",
9058 b"13.361389",
9059 b"38.9",
9060 b"Palermo"
9061 ]),
9062 ":0\r\n"
9063 );
9064 assert_eq!(
9065 f.run(&[
9066 b"GEOADD",
9067 b"Sicily",
9068 b"CH",
9069 b"13.361389",
9070 b"38.9",
9071 b"Palermo"
9072 ]),
9073 ":1\r\n"
9074 );
9075 assert_eq!(
9078 f.run(&[
9079 b"GEOADD",
9080 b"new",
9081 b"13.361389",
9082 b"38.115556",
9083 b"here",
9084 b"181",
9085 b"38",
9086 b"there"
9087 ]),
9088 "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9089 );
9090 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9091 assert_eq!(
9092 f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9093 "-ERR value is not a valid float\r\n"
9094 );
9095 assert_eq!(
9098 f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9099 "-ERR syntax error\r\n"
9100 );
9101 assert_eq!(
9102 f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9103 "-ERR syntax error\r\n"
9104 );
9105 assert_eq!(
9106 f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9107 "-ERR syntax error\r\n"
9108 );
9109 assert_eq!(
9110 f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9111 "-ERR wrong number of arguments for 'geoadd' command\r\n"
9112 );
9113 }
9114
9115 #[test]
9118 fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9119 let mut f = Fixture::new();
9120 sicily(&mut f);
9121 let cases: &[(&[&[u8]], &str)] = &[
9122 (
9123 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9124 "-ERR need numeric radius\r\n",
9125 ),
9126 (
9127 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9128 "-ERR radius cannot be negative\r\n",
9129 ),
9130 (
9131 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9132 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9133 ),
9134 (
9135 &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9136 "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9137 ),
9138 (
9139 &[
9140 b"GEOSEARCH",
9141 b"Sicily",
9142 b"FROMLONLAT",
9143 b"15",
9144 b"37",
9145 b"BYBOX",
9146 b"x",
9147 b"1",
9148 b"km",
9149 ],
9150 "-ERR need numeric width\r\n",
9151 ),
9152 (
9153 &[
9154 b"GEOSEARCH",
9155 b"Sicily",
9156 b"FROMLONLAT",
9157 b"15",
9158 b"37",
9159 b"BYBOX",
9160 b"1",
9161 b"y",
9162 b"km",
9163 ],
9164 "-ERR need numeric height\r\n",
9165 ),
9166 (
9167 &[
9168 b"GEOSEARCH",
9169 b"Sicily",
9170 b"FROMLONLAT",
9171 b"15",
9172 b"37",
9173 b"BYBOX",
9174 b"-1",
9175 b"1",
9176 b"km",
9177 ],
9178 "-ERR height or width cannot be negative\r\n",
9179 ),
9180 (
9181 &[
9182 b"GEOSEARCH",
9183 b"Sicily",
9184 b"FROMLONLAT",
9185 b"15",
9186 b"37",
9187 b"BYRADIUS",
9188 b"1",
9189 b"km",
9190 b"ANY",
9191 ],
9192 "-ERR the ANY argument requires COUNT argument\r\n",
9193 ),
9194 (
9195 &[
9196 b"GEOSEARCH",
9197 b"Sicily",
9198 b"FROMLONLAT",
9199 b"15",
9200 b"37",
9201 b"BYRADIUS",
9202 b"1",
9203 b"km",
9204 b"COUNT",
9205 b"0",
9206 ],
9207 "-ERR COUNT must be > 0\r\n",
9208 ),
9209 (
9210 &[
9211 b"GEOSEARCH",
9212 b"Sicily",
9213 b"BYRADIUS",
9214 b"1",
9215 b"km",
9216 b"BYBOX",
9217 b"1",
9218 b"1",
9219 b"km",
9220 ],
9221 "-ERR syntax error\r\n",
9222 ),
9223 (
9224 &[
9225 b"GEOSEARCH",
9226 b"Sicily",
9227 b"FROMMEMBER",
9228 b"Palermo",
9229 b"FROMLONLAT",
9230 b"1",
9231 b"2",
9232 b"BYRADIUS",
9233 b"1",
9234 b"km",
9235 ],
9236 "-ERR syntax error\r\n",
9237 ),
9238 (
9241 &[
9242 b"geosearch",
9243 b"Sicily",
9244 b"BYRADIUS",
9245 b"1",
9246 b"km",
9247 b"ASC",
9248 b"WITHDIST",
9249 ],
9250 "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9251 ),
9252 (
9253 &[
9254 b"GEOSEARCH",
9255 b"Sicily",
9256 b"FROMLONLAT",
9257 b"15",
9258 b"37",
9259 b"ASC",
9260 b"WITHDIST",
9261 ],
9262 "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9263 ),
9264 (
9267 &[
9268 b"GEOSEARCHSTORE",
9269 b"d",
9270 b"Sicily",
9271 b"FROMLONLAT",
9272 b"15",
9273 b"37",
9274 b"BYRADIUS",
9275 b"1",
9276 b"km",
9277 b"WITHCOORD",
9278 ],
9279 "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9280 ),
9281 (
9282 &[
9283 b"GEORADIUS",
9284 b"Sicily",
9285 b"15",
9286 b"37",
9287 b"1",
9288 b"km",
9289 b"WITHDIST",
9290 b"STORE",
9291 b"d",
9292 ],
9293 "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9294 ),
9295 (
9298 &[
9299 b"GEORADIUS_RO",
9300 b"Sicily",
9301 b"15",
9302 b"37",
9303 b"1",
9304 b"km",
9305 b"STORE",
9306 b"d",
9307 ],
9308 "-ERR syntax error\r\n",
9309 ),
9310 (
9311 &[
9312 b"GEOSEARCH",
9313 b"Sicily",
9314 b"FROMLONLAT",
9315 b"15",
9316 b"37",
9317 b"BYRADIUS",
9318 b"1",
9319 b"km",
9320 b"STOREDIST",
9321 ],
9322 "-ERR syntax error\r\n",
9323 ),
9324 ];
9325 for (parts, want) in cases {
9326 assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9327 }
9328 }
9329
9330 #[test]
9333 fn every_geo_command_says_wrongtype() {
9334 let mut f = Fixture::new();
9335 f.run(&[b"SET", b"s", b"v"]);
9336 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9337 let cases: &[&[&[u8]]] = &[
9338 &[b"GEOADD", b"s", b"13", b"38", b"m"],
9339 &[b"GEOPOS", b"s", b"m"],
9340 &[b"GEOHASH", b"s", b"m"],
9341 &[b"GEODIST", b"s", b"a", b"b"],
9342 &[
9343 b"GEOSEARCH",
9344 b"s",
9345 b"FROMLONLAT",
9346 b"15",
9347 b"37",
9348 b"BYRADIUS",
9349 b"1",
9350 b"km",
9351 ],
9352 &[
9353 b"GEOSEARCHSTORE",
9354 b"d",
9355 b"s",
9356 b"FROMLONLAT",
9357 b"15",
9358 b"37",
9359 b"BYRADIUS",
9360 b"1",
9361 b"km",
9362 ],
9363 &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9364 &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9365 &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9366 &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9367 ];
9368 for case in cases {
9369 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9370 }
9371 assert_eq!(
9374 f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9375 wrong
9376 );
9377 }
9378
9379 #[test]
9382 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9383 let mut f = Fixture::new();
9384 assert_eq!(
9387 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9388 ":3\r\n"
9389 );
9390 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9391 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9392 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9393 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9395 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9396 assert_eq!(
9397 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9398 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9399 );
9400 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9402 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9403 }
9404
9405 #[test]
9408 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9409 let mut f = Fixture::new();
9410 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9411 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9412 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9413 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9414 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9415 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9417 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9418 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9419
9420 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9424 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9425 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9426 assert_eq!(
9429 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9430 "-ERR array index overflow\r\n"
9431 );
9432 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9433 }
9434
9435 #[test]
9438 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9439 let mut f = Fixture::new();
9440 f.run(&[b"ARSET", b"a", b"1", b"x"]);
9441 assert_eq!(
9442 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9443 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9444 );
9445 assert_eq!(
9448 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9449 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9450 );
9451 assert_eq!(
9453 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9454 "*2\r\n$-1\r\n$-1\r\n"
9455 );
9456 assert_eq!(
9460 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9461 "-ERR range exceeds maximum of 1000000 items\r\n"
9462 );
9463 }
9464
9465 #[test]
9468 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9469 let mut f = Fixture::new();
9470 assert_eq!(
9471 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9472 "-ERR invalid array index\r\n"
9473 );
9474 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9475 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9476 assert_eq!(
9477 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9478 "-ERR invalid array index\r\n"
9479 );
9480 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9481 assert_eq!(
9484 f.run(&[b"ARGET", b"a", b"-1"]),
9485 "-ERR invalid array index\r\n"
9486 );
9487 assert_eq!(
9490 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9491 "-ERR wrong number of arguments for 'armset' command\r\n"
9492 );
9493 assert_eq!(
9494 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9495 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9496 );
9497 }
9498
9499 #[test]
9500 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9501 let mut f = Fixture::new();
9502 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9503 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9504 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9505 assert_eq!(
9508 f.run(&[
9509 b"ARDELRANGE",
9510 b"a",
9511 b"100",
9512 b"200",
9513 b"0",
9514 b"18446744073709551614"
9515 ]),
9516 ":2\r\n"
9517 );
9518 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9519 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
9520 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
9521 }
9522
9523 #[test]
9526 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
9527 let mut f = Fixture::new();
9528 let long = vec![b'v'; 200];
9529 f.run(&[
9530 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
9531 b"short", b"5", &long, b"6", b"-0",
9532 ]);
9533 assert_eq!(
9537 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
9538 format!(
9539 "*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",
9540 String::from_utf8_lossy(&long)
9541 )
9542 );
9543 }
9544
9545 #[test]
9546 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
9547 let mut f = Fixture::new();
9548 f.run(&[b"ARSET", b"a", b"0", b"x"]);
9549 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
9550 assert_eq!(
9551 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
9552 "$12\r\nsliced-array\r\n"
9553 );
9554 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
9556 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
9557 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
9558 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
9559 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
9560 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
9561 }
9562
9563 #[test]
9564 fn every_array_command_refuses_a_key_holding_something_else() {
9565 let mut f = Fixture::new();
9566 f.run(&[b"SET", b"s", b"v"]);
9567 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9568 for cmd in [
9569 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
9570 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
9571 &[b"ARGET".as_ref(), b"s", b"0"][..],
9572 &[b"ARMGET".as_ref(), b"s", b"0"][..],
9573 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
9574 &[b"ARLEN".as_ref(), b"s"][..],
9575 &[b"ARCOUNT".as_ref(), b"s"][..],
9576 &[b"ARDEL".as_ref(), b"s", b"0"][..],
9577 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
9578 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
9579 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
9580 &[b"ARNEXT".as_ref(), b"s"][..],
9581 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
9582 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
9583 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
9584 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
9585 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
9586 &[b"ARINFO".as_ref(), b"s"][..],
9587 ] {
9588 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
9589 }
9590 }
9591
9592 #[test]
9596 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
9597 let mut f = Fixture::new();
9598 f.run(&[b"SET", b"s", b"v"]);
9599 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9600 let bad = "-ERR invalid array index\r\n";
9601 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
9602 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
9603 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
9604 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
9605 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
9606 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
9607 f.run(&[b"ARSET", b"a", b"0", b"x"]);
9609 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
9610 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
9611 }
9612
9613 #[test]
9614 fn an_append_follows_a_cursor_the_client_can_move() {
9615 let mut f = Fixture::new();
9616 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
9617 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
9618 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
9619 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
9620 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
9621
9622 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
9625 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
9626 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
9627 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
9628 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
9629 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
9630 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
9631
9632 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
9635 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
9636 assert_eq!(
9637 f.run(&[b"ARINSERT", b"a", b"x"]),
9638 "-ERR insert index overflow\r\n"
9639 );
9640 assert_eq!(
9641 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
9642 "-ERR invalid array index\r\n"
9643 );
9644 }
9645
9646 #[test]
9647 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
9648 let mut f = Fixture::new();
9649 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
9650 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
9651 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
9652 assert_eq!(
9653 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
9654 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
9655 );
9656 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
9659 assert_eq!(
9660 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
9661 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
9662 );
9663 assert_eq!(
9666 f.run(&[b"ARRING", b"r", b"0", b"x"]),
9667 "-ERR size must be positive\r\n"
9668 );
9669 assert_eq!(
9670 f.run(&[b"ARRING", b"r", b"big", b"x"]),
9671 "-ERR invalid size\r\n"
9672 );
9673 }
9674
9675 #[test]
9676 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
9677 let mut f = Fixture::new();
9678 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
9679 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
9680 assert_eq!(
9681 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
9682 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
9683 );
9684 assert_eq!(
9685 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
9686 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
9687 );
9688 assert_eq!(
9689 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
9690 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
9691 "more than there is gets what there is"
9692 );
9693 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
9696 assert_eq!(
9697 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
9698 "-ERR syntax error\r\n"
9699 );
9700 assert_eq!(
9701 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
9702 "-ERR invalid COUNT\r\n"
9703 );
9704
9705 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
9708 assert_eq!(
9709 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
9710 "*2\r\n$-1\r\n$1\r\nz\r\n"
9711 );
9712 }
9713
9714 #[test]
9715 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
9716 let mut f = Fixture::new();
9717 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
9718 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
9719 assert_eq!(
9722 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
9723 "*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"
9724 );
9725 assert_eq!(
9726 f.run(&[
9727 b"ARSCAN",
9728 b"a",
9729 b"18446744073709551614",
9730 b"0",
9731 b"LIMIT",
9732 b"1"
9733 ]),
9734 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
9735 );
9736 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
9737 assert_eq!(
9738 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
9739 "-ERR LIMIT must be positive\r\n"
9740 );
9741 assert_eq!(
9742 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
9743 "-ERR syntax error\r\n"
9744 );
9745 assert_eq!(
9746 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
9747 "-ERR wrong number of arguments for 'arscan' command\r\n"
9748 );
9749 }
9750
9751 #[test]
9752 fn a_grep_answers_the_indexes_whose_elements_match() {
9753 let mut f = Fixture::new();
9754 assert_eq!(
9755 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
9756 "*0\r\n"
9757 );
9758 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
9759
9760 assert_eq!(
9763 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
9764 "*3\r\n:0\r\n:1\r\n:2\r\n"
9765 );
9766 assert_eq!(
9767 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
9768 "*3\r\n:2\r\n:1\r\n:0\r\n"
9769 );
9770 assert_eq!(
9771 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
9772 "*2\r\n:1\r\n:2\r\n"
9773 );
9774
9775 assert_eq!(
9778 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
9779 "*1\r\n:0\r\n"
9780 );
9781 assert_eq!(
9782 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
9783 "*2\r\n:0\r\n:3\r\n"
9784 );
9785 assert_eq!(
9786 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
9787 "*1\r\n:2\r\n"
9788 );
9789 assert_eq!(
9790 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
9791 "*2\r\n:1\r\n:2\r\n"
9792 );
9793
9794 let both: &[&[u8]] = &[
9797 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
9798 ];
9799 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
9800 assert_eq!(
9801 f.run(&[
9802 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
9803 ]),
9804 "*0\r\n"
9805 );
9806 assert_eq!(
9807 f.run(&[
9808 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
9809 ]),
9810 "*2\r\n:0\r\n:1\r\n"
9811 );
9812
9813 assert_eq!(
9816 f.run(&[
9817 b"ARGREP",
9818 b"a",
9819 b"-",
9820 b"+",
9821 b"MATCH",
9822 b"a",
9823 b"WITHVALUES",
9824 b"LIMIT",
9825 b"2"
9826 ]),
9827 "*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"
9828 );
9829 assert_eq!(
9830 f.run(&[
9831 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
9832 ]),
9833 "*1\r\n:3\r\n"
9834 );
9835 }
9836
9837 #[test]
9839 fn a_grep_reports_a_broken_command_the_way_redis_does() {
9840 let mut f = Fixture::new();
9841 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
9842 let syntax = "-ERR syntax error\r\n";
9843
9844 assert_eq!(
9847 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
9848 "-ERR invalid array index\r\n"
9849 );
9850 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
9851 assert_eq!(
9853 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
9854 syntax
9855 );
9856 assert_eq!(
9857 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
9858 syntax
9859 );
9860 assert_eq!(
9861 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
9862 syntax,
9863 "a command with no predicate in it at all"
9864 );
9865 assert_eq!(
9866 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
9867 "-ERR LIMIT must be positive\r\n"
9868 );
9869 assert_eq!(
9870 f.run(&[
9871 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
9872 ]),
9873 "-ERR value is not an integer or out of range\r\n"
9874 );
9875 assert_eq!(
9876 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
9877 "-ERR regular expression is empty\r\n"
9878 );
9879 assert_eq!(
9880 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
9881 "-ERR invalid regular expression: Missing ')'\r\n"
9882 );
9883 assert_eq!(
9884 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
9885 "-ERR regular expression backreferences are not supported\r\n"
9886 );
9887 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
9890 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
9891 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
9892 }
9893
9894 #[test]
9895 fn an_op_reduces_a_range_to_one_number() {
9896 let mut f = Fixture::new();
9897 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
9898 assert_eq!(
9899 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
9900 "$4\r\n-0.5\r\n"
9901 );
9902 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
9903 assert_eq!(
9904 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
9905 "$3\r\n2.5\r\n"
9906 );
9907 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
9908 assert_eq!(
9909 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
9910 ":1\r\n"
9911 );
9912 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
9915 assert_eq!(
9916 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
9917 "$19\r\n0.30000000000000004\r\n"
9918 );
9919 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
9920 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
9921
9922 f.run(&[b"ARSET", b"w", b"0", b"word"]);
9925 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
9926 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
9927 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
9928
9929 assert_eq!(
9930 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
9931 "-ERR unknown operation\r\n"
9932 );
9933 assert_eq!(
9934 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
9935 "-ERR MATCH requires a value argument\r\n"
9936 );
9937 assert_eq!(
9938 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
9939 "-ERR wrong number of arguments for 'arop' command\r\n"
9940 );
9941 }
9942
9943 #[test]
9944 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
9945 let mut f = Fixture::new();
9946 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
9947 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
9948 let short = f.run(&[b"ARINFO", b"a"]);
9949 assert!(
9950 short.starts_with("*14\r\n"),
9951 "seven pairs on RESP2: {short}"
9952 );
9953 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
9954 assert!(
9955 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
9956 "{short}"
9957 );
9958 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
9959 let full = f.run(&[b"ARINFO", b"a", b"full"]);
9960 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
9961 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
9964 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
9965 assert!(
9966 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
9967 "{full}"
9968 );
9969 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
9970
9971 let mut g = Fixture::new();
9973 g.run(&[b"HELLO", b"3"]);
9974 g.run(&[b"ARINSERT", b"a", b"x"]);
9975 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
9976 assert!(map.starts_with("%12\r\n"), "{map}");
9977 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
9978 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
9979 }
9980
9981 #[test]
9982 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
9983 let mut f = Fixture::new();
9984 for (score, want) in [
9987 ("3", "3"),
9988 ("3.5", "3.5"),
9989 ("0.3", "0.3"),
9990 ("1e30", "1e+30"),
9991 ("1e19", "1e+19"),
9992 ("1e-7", "1e-7"),
9993 ("0.000001", "0.000001"),
9994 ("4611686018427387904", "4611686018427387904"),
9995 ("-0", "-0"),
9996 ] {
9997 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
9998 assert_eq!(
9999 f.run(&[b"ZSCORE", b"z", b"m"]),
10000 format!("${}\r\n{want}\r\n", want.len()),
10001 "score {score}"
10002 );
10003 }
10004
10005 let mut g = Fixture::new();
10008 g.run(&[b"HELLO", b"3"]);
10009 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10010 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10011 assert_eq!(
10016 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10017 "$31\r\n1000000000000000000000000000000\r\n"
10018 );
10019 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10020 assert_eq!(
10021 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10022 "$20\r\n10000000000000000000\r\n"
10023 );
10024 }
10025
10026 #[test]
10029 fn a_node_comes_back_with_the_fields_it_went_in_with() {
10030 let mut f = Fixture::new();
10031 assert_eq!(
10032 f.run(&[
10033 b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10034 ]),
10035 ":1\r\n"
10036 );
10037 assert_eq!(
10043 f.run(&[b"G.NGET", b"social", b"ada"]),
10044 "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10045 );
10046 assert_eq!(
10049 f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10050 ":0\r\n"
10051 );
10052 assert_eq!(
10053 f.run(&[b"G.NGET", b"social", b"ada"]),
10054 "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10055 );
10056 assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10059 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10060 assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10061 assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10062
10063 assert_eq!(
10066 f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10067 "-ERR syntax error\r\n"
10068 );
10069 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10070
10071 let mut g = Fixture::new();
10073 g.run(&[b"HELLO", b"3"]);
10074 g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10075 assert_eq!(
10076 g.run(&[b"G.NGET", b"social", b"ada"]),
10077 "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10078 );
10079 }
10080
10081 #[test]
10082 fn an_edge_creates_the_ends_it_needs() {
10083 let mut f = Fixture::new();
10084 assert_eq!(
10085 f.run(&[
10086 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10087 ]),
10088 ":1\r\n"
10089 );
10090 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10092 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10093 assert_eq!(
10094 f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10095 "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10096 );
10097 assert_eq!(
10098 f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10099 "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10100 );
10101 assert_eq!(
10104 f.run(&[
10105 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10106 ]),
10107 ":0\r\n"
10108 );
10109 assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10110 assert_eq!(
10112 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10113 ":1\r\n"
10114 );
10115 assert_eq!(
10116 f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10117 ":1\r\n"
10118 );
10119
10120 assert_eq!(
10121 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10122 ":1\r\n"
10123 );
10124 assert_eq!(
10125 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10126 ":0\r\n"
10127 );
10128 assert_eq!(
10131 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10132 ":0\r\n"
10133 );
10134 assert_eq!(
10135 f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10136 ":0\r\n"
10137 );
10138 assert_eq!(
10139 f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10140 ":0\r\n"
10141 );
10142 }
10143
10144 #[test]
10147 fn a_hop_answers_a_cursor_and_a_page() {
10148 let mut f = Fixture::new();
10149 for i in 0..25u32 {
10150 let dst = format!("n{i}");
10151 f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10152 }
10153 let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10155 assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10156
10157 let mut seen = 0;
10158 let mut cursor = String::from("0");
10159 loop {
10160 let page = f.run(&[
10161 b"G.OUT",
10162 b"social",
10163 b"hub",
10164 b"FOLLOWS",
10165 b"COUNT",
10166 b"7",
10167 b"CURSOR",
10168 cursor.as_bytes(),
10169 ]);
10170 let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10171 cursor = head
10172 .rsplit("\r\n")
10173 .next()
10174 .expect("the cursor line")
10175 .to_string();
10176 seen += rest
10177 .split_once("\r\n")
10178 .expect("the page length")
10179 .0
10180 .parse::<usize>()
10181 .expect("a length");
10182 if cursor == "0" {
10183 break;
10184 }
10185 }
10186 assert_eq!(seen, 25, "every neighbour once across the pages");
10187
10188 assert_eq!(
10191 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10192 "*2\r\n$1\r\n0\r\n*0\r\n"
10193 );
10194 assert_eq!(
10195 f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10196 "*2\r\n$1\r\n0\r\n*0\r\n"
10197 );
10198 assert_eq!(
10199 f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10200 "*2\r\n$1\r\n0\r\n*0\r\n"
10201 );
10202 assert_eq!(
10203 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10204 "-ERR COUNT must be a positive integer\r\n"
10205 );
10206 assert_eq!(
10207 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10208 "-ERR syntax error\r\n"
10209 );
10210 }
10211
10212 #[test]
10213 fn a_degree_counts_one_way_or_both() {
10214 let mut f = Fixture::new();
10215 f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10216 f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10217 f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10218 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10219 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10220 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10221 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10222 assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10223 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10224 assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10225 assert_eq!(
10226 f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10227 "-ERR syntax error\r\n"
10228 );
10229 }
10230
10231 #[test]
10234 fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10235 let mut f = Fixture::new();
10236 for (src, dst) in [
10237 ("ada", "grace"),
10238 ("ada", "alan"),
10239 ("grace", "edsger"),
10240 ("alan", "edsger"),
10241 ("edsger", "barbara"),
10242 ] {
10243 f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10244 }
10245 assert_eq!(
10248 f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10249 "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10250 );
10251 assert_eq!(
10252 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10253 "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10254 );
10255 let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10256 assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10257 assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10258 assert_eq!(
10260 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10261 "*1\r\n$5\r\ngrace\r\n"
10262 );
10263 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10265 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10266 assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10267 assert_eq!(
10268 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10269 "-ERR DEPTH must be a positive integer\r\n"
10270 );
10271 assert_eq!(
10272 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10273 "-ERR syntax error\r\n"
10274 );
10275 }
10276
10277 #[test]
10280 fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10281 let mut f = Fixture::new();
10282 for i in 0..6u32 {
10285 let src = format!("n{i}");
10286 let dst = format!("n{}", i + 1);
10287 f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10288 }
10289 assert_eq!(
10290 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10291 "*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"
10292 );
10293 f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10294 assert_eq!(
10295 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10296 "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10297 );
10298 assert_eq!(
10301 f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10302 "*1\r\n$2\r\nn2\r\n"
10303 );
10304 assert_eq!(
10305 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10306 "*0\r\n"
10307 );
10308 assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10310 f.run(&[b"G.NADD", b"road", b"island"]);
10313 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10314 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10315 assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10316 assert_eq!(
10317 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10318 "-ERR syntax error\r\n"
10319 );
10320 }
10321
10322 #[test]
10326 fn the_keyspace_sees_a_graph_key_like_any_other() {
10327 let mut f = Fixture::new();
10328 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10329 assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10330 assert_eq!(
10331 f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10332 "$9\r\nadjacency\r\n"
10333 );
10334 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10335 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10336 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10337 let held = f.server.memory_bytes();
10341 for i in 0..200u32 {
10342 let dst = format!("n{i}");
10343 f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10344 }
10345 assert!(
10346 f.server.memory_bytes() > held,
10347 "two hundred edges cost something: {held} then {}",
10348 f.server.memory_bytes()
10349 );
10350 f.run(&[b"DEL", b"big"]);
10351
10352 assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10355 assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10356 assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10357 assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10358 assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10359 f.run(&[b"SELECT", b"1"]);
10360 assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10361
10362 assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10363 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10364 f.run(&[b"G.NADD", b"g", b"n"]);
10365 assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10366 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10367 }
10368
10369 #[test]
10372 fn a_graph_cannot_be_copied_or_dumped() {
10373 let mut f = Fixture::new();
10374 f.run(&[b"G.NADD", b"social", b"ada"]);
10375 assert_eq!(
10376 f.run(&[b"COPY", b"social", b"other"]),
10377 "-ERR COPY is not supported for a graph\r\n"
10378 );
10379 assert_eq!(
10380 f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10381 "-ERR COPY is not supported for a graph\r\n"
10382 );
10383 assert_eq!(
10384 f.run(&[b"DUMP", b"social"]),
10385 "-ERR DUMP is not supported for a graph\r\n"
10386 );
10387 assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10389 }
10390
10391 #[test]
10394 fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10395 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10396 let mut f = Fixture::new();
10397 f.run(&[b"G.NADD", b"social", b"ada"]);
10398 assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10399 assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10400 assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10401
10402 f.run(&[b"SET", b"str", b"v"]);
10403 for cmd in [
10404 vec![b"G.NADD".as_ref(), b"str", b"n"],
10405 vec![b"G.NGET".as_ref(), b"str", b"n"],
10406 vec![b"G.NDEL".as_ref(), b"str", b"n"],
10407 vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10408 vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10409 vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10410 vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10411 vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10412 vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10413 vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10414 ] {
10415 assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10416 }
10417 }
10418
10419 #[test]
10422 fn a_graph_goes_when_its_last_node_does() {
10423 let mut f = Fixture::new();
10424 f.run(&[
10425 b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10426 ]);
10427 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10428 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10430 assert_eq!(
10431 f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10432 ":0\r\n"
10433 );
10434 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10435 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10436
10437 assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10438 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10439 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10440 assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10441
10442 f.run(&[b"G.NADD", b"social", b"first"]);
10445 f.run(&[b"G.NADD", b"social", b"second"]);
10446 f.run(&[b"G.NDEL", b"social", b"first"]);
10447 f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10448 assert_eq!(
10449 f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10450 "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10451 );
10452 }
10453
10454 #[test]
10459 fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10460 let mut f = Fixture::new();
10461 let doc = br#"{"a":1,"b":{"c":true}}"#;
10462 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10463 assert_eq!(
10466 f.run(&[b"JSON.GET", b"doc"]),
10467 bulk(r#"{"a":1,"b":{"c":true}}"#)
10468 );
10469 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10470 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10471 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10472 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10475 assert_eq!(
10476 f.run(&[b"JSON.GET", b"doc", b".nope"]),
10477 "-ERR Path does not exist\r\n"
10478 );
10479 assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10480 assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10483 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10484 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10485 assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10486 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10487 }
10488
10489 #[test]
10495 fn the_two_json_errors_that_carry_no_prefix() {
10496 let mut f = Fixture::new();
10497 f.run(&[b"SET", b"plain", b"x"]);
10498 let wrong = "-Existing key has wrong Redis type\r\n";
10499 assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10500 assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10501 assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10502 assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10503 assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10504
10505 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10506 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10510 assert_eq!(
10511 f.run(&[b"JSON.GET", b"doc"]),
10512 bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10513 );
10514 assert_eq!(
10515 f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10516 "-Err wrong static path\r\n"
10517 );
10518 }
10519
10520 #[test]
10522 fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10523 let mut f = Fixture::new();
10524 assert_eq!(
10526 f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
10527 "-ERR new objects must be created at the root\r\n"
10528 );
10529 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
10530 assert_eq!(
10533 f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
10534 "-ERR new objects must be created at the root\r\n"
10535 );
10536 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
10537 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
10538
10539 f.run(&[
10540 b"JSON.SET",
10541 b"doc",
10542 b"$",
10543 br#"{"o":{},"arr":[1,2],"s":"x"}"#,
10544 ]);
10545 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10547 assert_eq!(
10550 f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
10551 "$-1\r\n"
10552 );
10553 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
10554 assert_eq!(
10556 f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
10557 "-ERR array index out of range\r\n"
10558 );
10559 assert_eq!(
10560 f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
10561 "-ERR array index out of range\r\n"
10562 );
10563 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
10564 assert_eq!(
10567 f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
10568 "$-1\r\n"
10569 );
10570 assert_eq!(
10571 f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
10572 "$-1\r\n"
10573 );
10574 assert_eq!(
10575 f.run(&[b"JSON.GET", b"doc"]),
10576 bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
10577 );
10578 assert!(
10582 f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
10583 .starts_with("-this is not the start of a value")
10584 );
10585 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
10586 }
10587
10588 #[test]
10591 fn the_json_commands_that_do_not_answer_text() {
10592 let mut f = Fixture::new();
10593 let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
10594 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10595
10596 assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
10597 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
10598 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
10599 assert_eq!(
10600 f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
10601 format!("*1\r\n{}", bulk("integer"))
10602 );
10603 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
10606 assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
10607
10608 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
10611 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
10612 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
10615 assert_eq!(
10616 f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
10617 "-ERR Path does not exist or not a bool\r\n"
10618 );
10619 assert_eq!(
10620 f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
10621 "-ERR Path does not exist or not a bool\r\n"
10622 );
10623 assert_eq!(
10624 f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
10625 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10626 );
10627
10628 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
10631 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
10632 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
10633 assert_eq!(
10634 f.run(&[b"JSON.GET", b"doc"]),
10635 bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
10636 );
10637
10638 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
10641 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
10642 assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
10647 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
10648 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10649 assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
10650 assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
10651 assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
10652 assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
10653 assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
10654 }
10655
10656 #[test]
10663 fn json_get_lays_out_the_wrapper_it_builds() {
10664 let mut f = Fixture::new();
10665 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
10666
10667 assert_eq!(
10668 f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
10669 bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
10670 );
10671 assert_eq!(
10673 f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
10674 bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
10675 );
10676 let fmt: &[&[u8]] = &[b"INDENT", b" ", b"NEWLINE", b"\n", b"SPACE", b" "];
10677 let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
10678 one.extend_from_slice(fmt);
10679 one.push(b"$.b");
10680 assert_eq!(
10681 f.run(&one),
10682 bulk("[\n [\n 1,\n {\n \"c\": 2\n }\n ]\n]")
10683 );
10684 let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
10685 two.extend_from_slice(fmt);
10686 two.push(b"$.a");
10687 two.push(b"$.nope");
10688 assert_eq!(
10689 f.run(&two),
10690 bulk("{\n \"$.a\": [\n 1\n ],\n \"$.nope\": []\n}")
10691 );
10692 let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
10695 root.push(b".a");
10696 assert_eq!(f.run(&root), bulk("1"));
10697 }
10698
10699 #[test]
10702 fn json_mget_answers_once_per_key_whatever_is_under_them() {
10703 let mut f = Fixture::new();
10704 f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
10705 f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
10706 f.run(&[b"SET", b"plain", b"x"]);
10707 assert_eq!(
10708 f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
10709 format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
10710 );
10711 assert_eq!(
10714 f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
10715 format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
10716 );
10717 assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
10720 }
10721
10722 #[test]
10730 fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
10731 let mut f = Fixture::new();
10732 let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
10733 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10734
10735 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
10736 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
10737 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
10738 assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
10739 assert_eq!(
10740 f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
10741 format!("*2\r\n{}{}", bulk("x"), bulk("y"))
10742 );
10743 assert_eq!(
10746 f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
10747 "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
10748 );
10749
10750 assert_eq!(
10753 f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
10754 "-ERR Path does not exist\r\n"
10755 );
10756 assert_eq!(
10757 f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
10758 "-ERR Path does not exist\r\n"
10759 );
10760 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
10761 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
10764 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
10767
10768 assert_eq!(
10772 f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
10773 "-ERR Path does not exist or not an array\r\n"
10774 );
10775 assert_eq!(
10776 f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
10777 "-ERR Path does not exist or not an object\r\n"
10778 );
10779 assert_eq!(
10780 f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
10781 "-WRONGTYPE wrong type of path value - expected object\r\n"
10782 );
10783 assert_eq!(
10784 f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
10785 "-WRONGTYPE wrong type of path value - expected string\r\n"
10786 );
10787
10788 assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
10791 assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
10792 assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
10793 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
10794 assert_eq!(
10795 f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
10796 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10797 );
10798 assert_eq!(
10800 f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
10801 "-ERR Path does not exist or not an object\r\n"
10802 );
10803 }
10804
10805 #[test]
10811 fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
10812 let mut f = Fixture::new();
10813 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
10814
10815 assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
10816 assert_eq!(
10817 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
10818 "*1\r\n:6\r\n"
10819 );
10820 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
10821
10822 assert_eq!(
10825 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
10826 ":7\r\n"
10827 );
10828 assert_eq!(
10829 f.run(&[b"JSON.GET", b"doc", b".a"]),
10830 bulk("[1,2,3,4,5,0,6]")
10831 );
10832 assert_eq!(
10833 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
10834 ":8\r\n"
10835 );
10836 assert_eq!(
10838 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
10839 "-ERR index out of bounds\r\n"
10840 );
10841 assert_eq!(
10842 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
10843 "-ERR index out of bounds\r\n"
10844 );
10845
10846 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
10849 assert_eq!(
10850 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
10851 ":3\r\n"
10852 );
10853 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
10854 assert_eq!(
10855 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
10856 ":2\r\n"
10857 );
10858 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
10859 assert_eq!(
10860 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
10861 ":0\r\n"
10862 );
10863 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10864
10865 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
10868 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
10869 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
10870 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
10871 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
10872
10873 for call in [
10876 &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
10877 &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
10878 &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
10879 &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
10880 ] {
10881 for path in [&b".n"[..], &b".nope"[..]] {
10882 let args: Vec<&[u8]> = call
10883 .iter()
10884 .map(|a| if *a == b"PATH" { path } else { *a })
10885 .collect();
10886 assert_eq!(
10887 f.run(&args),
10888 "-ERR Path does not exist or not an array\r\n",
10889 "{} {}",
10890 String::from_utf8_lossy(call[0]),
10891 String::from_utf8_lossy(path)
10892 );
10893 }
10894 }
10895
10896 assert_eq!(
10899 f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
10900 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10901 );
10902 assert_eq!(
10903 f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
10904 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10905 );
10906
10907 assert!(
10912 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
10913 .starts_with("-this is not the start of a value")
10914 );
10915 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10916 }
10917
10918 #[test]
10926 fn json_arrinsert_is_all_or_nothing_across_the_matches() {
10927 let mut f = Fixture::new();
10928 let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
10929 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10930 assert_eq!(
10931 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
10932 "-ERR index out of bounds\r\n"
10933 );
10934 assert_eq!(
10935 f.run(&[b"JSON.GET", b"doc"]),
10936 bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
10937 );
10938 assert_eq!(
10940 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
10941 "*3\r\n:4\r\n:3\r\n:2\r\n"
10942 );
10943 assert_eq!(
10944 f.run(&[b"JSON.GET", b"doc"]),
10945 bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
10946 );
10947 }
10948
10949 #[test]
10957 fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
10958 let mut f = Fixture::new();
10959 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
10960
10961 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
10962 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
10963 assert_eq!(
10964 f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
10965 "*1\r\n:1\r\n"
10966 );
10967
10968 assert_eq!(
10971 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
10972 ":3\r\n"
10973 );
10974 assert_eq!(
10977 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
10978 ":-1\r\n"
10979 );
10980
10981 for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
10984 assert_eq!(
10985 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
10986 ":3\r\n",
10987 "{}",
10988 String::from_utf8_lossy(start)
10989 );
10990 }
10991 assert_eq!(
10992 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
10993 ":0\r\n"
10994 );
10995 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
10998 assert_eq!(
10999 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
11000 ":-1\r\n"
11001 );
11002
11003 f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
11007 assert_eq!(
11008 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11009 ":0\r\n"
11010 );
11011 assert_eq!(
11012 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11013 ":1\r\n"
11014 );
11015 assert_eq!(
11016 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11017 ":-1\r\n"
11018 );
11019
11020 assert_eq!(
11024 f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11025 "-ERR Path does not exist\r\n"
11026 );
11027 assert_eq!(
11028 f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11029 "-WRONGTYPE wrong type of path value - expected array\r\n"
11030 );
11031 assert_eq!(
11032 f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11033 "-ERR Path does not exist\r\n"
11034 );
11035 assert_eq!(
11036 f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11037 "-ERR Path does not exist\r\n"
11038 );
11039 }
11040
11041 #[test]
11044 fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11045 let mut f = Fixture::new();
11046 let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11047 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11048
11049 assert_eq!(
11052 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11053 bulk("9").as_str()
11054 );
11055 assert_eq!(
11057 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11058 bulk("[11]").as_str()
11059 );
11060 assert_eq!(
11063 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11064 bulk("13.0").as_str()
11065 );
11066 assert_eq!(
11067 f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11068 bulk("number").as_str()
11069 );
11070 assert_eq!(
11071 f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11072 bulk("3.0").as_str()
11073 );
11074 assert_eq!(
11075 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11076 bulk("-8").as_str()
11077 );
11078 f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11081 assert_eq!(
11082 f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11083 bulk("1.224744871391589").as_str()
11084 );
11085 assert_eq!(
11086 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11087 "-ERR result is not a number\r\n"
11088 );
11089 f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11093 assert_eq!(
11094 f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11095 "-ERR numeric overflow\r\n"
11096 );
11097 f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11098 assert_eq!(
11099 f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11100 "-ERR numeric overflow\r\n"
11101 );
11102 f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11104 assert_eq!(
11105 f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11106 "-ERR result is not a number\r\n"
11107 );
11108
11109 assert_eq!(
11113 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11114 bulk("[null]").as_str()
11115 );
11116 assert_eq!(
11117 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11118 bulk("[]").as_str()
11119 );
11120 assert_eq!(
11121 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11122 "-ERR Path does not exist or does not contains a number\r\n"
11123 );
11124 assert_eq!(
11125 f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11126 "-ERR Path does not exist or does not contains a number\r\n"
11127 );
11128 assert_eq!(
11131 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11132 "-bad input number\r\n"
11133 );
11134 assert_eq!(
11135 f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11136 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11137 );
11138 assert_eq!(
11139 f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11140 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11141 );
11142 }
11143
11144 #[test]
11147 fn json_strappend_reads_its_shape_off_the_argument_count() {
11148 let mut f = Fixture::new();
11149 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11150
11151 assert_eq!(
11152 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11153 ":3\r\n"
11154 );
11155 assert_eq!(
11156 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11157 "*1\r\n:4\r\n"
11158 );
11159 assert_eq!(
11162 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11163 ":6\r\n"
11164 );
11165 f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11168 assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11169 assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11170
11171 assert_eq!(
11175 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11176 "-WRONGTYPE wrong type of path value - expected string\r\n"
11177 );
11178 assert_eq!(
11179 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11180 "*1\r\n$-1\r\n"
11181 );
11182 assert_eq!(
11183 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11184 "-ERR Path does not exist or not a string\r\n"
11185 );
11186 assert_eq!(
11187 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11188 "*0\r\n"
11189 );
11190 assert_eq!(
11191 f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11192 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11193 );
11194 }
11195
11196 #[test]
11199 fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11200 let mut f = Fixture::new();
11201 let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11204
11205 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11206 assert_eq!(
11207 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11208 ":4\r\n"
11209 );
11210 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11211 assert_eq!(
11212 f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11213 ":2\r\n"
11214 );
11215 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11216 assert_eq!(
11217 f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11218 ":1\r\n"
11219 );
11220 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11221 assert_eq!(
11222 f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11223 bulk("1").as_str()
11224 );
11225 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11226 assert_eq!(
11227 f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11228 bulk("13").as_str()
11229 );
11230 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11231 assert_eq!(
11232 f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11233 ":4\r\n"
11234 );
11235 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11236 assert_eq!(
11237 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11238 bulk("false").as_str()
11239 );
11240 assert_eq!(
11243 f.run(&[b"JSON.GET", b"doc", b".a"]),
11244 bulk("[false,true,false]").as_str()
11245 );
11246
11247 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11250 assert_eq!(
11251 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11252 ":3\r\n"
11253 );
11254 assert_eq!(
11255 f.run(&[b"JSON.GET", b"doc", b".a"]),
11256 bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11257 );
11258 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11261 assert_eq!(
11262 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11263 "-ERR Path does not exist or not an array\r\n"
11264 );
11265 assert_eq!(
11266 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11267 "-ERR Path does not exist or not a bool\r\n"
11268 );
11269 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11273 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11274 assert_eq!(
11275 f.run(&[b"JSON.GET", b"doc", b".a"]),
11276 bulk("[[],[2]]").as_str()
11277 );
11278 }
11279
11280 #[test]
11283 fn a_write_reaches_a_match_that_sits_inside_another_match() {
11284 let mut f = Fixture::new();
11285 let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11286
11287 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11288 assert_eq!(
11289 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11290 "*3\r\n:3\r\n:2\r\n:3\r\n"
11291 );
11292 assert_eq!(
11293 f.run(&[b"JSON.GET", b"doc", b"$"]),
11294 bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11295 );
11296
11297 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11300 assert_eq!(
11301 f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11302 "*3\r\n:1\r\n:1\r\n:1\r\n"
11303 );
11304 assert_eq!(
11305 f.run(&[b"JSON.GET", b"doc", b"$"]),
11306 bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11307 );
11308
11309 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11312 assert_eq!(
11313 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11314 bulk("[null,8,8]").as_str()
11315 );
11316 }
11317
11318 #[test]
11321 fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11322 let mut f = Fixture::new();
11323 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11324
11325 assert_eq!(
11328 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11329 bulk("[null]").as_str()
11330 );
11331 assert_eq!(
11332 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11333 bulk("[null]").as_str()
11334 );
11335 assert_eq!(
11336 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11337 bulk("[]").as_str()
11338 );
11339 assert_eq!(
11340 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11341 "-ERR Path does not exist or does not contains a number\r\n"
11342 );
11343 assert_eq!(
11345 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11346 "-bad input number\r\n"
11347 );
11348
11349 assert_eq!(
11351 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11352 "*1\r\n$-1\r\n"
11353 );
11354 assert_eq!(
11355 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11356 "-ERR Path does not exist or not a string\r\n"
11357 );
11358 assert_eq!(
11359 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11360 "-WRONGTYPE wrong type of path value - expected string\r\n"
11361 );
11362
11363 assert_eq!(
11365 f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11366 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11367 );
11368 assert_eq!(
11369 f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11370 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11371 );
11372 }
11373
11374 #[test]
11377 fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11378 let mut f = Fixture::new();
11379
11380 assert_eq!(
11384 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11385 "+OK\r\n"
11386 );
11387 assert_eq!(
11388 f.run(&[b"JSON.GET", b"doc", b"$"]),
11389 bulk(r#"[{"x":null,"y":1}]"#).as_str()
11390 );
11391
11392 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11395 assert_eq!(
11396 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11397 "+OK\r\n"
11398 );
11399 assert_eq!(
11400 f.run(&[b"JSON.GET", b"doc", b"$"]),
11401 bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11402 );
11403
11404 assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11406 assert_eq!(
11407 f.run(&[b"JSON.GET", b"doc", b"$"]),
11408 bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11409 );
11410
11411 assert_eq!(
11415 f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11416 "+OK\r\n"
11417 );
11418 assert_eq!(
11419 f.run(&[b"JSON.GET", b"doc", b"$"]),
11420 bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11421 );
11422
11423 assert_eq!(
11427 f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11428 "+OK\r\n"
11429 );
11430 assert_eq!(
11431 f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11432 bulk(r#"[{"z":null}]"#).as_str()
11433 );
11434 assert_eq!(
11435 f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11436 "$-1\r\n"
11437 );
11438 assert_eq!(
11439 f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11440 "-Err wrong static path\r\n"
11441 );
11442
11443 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11445 assert_eq!(
11446 f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11447 "+OK\r\n"
11448 );
11449 assert_eq!(
11450 f.run(&[b"JSON.GET", b"doc", b"$"]),
11451 bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11452 );
11453
11454 assert_eq!(
11456 f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11457 "-ERR syntax error\r\n"
11458 );
11459 assert_eq!(
11460 f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11461 "-ERR new objects must be created at the root\r\n"
11462 );
11463 f.run(&[b"SET", b"str", b"x"]);
11464 assert_eq!(
11465 f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11466 "-Existing key has wrong Redis type\r\n"
11467 );
11468 }
11469
11470 #[test]
11473 fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11474 let mut f = Fixture::new();
11475 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11476 assert_eq!(
11477 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11478 "+OK\r\n"
11479 );
11480 assert_eq!(
11484 f.run(&[b"JSON.GET", b"doc", b"$"]),
11485 bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11486 );
11487
11488 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11491 assert_eq!(
11492 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11493 "+OK\r\n"
11494 );
11495 assert_eq!(
11496 f.run(&[b"JSON.GET", b"doc", b"$"]),
11497 bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11498 );
11499 }
11500
11501 #[test]
11504 fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11505 let mut f = Fixture::new();
11506 let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11507 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11508
11509 assert_eq!(
11510 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11511 bulk(r#"["a","c"]"#).as_str()
11512 );
11513 assert_eq!(
11516 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11517 bulk(r#"["a","c"]"#).as_str()
11518 );
11519 assert_eq!(
11521 f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
11522 bulk(r#""a""#).as_str()
11523 );
11524 assert_eq!(
11525 f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
11526 "*1\r\n$6\r\nobject\r\n"
11527 );
11528
11529 assert_eq!(
11533 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
11534 bulk("[9,10]").as_str()
11535 );
11536 assert_eq!(
11537 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
11538 "+OK\r\n"
11539 );
11540 assert_eq!(
11541 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
11542 "-Err wrong static path\r\n"
11543 );
11544 assert_eq!(
11545 f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
11546 ":2\r\n"
11547 );
11548 assert_eq!(
11549 f.run(&[b"JSON.GET", b"doc", b"$"]),
11550 bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
11551 );
11552
11553 assert!(
11556 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
11557 .starts_with("-ERR")
11558 );
11559 assert!(
11560 f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
11561 .starts_with("-ERR")
11562 );
11563 }
11564
11565 #[test]
11568 fn a_filter_takes_the_membership_operators_and_the_methods_too() {
11569 let mut f = Fixture::new();
11570 let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
11571 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11572
11573 for (path, want) in [
11574 (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
11575 (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
11576 (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
11577 (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
11578 (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
11579 (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
11580 (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
11581 (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
11582 (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
11583 (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
11584 (b"$.box[?(@.n~)].t", "[]"),
11585 (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
11586 (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
11587 (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
11588 (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
11589 ] {
11590 assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
11591 }
11592
11593 assert_eq!(
11596 f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
11597 "+OK\r\n"
11598 );
11599 assert_eq!(
11600 f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
11601 bulk(r#"["b"]"#).as_str()
11602 );
11603 }
11604
11605 #[test]
11608 fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
11609 let mut f = Fixture::new();
11610 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
11611 assert_eq!(
11612 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11613 "+OK\r\n"
11614 );
11615 assert_eq!(
11616 f.run(&[b"JSON.GET", b"doc", b"$"]),
11617 bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
11618 );
11619 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
11622 assert_eq!(
11623 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11624 "+OK\r\n"
11625 );
11626 assert_eq!(
11627 f.run(&[b"JSON.GET", b"doc", b"$"]),
11628 bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
11629 );
11630 }
11631
11632 #[test]
11635 fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
11636 let mut f = Fixture::new();
11637 assert_eq!(
11638 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
11639 "+OK\r\n"
11640 );
11641 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
11642 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
11643
11644 assert_eq!(
11646 f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
11647 "+OK\r\n"
11648 );
11649 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
11650
11651 f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
11656 assert_eq!(
11657 f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
11658 "$-1\r\n"
11659 );
11660 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
11661 assert_eq!(
11662 f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
11663 "$-1\r\n"
11664 );
11665 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11666
11667 f.run(&[b"SET", b"str", b"x"]);
11672 assert_eq!(
11673 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
11674 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
11675 );
11676 assert_eq!(
11677 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
11678 "-Existing key has wrong Redis type\r\n"
11679 );
11680 assert_eq!(
11681 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
11682 "-ERR new objects must be created at the root\r\n"
11683 );
11684 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
11685
11686 assert_eq!(
11691 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
11692 "-Err wrong static path\r\n"
11693 );
11694 assert_eq!(
11695 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
11696 "-ERR array index out of range\r\n"
11697 );
11698 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11699
11700 f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
11704 assert_eq!(
11705 f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
11706 "+OK\r\n"
11707 );
11708 assert_eq!(
11709 f.run(&[b"JSON.GET", b"c", b"$"]),
11710 bulk(r#"[{"n":3}]"#).as_str()
11711 );
11712
11713 assert_eq!(
11716 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
11717 "-ERR wrong number of arguments for 'json.mset' command\r\n"
11718 );
11719 }
11720
11721 #[test]
11724 fn json_resp_answers_the_document_as_resp_types() {
11725 let mut f = Fixture::new();
11726 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
11727 assert_eq!(
11728 f.run(&[b"JSON.RESP", b"doc"]),
11729 "*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"
11730 );
11731 assert_eq!(
11733 f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
11734 "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
11735 );
11736
11737 f.run(&[
11738 b"JSON.SET",
11739 b"doc",
11740 b"$",
11741 br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
11742 ]);
11743 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
11744 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
11745 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
11748 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
11749 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
11750
11751 assert_eq!(
11754 f.run(&[b"JSON.RESP", b"doc", b".nope"]),
11755 "-ERR Path does not exist\r\n"
11756 );
11757 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
11758 assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
11759 assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
11760 }
11761
11762 #[test]
11766 fn json_debug_answers_a_byte_count_and_its_own_help() {
11767 let mut f = Fixture::new();
11768 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
11769 let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
11770 assert!(one.starts_with(':'), "{one}");
11771 assert_eq!(
11772 f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
11773 format!("*1\r\n{one}")
11774 );
11775 let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
11776 assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
11777
11778 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
11782 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
11783 assert_eq!(
11784 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
11785 "-ERR Path does not exist\r\n"
11786 );
11787 assert_eq!(
11788 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
11789 "*0\r\n"
11790 );
11791
11792 assert_eq!(
11793 f.run(&[b"JSON.DEBUG", b"HELP"]),
11794 "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
11795 $34\r\nHELP - this message\r\n"
11796 );
11797 assert_eq!(
11798 f.run(&[b"JSON.DEBUG", b"NOPE"]),
11799 "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
11800 );
11801 assert_eq!(
11802 f.run(&[b"JSON.DEBUG", b"MEMORY"]),
11803 "-ERR wrong number of arguments for 'json.debug' command\r\n"
11804 );
11805 }
11806
11807 #[test]
11812 fn the_first_vadd_decides_how_wide_the_set_is() {
11813 let mut f = Fixture::new();
11814 assert_eq!(
11815 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
11816 ":1\r\n"
11817 );
11818 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
11819 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11820 assert_eq!(
11823 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
11824 ":0\r\n"
11825 );
11826 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11827 assert_eq!(
11830 f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
11831 "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
11832 );
11833 assert_eq!(
11836 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
11837 ":1\r\n"
11838 );
11839 assert_eq!(
11840 f.run(&[b"VEMB", b"v", b"nowhere"]),
11841 "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
11842 );
11843 assert_eq!(
11847 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
11848 "-ERR asked quantization mismatch with existing vector set\r\n"
11849 );
11850 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
11853 assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
11854 assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
11855 }
11856
11857 #[test]
11860 fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
11861 let mut f = Fixture::new();
11862 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
11863 assert_eq!(
11869 f.run(&[b"VEMB", b"v", b"a"]),
11870 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
11871 );
11872 f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
11874 assert_eq!(
11875 f.run(&[b"VEMB", b"n", b"a"]),
11876 "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
11877 );
11878 f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
11881 assert_eq!(
11882 f.run(&[b"VEMB", b"b", b"a"]),
11883 "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
11884 );
11885 assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
11886 assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
11887
11888 let mut f = Fixture::new();
11892 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
11893 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
11894 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
11895 assert_eq!(
11896 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
11897 "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
11898 $8\r\nopposite\r\n$1\r\n0\r\n"
11899 );
11900 assert_eq!(
11903 f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
11904 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11905 );
11906 assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
11909 assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
11910 assert_eq!(
11913 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
11914 "*1\r\n$6\r\nacross\r\n"
11915 );
11916 assert_eq!(
11917 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
11918 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11919 );
11920 assert_eq!(
11924 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
11925 "*1\r\n$6\r\nacross\r\n"
11926 );
11927
11928 let mut g = Fixture::new();
11931 g.run(&[b"HELLO", b"3"]);
11932 g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11933 assert_eq!(
11934 g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
11935 "%1\r\n$4\r\neast\r\n,1\r\n"
11936 );
11937 }
11938
11939 #[test]
11941 fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
11942 let mut f = Fixture::new();
11943 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11944 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11945 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
11946 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
11947 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
11950 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
11951 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
11953 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11954 assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
11957 assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
11958 assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
11959 assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
11962 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
11963 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
11964 assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
11965
11966 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11968 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11969 assert_eq!(
11970 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
11971 "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
11972 );
11973 }
11974
11975 #[test]
11978 fn vrem_takes_the_attribute_with_it() {
11979 let mut f = Fixture::new();
11980 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11981 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11982 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
11983 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
11984 assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
11985 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
11988
11989 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11992 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11993 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11994 f.run(&[b"VREM", b"v", b"east"]);
11995 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
11996 assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
11997 }
11998
11999 #[test]
12002 fn vinfo_says_partition_first() {
12003 let mut f = Fixture::new();
12004 f.run(&[
12005 b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
12006 ]);
12007 f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
12008 let info = f.run(&[b"VINFO", b"v"]);
12009 assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12010 assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12013 assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12014 assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12015 assert!(
12018 info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12019 "{info}"
12020 );
12021 let mut f = Fixture::new();
12022 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12023 assert!(
12024 f.run(&[b"VINFO", b"v"])
12025 .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12026 );
12027 assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12028 }
12029
12030 fn named() -> Fixture {
12032 let mut f = Fixture::new();
12033 for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12034 .iter()
12035 .enumerate()
12036 {
12037 let x = (i + 1).to_string();
12038 f.run(&[
12039 b"VADD",
12040 b"r",
12041 b"VALUES",
12042 b"2",
12043 x.as_bytes(),
12044 b"1",
12045 name.as_bytes(),
12046 ]);
12047 }
12048 f
12049 }
12050
12051 #[test]
12054 fn vrange_walks_the_names_and_not_the_vectors() {
12055 let mut f = named();
12056 assert_eq!(
12057 f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12058 "*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"
12059 );
12060 assert_eq!(
12061 f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12062 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12063 "the high end is a name and not a prefix, so delta is past it"
12064 );
12065 assert_eq!(
12066 f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12067 "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12068 );
12069 assert_eq!(
12070 f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12071 "*1\r\n$4\r\nbeta\r\n"
12072 );
12073 assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12074 f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12077 assert_eq!(
12078 f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12079 "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12080 );
12081 assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12082 }
12083
12084 #[test]
12087 fn a_vrange_count_of_zero_asks_for_nothing() {
12088 let mut f = named();
12089 assert_eq!(
12090 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12091 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12092 );
12093 assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12094 assert!(
12095 f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12096 .starts_with("*5\r\n"),
12097 "a negative count is no limit at all"
12098 );
12099 }
12100
12101 #[test]
12104 fn vrange_says_which_end_it_could_not_read() {
12105 let mut f = named();
12106 assert_eq!(
12107 f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12108 "-ERR invalid start range format\r\n"
12109 );
12110 assert_eq!(
12111 f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12112 "-ERR invalid end range format\r\n",
12113 "the high end is spelled wrong, which is worth saying before the \
12114 low end being on the wrong side"
12115 );
12116 assert_eq!(
12117 f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12118 "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12119 );
12120 assert_eq!(
12123 f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12124 "-ERR invalid start range format\r\n"
12125 );
12126 assert_eq!(
12127 f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12128 "-ERR invalid COUNT value\r\n"
12129 );
12130 assert_eq!(
12131 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12132 "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12133 );
12134 f.run(&[b"SET", b"s", b"x"]);
12135 assert!(
12136 f.run(&[b"VRANGE", b"s", b"-", b"+"])
12137 .starts_with("-WRONGTYPE")
12138 );
12139 }
12140
12141 #[test]
12144 fn reduce_is_refused_and_not_ignored() {
12145 let mut f = Fixture::new();
12146 let reduce = f.run(&[
12147 b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12148 ]);
12149 assert!(
12150 reduce.starts_with("-ERR REDUCE is not supported."),
12151 "{reduce}"
12152 );
12153 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12154 }
12155
12156 #[test]
12159 fn vsim_filter_reads_the_attributes() {
12160 let mut f = Fixture::new();
12161 for (name, x, y, attr) in [
12162 ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12163 ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12164 ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12165 ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12166 ] {
12167 f.run(&[
12168 b"VADD",
12169 b"v",
12170 b"VALUES",
12171 b"2",
12172 x.as_bytes(),
12173 y.as_bytes(),
12174 name.as_bytes(),
12175 b"SETATTR",
12176 attr.as_bytes(),
12177 ]);
12178 }
12179 assert_eq!(
12182 f.run(&[
12183 b"VSIM",
12184 b"v",
12185 b"VALUES",
12186 b"2",
12187 b"9",
12188 b"1",
12189 b"COUNT",
12190 b"2",
12191 b"FILTER",
12192 b".lang == \"en\"",
12193 ]),
12194 "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12195 );
12196 assert_eq!(
12199 f.run(&[
12200 b"VSIM",
12201 b"v",
12202 b"VALUES",
12203 b"2",
12204 b"9",
12205 b"1",
12206 b"FILTER",
12207 b".lang == 'en' and .year > 1980",
12208 ]),
12209 "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12210 );
12211 assert_eq!(
12213 f.run(&[
12214 b"VSIM",
12215 b"v",
12216 b"VALUES",
12217 b"2",
12218 b"9",
12219 b"1",
12220 b"FILTER",
12221 b".lang in ['fr', 'de']",
12222 ]),
12223 "*1\r\n$1\r\nb\r\n"
12224 );
12225 assert_eq!(
12226 f.run(&[
12227 b"VSIM",
12228 b"v",
12229 b"VALUES",
12230 b"2",
12231 b"9",
12232 b"1",
12233 b"FILTER",
12234 b".rating > 3"
12235 ]),
12236 "*0\r\n"
12237 );
12238 assert_eq!(
12241 f.run(&[
12242 b"VSIM",
12243 b"v",
12244 b"VALUES",
12245 b"2",
12246 b"9",
12247 b"1",
12248 b"TRUTH",
12249 b"FILTER",
12250 b".year < 1980",
12251 ]),
12252 "*1\r\n$1\r\nc\r\n"
12253 );
12254 f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12257 assert_eq!(
12258 f.run(&[
12259 b"VSIM",
12260 b"v",
12261 b"VALUES",
12262 b"2",
12263 b"9",
12264 b"1",
12265 b"COUNT",
12266 b"1",
12267 b"FILTER",
12268 b".lang == \"en\"",
12269 ]),
12270 "*1\r\n$1\r\nb\r\n"
12271 );
12272 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12275 assert_eq!(
12276 f.run(&[
12277 b"VSIM",
12278 b"v",
12279 b"VALUES",
12280 b"2",
12281 b"9",
12282 b"1",
12283 b"COUNT",
12284 b"1",
12285 b"FILTER",
12286 b".lang == \"en\"",
12287 ]),
12288 "*1\r\n$1\r\nb\r\n"
12289 );
12290
12291 let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12294 assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12295 assert_eq!(
12296 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12297 "-ERR invalid FILTER expression\r\n"
12298 );
12299 assert_eq!(
12302 f.run(&[
12303 b"VSIM",
12304 b"v",
12305 b"VALUES",
12306 b"2",
12307 b"9",
12308 b"1",
12309 b"COUNT",
12310 b"1",
12311 b"FILTER-EF",
12312 b"500",
12313 b"FILTER",
12314 b".lang == 'en'",
12315 ]),
12316 "*1\r\n$1\r\nb\r\n"
12317 );
12318 assert_eq!(
12319 f.run(&[
12320 b"VSIM",
12321 b"v",
12322 b"VALUES",
12323 b"2",
12324 b"9",
12325 b"1",
12326 b"COUNT",
12327 b"1",
12328 b"FILTER-EF",
12329 b"0"
12330 ]),
12331 "*1\r\n$1\r\nb\r\n"
12332 );
12333 assert_eq!(
12334 f.run(&[
12335 b"VSIM",
12336 b"v",
12337 b"VALUES",
12338 b"2",
12339 b"9",
12340 b"1",
12341 b"FILTER-EF",
12342 b"lots"
12343 ]),
12344 "-ERR EF must be a positive integer\r\n"
12345 );
12346 }
12347
12348 #[test]
12351 fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12352 let mut f = Fixture::new();
12353 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12354 assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12355 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12356 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12357 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12358 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12359 assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12360 assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12361 assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12362 assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12363 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12364
12365 f.run(&[b"SET", b"s", b"1"]);
12367 assert_eq!(
12368 f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12369 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12370 );
12371 assert_eq!(
12372 f.run(&[b"VCARD", b"s"]),
12373 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12374 );
12375 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12376 assert_eq!(
12377 f.run(&[b"GET", b"v"]),
12378 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12379 );
12380 f.run(&[b"G.NADD", b"social", b"ada"]);
12384 assert_eq!(
12385 f.run(&[b"VCARD", b"social"]),
12386 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12387 );
12388 assert_eq!(
12389 f.run(&[b"G.NGET", b"v", b"ada"]),
12390 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12391 );
12392 }
12393
12394 #[test]
12397 fn vrandmember_has_the_two_shapes_srandmember_has() {
12398 let mut f = Fixture::new();
12399 for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12400 let x = (i + 1).to_string();
12401 f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12402 }
12403 let one = f.run(&[b"VRANDMEMBER", b"v"]);
12405 assert!(one.starts_with("$1\r\n"), "{one}");
12406 let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12408 assert!(all.starts_with("*3\r\n"), "{all}");
12409 for name in ["a", "b", "c"] {
12410 assert!(all.contains(name), "{all} is missing {name}");
12411 }
12412 all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12413 assert!(all.starts_with("*2\r\n"), "{all}");
12414 let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12416 assert!(many.starts_with("*5\r\n"), "{many}");
12417 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12419 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12420 }
12421
12422 #[test]
12425 fn vlinks_reports_one_layer_of_partition_neighbours() {
12426 let mut f = Fixture::new();
12427 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12428 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12429 assert_eq!(
12432 f.run(&[b"VLINKS", b"v", b"east"]),
12433 "*1\r\n*1\r\n$5\r\nnorth\r\n"
12434 );
12435 assert_eq!(
12436 f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12437 "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12438 );
12439 assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12440 assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12441 }
12442
12443 #[test]
12446 fn fp32_and_values_are_the_same_vector() {
12447 let mut f = Fixture::new();
12448 let mut blob = Vec::new();
12449 for x in [3.0f32, 4.0] {
12450 blob.extend_from_slice(&x.to_le_bytes());
12451 }
12452 assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12453 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12454 assert_eq!(
12455 f.run(&[b"VEMB", b"v", b"a"]),
12456 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12457 );
12458 assert_eq!(
12464 f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
12465 "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
12466 );
12467 assert_eq!(
12469 f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12470 "-ERR invalid vector specification\r\n"
12471 );
12472 assert_eq!(
12474 f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12475 "-ERR syntax error\r\n"
12476 );
12477 assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12478 }
12479
12480 #[test]
12485 fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12486 let mut f = Fixture::new();
12487 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12488 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12489 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12490 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12491 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12492 assert_eq!(
12495 f.run(&[b"BF.INFO", b"b"]),
12496 "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12497 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12498 +Expansion rate\r\n:2\r\n"
12499 );
12500 assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12501 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12502 assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12505 assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12506 }
12507
12508 #[test]
12514 fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12515 let mut f = Fixture::new();
12516 f.run(&[b"SET", b"s", b"text"]);
12517 assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12518 assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12519 for cmd in [
12520 vec![&b"BF.ADD"[..], b"s", b"x"],
12521 vec![&b"BF.MADD"[..], b"s", b"x"],
12522 vec![&b"BF.CARD"[..], b"s"],
12523 vec![&b"BF.INFO"[..], b"s"],
12524 vec![&b"BF.DEBUG"[..], b"s"],
12525 vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
12526 ] {
12527 let name = String::from_utf8_lossy(cmd[0]).into_owned();
12528 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12529 }
12530 assert_eq!(
12533 f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
12534 "-ERR bad error rate\r\n"
12535 );
12536 assert!(
12537 f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
12538 .starts_with("-WRONGTYPE")
12539 );
12540 }
12541
12542 #[test]
12545 fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
12546 let mut f = Fixture::new();
12547 assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
12548 for i in 0..10u32 {
12549 assert_eq!(
12550 f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
12551 ":1\r\n"
12552 );
12553 }
12554 assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
12555 assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
12556 assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
12557 assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
12560 assert_eq!(
12561 f.run(&[b"BF.DEBUG", b"g"]),
12562 "*3\r\n$7\r\nsize:11\r\n\
12563 $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
12564 $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
12565 );
12566
12567 assert_eq!(
12569 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
12570 "+OK\r\n"
12571 );
12572 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
12573 assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
12574 assert_eq!(
12575 f.run(&[b"BF.ADD", b"n", b"c"]),
12576 "-ERR non scaling filter is full\r\n"
12577 );
12578 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12581 assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
12584 f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
12585 assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
12586 assert_eq!(
12589 f.run(&[
12590 b"BF.RESERVE",
12591 b"q",
12592 b"0.01",
12593 b"2",
12594 b"NONSCALING",
12595 b"EXPANSION",
12596 b"2"
12597 ]),
12598 "-Nonscaling filters cannot expand\r\n"
12599 );
12600 }
12601
12602 #[test]
12605 fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
12606 let mut f = Fixture::new();
12607 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
12608 assert_eq!(
12609 f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
12610 "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
12611 );
12612 assert_eq!(
12613 f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
12614 "*2\r\n:1\r\n:0\r\n"
12615 );
12616 }
12617
12618 #[test]
12621 fn insert_is_a_reserve_and_a_madd_with_different_errors() {
12622 let mut f = Fixture::new();
12623 assert_eq!(
12624 f.run(&[
12625 b"BF.INSERT",
12626 b"i",
12627 b"CAPACITY",
12628 b"50",
12629 b"ERROR",
12630 b"0.001",
12631 b"ITEMS",
12632 b"a",
12633 b"b"
12634 ]),
12635 "*2\r\n:1\r\n:1\r\n"
12636 );
12637 assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
12638 assert_eq!(
12640 f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
12641 "-ERR not found\r\n"
12642 );
12643 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
12644 assert_eq!(
12647 f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
12648 "-Bad capacity\r\n"
12649 );
12650 assert_eq!(
12651 f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
12652 "-Bad error rate\r\n"
12653 );
12654 assert_eq!(
12655 f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
12656 "-Bad expansion\r\n"
12657 );
12658 assert_eq!(
12663 f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
12664 "*1\r\n:1\r\n"
12665 );
12666 assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
12667 assert_eq!(
12671 f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
12672 "*1\r\n:1\r\n"
12673 );
12674 assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
12675 assert_eq!(
12676 f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
12677 "*1\r\n:1\r\n"
12678 );
12679 assert_eq!(
12680 f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
12681 "-ERR not found\r\n"
12682 );
12683 assert_eq!(
12685 f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12686 "-Unknown argument received\r\n"
12687 );
12688 assert_eq!(
12690 f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12691 "*1\r\n:1\r\n"
12692 );
12693 assert!(
12695 f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
12696 .contains("wrong number of arguments")
12697 );
12698 }
12699
12700 #[test]
12703 fn a_dump_replays_into_a_filter_that_answers_the_same() {
12704 let mut f = Fixture::new();
12705 f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
12706 for i in 0..25u32 {
12707 f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
12708 }
12709 assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
12710
12711 let mut iter = b"0".to_vec();
12714 let mut chunks = 0;
12715 loop {
12716 let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
12717 let text = String::from_utf8_lossy(&raw).into_owned();
12718 let next = text
12719 .split("\r\n")
12720 .nth(1)
12721 .and_then(|n| n.strip_prefix(':'))
12722 .expect("a two element reply of an iterator and a chunk")
12723 .to_owned();
12724 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
12725 let data = &body[body
12726 .windows(2)
12727 .position(|w| w == b"\r\n")
12728 .expect("a length line")
12729 + 2..body.len() - 2];
12730 if next == "0" {
12731 assert!(data.is_empty(), "the last chunk is empty");
12732 break;
12733 }
12734 let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
12735 assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
12736 iter = next.into_bytes();
12737 chunks += 1;
12738 }
12739 assert_eq!(chunks, 3, "a header and one chunk per link");
12740
12741 assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
12742 assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
12743 for i in 0..25u32 {
12744 assert_eq!(
12745 f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
12746 ":1\r\n"
12747 );
12748 }
12749
12750 assert_eq!(
12753 f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
12754 "-ERR received bad data\r\n"
12755 );
12756 assert_eq!(
12757 f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
12758 "-ERR received bad data\r\n"
12759 );
12760 assert_eq!(
12762 f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
12763 "-ERR invalid offset - no link found\r\n"
12764 );
12765 assert_eq!(
12766 f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
12767 "-ERR Second argument must be numeric\r\n"
12768 );
12769 assert_eq!(
12772 f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
12773 "-Second argument must be numeric\r\n"
12774 );
12775 }
12776
12777 #[test]
12780 fn reserve_reads_its_numbers_the_way_string2ll_does() {
12781 let mut f = Fixture::new();
12782 for (args, want) in [
12783 (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
12784 (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
12785 (
12786 vec![&b"0"[..], b"10"],
12787 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12788 ),
12789 (
12790 vec![&b"1"[..], b"10"],
12791 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12792 ),
12793 (
12794 vec![&b"inf"[..], b"10"],
12795 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12796 ),
12797 (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
12798 (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
12799 (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
12800 (
12801 vec![&b"0.01"[..], b"0"],
12802 "-ERR capacity must be in the range [1, 1073741824]\r\n",
12803 ),
12804 (
12805 vec![&b"0.01"[..], b"1073741825"],
12806 "-ERR capacity must be in the range [1, 1073741824]\r\n",
12807 ),
12808 ] {
12809 let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
12810 cmd.extend(args.iter().copied());
12811 assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
12812 }
12813 assert_eq!(
12814 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
12815 "-ERR no expansion\r\n"
12816 );
12817 assert_eq!(
12818 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
12819 "-ERR bad expansion\r\n"
12820 );
12821 assert_eq!(
12822 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
12823 "-ERR expansion must be in the range [0, 32768]\r\n"
12824 );
12825 assert_eq!(
12827 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
12828 "+OK\r\n"
12829 );
12830 assert_eq!(
12831 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
12832 "-ERR item exists\r\n"
12833 );
12834 assert_eq!(
12835 f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
12836 "-Invalid information value\r\n"
12837 );
12838 assert!(
12839 f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
12840 .contains("wrong number of arguments")
12841 );
12842 }
12843
12844 #[test]
12846 fn the_bloom_family_answers_in_resp3_spelling_too() {
12847 let mut f = Fixture::new();
12848 f.out.set_proto(Proto::Resp3);
12849 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
12850 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
12851 assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
12852 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
12853 assert_eq!(
12854 f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
12855 "*2\r\n#t\r\n#f\r\n"
12856 );
12857 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
12859 assert_eq!(
12860 f.run(&[b"BF.INFO", b"b"]),
12861 "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12862 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
12863 +Expansion rate\r\n:2\r\n"
12864 );
12865 assert_eq!(
12868 f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
12869 "%1\r\n+Capacity\r\n:100\r\n"
12870 );
12871 }
12872
12873 fn cf_header(
12882 items: u64,
12883 buckets: u64,
12884 deletes: u64,
12885 filters: u64,
12886 geometry: [u16; 3],
12887 ) -> Vec<u8> {
12888 let mut out = Vec::with_capacity(38);
12889 for n in [items, buckets, deletes, filters] {
12890 out.extend_from_slice(&n.to_le_bytes());
12891 }
12892 for n in geometry {
12893 out.extend_from_slice(&n.to_le_bytes());
12894 }
12895 out
12896 }
12897
12898 #[test]
12902 fn cf_add_makes_the_filter_and_counts_the_copies() {
12903 let mut f = Fixture::new();
12904 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12905 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12906 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
12907 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
12910 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
12911 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
12912 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
12913 assert_eq!(
12914 f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
12915 "*2\r\n:1\r\n:0\r\n"
12916 );
12917 assert_eq!(
12920 f.run(&[b"CF.INFO", b"d"]),
12921 "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
12922 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
12923 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
12924 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
12925 );
12926 assert_eq!(
12927 f.run(&[b"CF.DEBUG", b"d"]),
12928 "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
12929 max_iterations:20 expansion:1\r\n"
12930 );
12931 assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
12932 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
12933
12934 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12936 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
12937 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12938 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
12939 assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
12940
12941 assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
12944 assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
12945 assert_eq!(
12946 f.run(&[b"CF.COMPACT", b"gone"]),
12947 "-Cuckoo filter was not found\r\n"
12948 );
12949 assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
12950 assert!(
12954 f.run(&[b"CF.COMPACT", b"a", b"b"])
12955 .contains("wrong number of arguments")
12956 );
12957 }
12958
12959 #[test]
12962 fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
12963 let mut f = Fixture::new();
12964 f.run(&[b"SET", b"s", b"text"]);
12965 assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
12966 assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12967 assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
12968 assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
12972 assert_eq!(
12973 f.run(&[b"CF.COMPACT", b"s"]),
12974 "-Cuckoo filter was not found\r\n"
12975 );
12976 for cmd in [
12977 vec![&b"CF.ADD"[..], b"s", b"x"],
12978 vec![&b"CF.ADDNX"[..], b"s", b"x"],
12979 vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
12980 vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
12981 vec![&b"CF.INFO"[..], b"s"],
12982 vec![&b"CF.DEBUG"[..], b"s"],
12983 vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
12984 vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
12985 vec![&b"CF.RESERVE"[..], b"s", b"64"],
12986 ] {
12987 let name = String::from_utf8_lossy(cmd[0]).into_owned();
12988 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12989 }
12990 }
12991
12992 #[test]
12995 fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
12996 let mut f = Fixture::new();
12997 assert_eq!(
12998 f.run(&[
12999 b"CF.RESERVE",
13000 b"r",
13001 b"64",
13002 b"BUCKETSIZE",
13003 b"1",
13004 b"MAXITERATIONS",
13005 b"7",
13006 b"EXPANSION",
13007 b"4"
13008 ]),
13009 "+OK\r\n"
13010 );
13011 assert_eq!(
13012 f.run(&[b"CF.DEBUG", b"r"]),
13013 "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13014 max_iterations:7 expansion:4\r\n"
13015 );
13016 assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13017
13018 assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13019 assert_eq!(
13020 f.run(&[b"CF.RESERVE", b"q", b"1"]),
13021 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13022 );
13023 assert_eq!(
13026 f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13027 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13028 );
13029 assert_eq!(
13030 f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13031 "+OK\r\n"
13032 );
13033
13034 assert_eq!(
13039 f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13040 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13041 );
13042 assert_eq!(
13043 f.run(&[
13044 b"CF.RESERVE",
13045 b"q2",
13046 b"64",
13047 b"EXPANSION",
13048 b"xx",
13049 b"BUCKETSIZE",
13050 b"0"
13051 ]),
13052 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13053 );
13054 assert_eq!(
13055 f.run(&[
13056 b"CF.RESERVE",
13057 b"q2",
13058 b"64",
13059 b"MAXITERATIONS",
13060 b"0",
13061 b"BUCKETSIZE",
13062 b"0"
13063 ]),
13064 "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13065 );
13066 assert_eq!(
13070 f.run(&[
13071 b"CF.RESERVE",
13072 b"q3",
13073 b"64",
13074 b"BUCKETSIZE",
13075 b"1",
13076 b"BUCKETSIZE",
13077 b"0"
13078 ]),
13079 "+OK\r\n"
13080 );
13081 assert_eq!(
13084 f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13085 "+OK\r\n"
13086 );
13087 assert_eq!(
13088 f.run(&[b"CF.DEBUG", b"q4"]),
13089 "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13090 max_iterations:20 expansion:1\r\n"
13091 );
13092 assert!(
13095 f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13096 .contains("wrong number of arguments")
13097 );
13098 }
13099
13100 #[test]
13103 fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13104 let mut f = Fixture::new();
13105 assert_eq!(
13106 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13107 "*2\r\n:1\r\n:1\r\n"
13108 );
13109 assert_eq!(
13110 f.run(&[b"CF.DEBUG", b"i"]),
13111 "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13112 max_iterations:20 expansion:1\r\n"
13113 );
13114 assert_eq!(
13117 f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13118 "*2\r\n:0\r\n:1\r\n"
13119 );
13120 assert_eq!(
13121 f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13122 "-ERR not found\r\n"
13123 );
13124 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13125
13126 assert_eq!(
13127 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13128 "-Bad capacity\r\n"
13129 );
13130 assert_eq!(
13133 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13134 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13135 );
13136 assert_eq!(
13140 f.run(&[
13141 b"CF.INSERT",
13142 b"i",
13143 b"CAPACITY",
13144 b"8",
13145 b"CAPACITY",
13146 b"2",
13147 b"ITEMS",
13148 b"a"
13149 ]),
13150 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13151 );
13152 assert_eq!(
13156 f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13157 "*1\r\n:1\r\n"
13158 );
13159 assert_eq!(
13160 f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13161 "*1\r\n:1\r\n"
13162 );
13163 assert_eq!(
13164 f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13165 "-Unknown argument received\r\n"
13166 );
13167 assert_eq!(
13169 f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13170 "*1\r\n:1\r\n"
13171 );
13172 assert!(
13174 f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13175 .contains("wrong number of arguments")
13176 );
13177 assert!(
13178 f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13179 .contains("wrong number of arguments")
13180 );
13181 }
13182
13183 #[test]
13186 fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13187 let mut f = Fixture::new();
13188 f.run(&[
13189 b"CF.RESERVE",
13190 b"s",
13191 b"4",
13192 b"BUCKETSIZE",
13193 b"1",
13194 b"EXPANSION",
13195 b"0",
13196 ]);
13197 for i in 0..4u32 {
13198 assert_eq!(
13199 f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13200 ":1\r\n"
13201 );
13202 }
13203 assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13204 assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13205 assert_eq!(
13208 f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13209 "*2\r\n:-1\r\n:-1\r\n"
13210 );
13211 assert_eq!(
13212 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13213 "*2\r\n:0\r\n:-1\r\n"
13214 );
13215
13216 let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13222 assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13223 assert_eq!(
13224 f.run(&[b"CF.ADD", b"g", b"q"]),
13225 "-Maximum expansions reached\r\n"
13226 );
13227 assert_eq!(
13228 f.run(&[b"CF.INFO", b"g"]),
13229 "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13230 +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13231 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13232 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13233 );
13234 }
13235
13236 #[test]
13240 fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13241 let mut f = Fixture::new();
13242 f.run(&[
13243 b"CF.RESERVE",
13244 b"src",
13245 b"8",
13246 b"BUCKETSIZE",
13247 b"2",
13248 b"EXPANSION",
13249 b"2",
13250 ]);
13251 for i in 0..40u32 {
13252 f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13253 }
13254 let mut pos = b"0".to_vec();
13258 let mut chunks = 0;
13259 loop {
13260 let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13261 let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13262 let next = head
13263 .split("\r\n")
13264 .nth(1)
13265 .and_then(|n| n.strip_prefix(':'))
13266 .expect("a two element reply of a position and a chunk")
13267 .to_owned();
13268 if next == "0" {
13269 assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13270 break;
13271 }
13272 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13273 let at = body
13274 .windows(2)
13275 .position(|w| w == b"\r\n")
13276 .expect("a length line")
13277 + 2;
13278 let data = &body[at..body.len() - 2];
13279 assert_eq!(
13280 f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13281 "+OK\r\n",
13282 "loading chunk {chunks}"
13283 );
13284 pos = next.into_bytes();
13285 chunks += 1;
13286 }
13287 assert!(chunks >= 2, "a header and at least one chunk");
13288
13289 assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13290 assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13291 for i in 0..40u32 {
13292 assert_eq!(
13293 f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13294 ":1\r\n"
13295 );
13296 }
13297
13298 f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13301 assert_eq!(
13302 f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13303 "*2\r\n:0\r\n$-1\r\n"
13304 );
13305
13306 assert_eq!(
13310 f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13311 "-Invalid position\r\n"
13312 );
13313 assert_eq!(
13314 f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13315 "-Invalid position\r\n"
13316 );
13317 assert_eq!(
13318 f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13319 "-Invalid position\r\n"
13320 );
13321 assert_eq!(
13322 f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13323 "-Couldn't load chunk!\r\n"
13324 );
13325 let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13327 assert_eq!(
13328 f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13329 "-ERR item exists\r\n"
13330 );
13331 assert_eq!(
13335 f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13336 "-Invalid header\r\n"
13337 );
13338 for (why, bad) in [
13339 ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13340 ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13341 (
13342 "a bucket count that is not a power of two",
13343 cf_header(0, 3, 0, 1, [2, 20, 1]),
13344 ),
13345 ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13346 ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13347 (
13348 "a growth nobody could reach",
13349 cf_header(0, 8, 0, 1, [2, 20, 32769]),
13350 ),
13351 (
13352 "a chain that cannot grow and did",
13353 cf_header(0, 8, 0, 2, [2, 20, 0]),
13354 ),
13355 (
13358 "a filter count that wraps",
13359 cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13360 ),
13361 ] {
13362 assert_eq!(
13363 f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13364 "-Couldn't create filter!\r\n",
13365 "{why}"
13366 );
13367 }
13368 }
13369
13370 #[test]
13373 fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13374 let mut f = Fixture::new();
13375 f.out.set_proto(Proto::Resp3);
13376 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13377 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13378 assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13379 assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13380 assert_eq!(
13381 f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13382 "*2\r\n#t\r\n#f\r\n"
13383 );
13384 assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13385 assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13386 assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13388 assert_eq!(
13389 f.run(&[b"CF.INFO", b"c"]),
13390 "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13391 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13392 +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13393 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13394 );
13395
13396 f.run(&[
13401 b"CF.RESERVE",
13402 b"s",
13403 b"4",
13404 b"BUCKETSIZE",
13405 b"1",
13406 b"EXPANSION",
13407 b"0",
13408 ]);
13409 assert_eq!(
13410 f.run(&[
13411 b"CF.INSERT",
13412 b"s",
13413 b"ITEMS",
13414 b"a",
13415 b"b",
13416 b"c",
13417 b"d",
13418 b"e",
13419 b"f"
13420 ]),
13421 "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13422 );
13423 assert_eq!(
13424 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13425 "*2\r\n:0\r\n:-1\r\n"
13426 );
13427 assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13428 assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13431 }
13432
13433 #[test]
13438 fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13439 let mut f = Fixture::new();
13440 assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13441 assert_eq!(
13442 f.run(&[b"CMS.INFO", b"d"]),
13443 "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13444 );
13445 assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13446 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13447 assert_eq!(
13450 f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13451 "+OK\r\n"
13452 );
13453 assert_eq!(
13454 f.run(&[b"CMS.INFO", b"p"]),
13455 "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13456 );
13457 assert_eq!(
13460 f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13461 "-CMS: key already exists\r\n"
13462 );
13463 assert_eq!(
13464 f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13465 "-CMS: invalid width\r\n"
13466 );
13467 assert_eq!(
13468 f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13469 "-CMS: invalid depth\r\n"
13470 );
13471 assert_eq!(
13472 f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13473 "-CMS: invalid overestimation value\r\n"
13474 );
13475 assert_eq!(
13476 f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13477 "-CMS: invalid prob value\r\n"
13478 );
13479 assert_eq!(
13483 f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13484 "-CMS: invalid init arguments\r\n"
13485 );
13486 assert_eq!(
13490 f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13491 "-CMS: Insufficient memory to create the key\r\n"
13492 );
13493 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13494 }
13495
13496 #[test]
13499 fn increments_are_parsed_whole_and_the_counters_saturate() {
13500 let mut f = Fixture::new();
13501 f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13502 assert_eq!(
13503 f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13504 "*2\r\n:3\r\n:4\r\n"
13505 );
13506 assert_eq!(
13509 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13510 "*2\r\n:4\r\n:5\r\n"
13511 );
13512 assert_eq!(
13514 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13515 "-CMS: Cannot parse number\r\n"
13516 );
13517 assert_eq!(
13518 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
13519 "-CMS: Number cannot be negative\r\n"
13520 );
13521 assert_eq!(
13522 f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
13523 "*2\r\n:5\r\n:4\r\n"
13524 );
13525 f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
13528 assert_eq!(
13529 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
13530 "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
13531 );
13532 assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
13533 f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
13536 f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
13537 f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
13538 assert_eq!(
13539 f.run(&[b"CMS.INFO", b"w"]),
13540 "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
13541 );
13542 assert!(
13545 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
13546 .contains("wrong number of arguments")
13547 );
13548 assert_eq!(
13549 f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
13550 "-CMS: key does not exist\r\n"
13551 );
13552 assert_eq!(
13553 f.run(&[b"CMS.QUERY", b"nope", b"a"]),
13554 "-CMS: key does not exist\r\n"
13555 );
13556 }
13557
13558 #[test]
13561 fn a_merge_lands_whole_or_not_at_all() {
13562 let mut f = Fixture::new();
13563 for name in [&b"m1"[..], b"m2", b"dst"] {
13564 f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
13565 }
13566 f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
13567 f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
13568 assert_eq!(
13569 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13570 "+OK\r\n"
13571 );
13572 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13573 assert_eq!(
13576 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13577 "+OK\r\n"
13578 );
13579 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13580 assert_eq!(
13581 f.run(&[
13582 b"CMS.MERGE",
13583 b"dst",
13584 b"2",
13585 b"m1",
13586 b"m2",
13587 b"WEIGHTS",
13588 b"2",
13589 b"3"
13590 ]),
13591 "+OK\r\n"
13592 );
13593 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13594 assert_eq!(
13597 f.run(&[
13598 b"CMS.MERGE",
13599 b"dst",
13600 b"1",
13601 b"m1",
13602 b"WEIGHTS",
13603 b"4611686018427387904"
13604 ]),
13605 "-CMS: MERGE overflow\r\n"
13606 );
13607 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13608 f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
13611 assert_eq!(
13612 f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
13613 "-CMS: key does not exist\r\n"
13614 );
13615 assert_eq!(
13616 f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
13617 "-CMS: Number of keys must be positive\r\n"
13618 );
13619 assert_eq!(
13620 f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
13621 "-CMS: wrong number of keys\r\n"
13622 );
13623 assert_eq!(
13624 f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
13625 "-CMS: wrong number of keys/weights\r\n"
13626 );
13627 assert_eq!(
13628 f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
13629 "-CMS: width/depth is not equal\r\n"
13630 );
13631 assert_eq!(
13632 f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
13633 "-CMS: key does not exist\r\n"
13634 );
13635 }
13636
13637 #[test]
13640 fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13641 let mut f = Fixture::new();
13642 f.run(&[b"SET", b"s", b"text"]);
13643 for cmd in [
13644 vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
13645 vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
13646 vec![&b"CMS.QUERY"[..], b"s", b"a"],
13647 vec![&b"CMS.INFO"[..], b"s"],
13648 vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
13649 ] {
13650 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13651 let reply = f.run(&cmd);
13652 assert!(
13655 reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
13656 "{name}: {reply}"
13657 );
13658 }
13659 f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
13660 assert_eq!(
13664 f.run(&[b"COPY", b"c", b"c2"]),
13665 "-ERR not supported for this module key\r\n"
13666 );
13667 assert_eq!(
13668 f.run(&[b"DUMP", b"c"]),
13669 "-ERR DUMP is not supported for this module key\r\n"
13670 );
13671 f.run(&[b"G.NADD", b"g", b"a"]);
13673 assert_eq!(
13674 f.run(&[b"COPY", b"g", b"g2"]),
13675 "-ERR COPY is not supported for a graph\r\n"
13676 );
13677 assert_eq!(
13678 f.run(&[b"DUMP", b"g"]),
13679 "-ERR DUMP is not supported for a graph\r\n"
13680 );
13681 assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
13684 assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
13685 assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
13686 assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
13687 assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
13688 }
13689
13690 #[test]
13695 fn a_reserve_takes_three_arguments_or_six() {
13696 let mut f = Fixture::new();
13697 assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
13698 assert_eq!(
13699 f.run(&[b"TOPK.INFO", b"t"]),
13700 "*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"
13701 );
13702 for cmd in [
13705 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
13706 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
13707 ] {
13708 assert!(f.run(&cmd).contains("wrong number of arguments"));
13709 }
13710 assert_eq!(
13711 f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
13712 "+OK\r\n"
13713 );
13714 assert_eq!(
13717 f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
13718 "-TopK: key already exists\r\n"
13719 );
13720 assert_eq!(
13721 f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
13722 "-TopK: invalid k\r\n"
13723 );
13724 assert_eq!(
13725 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
13726 "-TopK: invalid width\r\n"
13727 );
13728 assert_eq!(
13729 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
13730 "-TopK: invalid depth\r\n"
13731 );
13732 assert_eq!(
13734 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
13735 "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
13736 );
13737 assert_eq!(
13738 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
13739 "+OK\r\n"
13740 );
13741 assert_eq!(
13743 f.run(&[
13744 b"TOPK.RESERVE",
13745 b"w",
13746 b"1",
13747 b"4294967295",
13748 b"4294967295",
13749 b"0.9"
13750 ]),
13751 "-ERR Insufficient memory to create topk data structure\r\n"
13752 );
13753 }
13754
13755 #[test]
13757 fn the_kept_set_is_what_query_and_list_answer_from() {
13758 let mut f = Fixture::new();
13759 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
13760 assert_eq!(
13763 f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
13764 "*2\r\n$-1\r\n$-1\r\n"
13765 );
13766 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
13767 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
13770 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
13771 assert_eq!(
13772 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
13773 "*3\r\n:1\r\n:0\r\n:1\r\n"
13774 );
13775 assert_eq!(
13777 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13778 "*3\r\n:11\r\n:1\r\n:6\r\n"
13779 );
13780 assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
13781 assert_eq!(
13782 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
13783 "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
13784 );
13785 assert_eq!(
13788 f.run(&[b"TOPK.LIST", b"t", b"w"]),
13789 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13790 );
13791 assert_eq!(
13792 f.run(&[b"TOPK.LIST", b"t", b""]),
13793 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13794 );
13795 assert_eq!(
13796 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
13797 "-WITHCOUNT keyword expected\r\n"
13798 );
13799 assert_eq!(
13802 f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
13803 "-WITHCOUNT keyword expected\r\n"
13804 );
13805 assert_eq!(
13806 f.run(&[b"TOPK.LIST", b"missing"]),
13807 "-TopK: key does not exist\r\n"
13808 );
13809 f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
13811 assert_eq!(
13812 f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
13813 "*1\r\n$-1\r\n"
13814 );
13815 assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
13816 assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
13817 }
13818
13819 #[test]
13822 fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
13823 let mut f = Fixture::new();
13824 f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
13825 assert_eq!(
13829 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
13830 format!(
13831 "*2\r\n$-1\r\n-{}\r\n",
13832 "TopK: increment must be an integer greater or equal to 0 and smaller or equal to 100,000"
13833 )
13834 );
13835 assert_eq!(
13836 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13837 "*3\r\n:3\r\n:0\r\n:0\r\n"
13838 );
13839 assert_eq!(
13841 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
13842 "*1\r\n$-1\r\n"
13843 );
13844 assert!(
13845 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
13846 .contains("smaller or equal to 100,000")
13847 );
13848 assert!(
13850 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
13851 .contains("wrong number of arguments")
13852 );
13853 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
13854 }
13855
13856 #[test]
13858 fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
13859 let mut f = Fixture::new();
13860 f.run(&[b"HELLO", b"3"]);
13861 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
13862 f.run(&[b"TOPK.ADD", b"t", b"a"]);
13863 assert_eq!(
13864 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
13865 "*2\r\n#t\r\n#f\r\n"
13866 );
13867 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
13869 assert_eq!(
13870 f.run(&[b"TOPK.INFO", b"t"]),
13871 "%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"
13872 );
13873 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
13874 }
13875
13876 #[test]
13879 fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13880 let mut f = Fixture::new();
13881 f.run(&[b"SET", b"s", b"text"]);
13882 for cmd in [
13883 vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
13884 vec![&b"TOPK.ADD"[..], b"s", b"a"],
13885 vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
13886 vec![&b"TOPK.QUERY"[..], b"s", b"a"],
13887 vec![&b"TOPK.COUNT"[..], b"s", b"a"],
13888 vec![&b"TOPK.LIST"[..], b"s"],
13889 vec![&b"TOPK.INFO"[..], b"s"],
13890 ] {
13891 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13892 let reply = f.run(&cmd);
13893 assert!(
13894 reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
13895 "{name}: {reply}"
13896 );
13897 }
13898 f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
13899 assert_eq!(
13900 f.run(&[b"COPY", b"t", b"t2"]),
13901 "-ERR not supported for this module key\r\n"
13902 );
13903 assert_eq!(
13904 f.run(&[b"DUMP", b"t"]),
13905 "-ERR DUMP is not supported for this module key\r\n"
13906 );
13907 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
13908 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
13909 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
13910 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
13911 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
13912 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
13913 assert_eq!(
13916 f.run(&[b"TOPK.INFO", b"t3"]),
13917 "-TopK: key does not exist\r\n"
13918 );
13919 }
13920
13921 #[test]
13926 fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
13927 let mut f = Fixture::new();
13928 assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
13929 assert_eq!(
13931 f.run(&[b"TDIGEST.INFO", b"t"]),
13932 "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
13933 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
13934 +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
13935 );
13936 assert_eq!(
13937 f.run(&[b"TDIGEST.CREATE", b"t"]),
13938 "-ERR T-Digest: key already exists\r\n"
13939 );
13940 assert!(
13942 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
13943 .contains("wrong number of arguments")
13944 );
13945 assert_eq!(
13946 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
13947 "+OK\r\n"
13948 );
13949 assert_eq!(
13950 f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
13951 "+OK\r\n"
13952 );
13953 assert_eq!(
13957 f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
13958 "-ERR T-Digest: error parsing compression parameter\r\n"
13959 );
13960 assert_eq!(
13961 f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
13962 "-ERR T-Digest: wrong keyword\r\n"
13963 );
13964 assert_eq!(
13965 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
13966 "-ERR T-Digest: error parsing compression parameter\r\n"
13967 );
13968 assert_eq!(
13969 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
13970 "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
13971 );
13972 assert_eq!(
13975 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
13976 "-ERR T-Digest: allocation failed\r\n"
13977 );
13978 assert_eq!(
13980 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
13981 "-ERR T-Digest: allocation failed\r\n"
13982 );
13983 assert_eq!(
13986 f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
13987 "-ERR T-Digest: key already exists\r\n"
13988 );
13989 }
13990
13991 #[test]
13994 fn the_quantile_family_answers_what_the_module_answers() {
13995 let mut f = Fixture::new();
13996 f.run(&[b"TDIGEST.CREATE", b"s"]);
13997 assert_eq!(
13998 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
13999 "+OK\r\n"
14000 );
14001 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
14002 assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
14003 assert_eq!(
14005 f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
14006 "*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"
14007 );
14008 assert_eq!(
14009 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14010 "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14011 );
14012 assert_eq!(
14015 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14016 "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14017 );
14018 assert_eq!(
14019 f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14020 "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14021 );
14022 assert_eq!(
14023 f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14024 "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14025 );
14026 assert_eq!(
14027 f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14028 "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14029 );
14030 assert_eq!(
14031 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14032 "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14033 );
14034 assert_eq!(
14035 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14036 "$3\r\n2.5\r\n"
14037 );
14038 assert_eq!(
14039 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14040 "$3\r\n2.5\r\n"
14041 );
14042 assert_eq!(
14044 f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14045 "-ERR T-Digest: quantile should be in [0,1]\r\n"
14046 );
14047 assert_eq!(
14048 f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14049 "-ERR T-Digest: error parsing quantile\r\n"
14050 );
14051 assert_eq!(
14052 f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14053 "-ERR T-Digest: error parsing cdf\r\n"
14054 );
14055 assert_eq!(
14056 f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14057 "-ERR T-Digest: error parsing value\r\n"
14058 );
14059 assert_eq!(
14060 f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14061 "-ERR T-Digest: rank needs to be non negative\r\n"
14062 );
14063 assert_eq!(
14064 f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14065 "-ERR T-Digest: error parsing rank\r\n"
14066 );
14067 assert_eq!(
14070 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14071 "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14072 );
14073 assert_eq!(
14074 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14075 "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14076 );
14077 assert_eq!(
14078 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14079 "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14080 );
14081 assert_eq!(
14082 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14083 "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14084 );
14085 }
14086
14087 #[test]
14090 fn an_empty_digest_has_an_answer_for_everything() {
14091 let mut f = Fixture::new();
14092 f.run(&[b"TDIGEST.CREATE", b"e"]);
14093 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14094 assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14095 assert_eq!(
14096 f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14097 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14098 );
14099 assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14100 assert_eq!(
14101 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14102 "$3\r\nnan\r\n"
14103 );
14104 assert_eq!(
14107 f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14108 "*2\r\n:-2\r\n:-2\r\n"
14109 );
14110 assert_eq!(
14111 f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14112 "*2\r\n:-2\r\n:-2\r\n"
14113 );
14114 assert_eq!(
14115 f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14116 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14117 );
14118 f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14120 assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14121 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14122 f.run(&[b"TDIGEST.CREATE", b"e2"]);
14125 assert_eq!(
14126 f.run(&[b"TDIGEST.INFO", b"e"]),
14127 f.run(&[b"TDIGEST.INFO", b"e2"])
14128 );
14129 }
14130
14131 #[test]
14134 fn a_sample_is_read_the_way_redis_reads_a_double() {
14135 let mut f = Fixture::new();
14136 f.run(&[b"TDIGEST.CREATE", b"a"]);
14137 for bad in [
14140 &b"nan"[..],
14141 b"1e400",
14142 b"-1e400",
14143 b"1e309",
14144 b"1e-400",
14145 b"",
14146 b" 1",
14147 b"1 ",
14148 b"1e",
14149 b"--1",
14150 ] {
14151 assert_eq!(
14152 f.run(&[b"TDIGEST.ADD", b"a", bad]),
14153 "-ERR T-Digest: error parsing val parameter\r\n",
14154 "{}",
14155 String::from_utf8_lossy(bad)
14156 );
14157 }
14158 for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14161 assert_eq!(
14162 f.run(&[b"TDIGEST.ADD", b"a", word]),
14163 "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14164 "{}",
14165 String::from_utf8_lossy(word)
14166 );
14167 }
14168 for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14171 assert_eq!(
14172 f.run(&[b"TDIGEST.ADD", b"a", good]),
14173 "+OK\r\n",
14174 "{}",
14175 String::from_utf8_lossy(good)
14176 );
14177 }
14178 assert!(
14180 f.run(&[b"TDIGEST.INFO", b"a"])
14181 .contains("Observations\r\n:6\r\n")
14182 );
14183 assert_eq!(
14186 f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14187 "-ERR T-Digest: error parsing val parameter\r\n"
14188 );
14189 assert!(
14190 f.run(&[b"TDIGEST.INFO", b"a"])
14191 .contains("Observations\r\n:6\r\n")
14192 );
14193 }
14194
14195 #[test]
14198 fn a_merge_sweeps_the_destination_between_its_inputs() {
14199 let mut f = Fixture::new();
14200 f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14201 f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14202 f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14203 f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14204 assert_eq!(
14205 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14206 "+OK\r\n"
14207 );
14208 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14213 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14214 assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14215 assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14216 assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14217 assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14218 assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14219 assert!(
14222 f.run(&[b"TDIGEST.INFO", b"m1"])
14223 .contains("Merged nodes\r\n:3\r\n")
14224 );
14225 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14228 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14229 assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14230 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14231 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14234 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14235 assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14236 assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14237 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14239 assert!(
14240 f.run(&[b"TDIGEST.INFO", b"d"])
14241 .contains("Compression\r\n:500\r\n")
14242 );
14243 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14245 assert!(
14246 f.run(&[b"TDIGEST.INFO", b"d"])
14247 .contains("Observations\r\n:12\r\n")
14248 );
14249 assert_eq!(
14251 f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14252 "-ERR T-Digest: error parsing numkeys\r\n"
14253 );
14254 assert_eq!(
14255 f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14256 "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14257 );
14258 assert!(
14259 f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14260 .contains("wrong number of arguments")
14261 );
14262 assert!(
14263 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14264 .contains("wrong number of arguments")
14265 );
14266 assert_eq!(
14267 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14268 "-ERR T-Digest: wrong keyword\r\n"
14269 );
14270 assert_eq!(
14273 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14274 "-ERR T-Digest: key does not exist\r\n"
14275 );
14276 assert!(
14277 f.run(&[b"TDIGEST.INFO", b"d"])
14278 .contains("Observations\r\n:12\r\n")
14279 );
14280 assert_eq!(
14283 f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14284 "-ERR T-Digest: key does not exist\r\n"
14285 );
14286 }
14287
14288 #[test]
14290 fn a_digest_answers_doubles_and_a_map_on_resp3() {
14291 let mut f = Fixture::new();
14292 f.run(&[b"HELLO", b"3"]);
14293 f.run(&[b"TDIGEST.CREATE", b"s"]);
14294 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14295 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14296 assert_eq!(
14297 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14298 "*2\r\n,1\r\n,4\r\n"
14299 );
14300 assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14301 assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14303 assert_eq!(
14304 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14305 "*1\r\n,-inf\r\n"
14306 );
14307 f.run(&[b"TDIGEST.CREATE", b"e"]);
14308 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14309 assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14311 assert_eq!(
14314 f.run(&[b"TDIGEST.INFO", b"s"]),
14315 "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14316 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14317 +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14318 );
14319 }
14320
14321 #[test]
14324 fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14325 let mut f = Fixture::new();
14326 f.run(&[b"SET", b"s", b"text"]);
14327 for cmd in [
14328 vec![&b"TDIGEST.CREATE"[..], b"s"],
14329 vec![&b"TDIGEST.RESET"[..], b"s"],
14330 vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14331 vec![&b"TDIGEST.MIN"[..], b"s"],
14332 vec![&b"TDIGEST.MAX"[..], b"s"],
14333 vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14334 vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14335 vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14336 vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14337 vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14338 vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14339 vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14340 vec![&b"TDIGEST.INFO"[..], b"s"],
14341 ] {
14342 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14343 let reply = f.run(&cmd);
14344 assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14345 }
14346 f.run(&[b"TDIGEST.CREATE", b"t"]);
14348 assert!(
14349 f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14350 .starts_with("-WRONGTYPE")
14351 );
14352 assert!(
14353 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14354 .starts_with("-WRONGTYPE")
14355 );
14356 assert_eq!(
14357 f.run(&[b"COPY", b"t", b"t2"]),
14358 "-ERR not supported for this module key\r\n"
14359 );
14360 assert_eq!(
14361 f.run(&[b"DUMP", b"t"]),
14362 "-ERR DUMP is not supported for this module key\r\n"
14363 );
14364 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14365 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14366 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14367 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14368 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14369 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14370 assert_eq!(
14373 f.run(&[b"TDIGEST.INFO", b"t3"]),
14374 "-ERR T-Digest: key does not exist\r\n"
14375 );
14376 assert_eq!(
14379 f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14380 "-ERR T-Digest: key does not exist\r\n"
14381 );
14382 }
14383
14384 fn without_memory(reply: &str) -> String {
14394 let head = "+memoryUsage\r\n:";
14395 let at = reply.find(head).expect("every TS.INFO reports memory");
14396 let rest = &reply[at + head.len()..];
14397 let end = rest.find("\r\n").expect("and it is a whole number");
14398 format!("{}{}", &reply[..at + head.len()], &rest[end..])
14399 }
14400
14401 #[test]
14404 fn a_series_is_made_empty_and_reports_on_itself() {
14405 let mut f = Fixture::new();
14406 assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
14407 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14408 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
14409 assert_eq!(
14413 without_memory(&f.run(&[b"TS.INFO", b"t"])),
14414 "*28\r\n\
14415 +totalSamples\r\n:0\r\n\
14416 +memoryUsage\r\n:\r\n\
14417 +firstTimestamp\r\n:0\r\n\
14418 +lastTimestamp\r\n:0\r\n\
14419 +retentionTime\r\n:0\r\n\
14420 +chunkCount\r\n:1\r\n\
14421 +chunkSize\r\n:4096\r\n\
14422 +chunkType\r\n+compressed\r\n\
14423 +duplicatePolicy\r\n+block\r\n\
14424 +labels\r\n*0\r\n\
14425 +sourceKey\r\n$-1\r\n\
14426 +rules\r\n*0\r\n\
14427 +ignoreMaxTimeDiff\r\n:0\r\n\
14428 +ignoreMaxValDiff\r\n$1\r\n0\r\n"
14429 );
14430 assert_eq!(
14433 f.run(&[b"TS.CREATE", b"t"]),
14434 "-ERR TSDB: key already exists\r\n"
14435 );
14436 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14437 assert_eq!(
14438 f.run(&[b"TS.CREATE", b"str"]),
14439 "-ERR TSDB: key already exists\r\n"
14440 );
14441 assert_eq!(
14444 f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
14445 "-ERR TSDB: Couldn't parse RETENTION\r\n"
14446 );
14447 let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14453 assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
14454 assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
14455 assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
14456 assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
14457 assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
14458 assert_eq!(
14459 f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
14460 "-ERR TSDB: the key is not a TSDB key\r\n"
14461 );
14462 assert_eq!(
14464 f.run(&[b"TS.INFO", b"nope"]),
14465 "-ERR TSDB: the key does not exist\r\n"
14466 );
14467 assert_eq!(
14468 f.run(&[b"TS.GET", b"nope"]),
14469 "-ERR TSDB: the key does not exist\r\n"
14470 );
14471 assert_eq!(
14472 f.run(&[b"TS.ALTER", b"nope"]),
14473 "-ERR TSDB: the key does not exist\r\n"
14474 );
14475 assert_eq!(
14476 f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
14477 "-ERR TSDB: the key does not exist\r\n"
14478 );
14479 }
14480
14481 #[test]
14484 fn the_options_are_a_keyword_scan_and_not_a_grammar() {
14485 let mut f = Fixture::new();
14486 assert_eq!(
14487 f.run(&[
14488 b"TS.CREATE",
14489 b"t",
14490 b"RETENTION",
14491 b"5000",
14492 b"ENCODING",
14493 b"UNCOMPRESSED",
14494 b"CHUNK_SIZE",
14495 b"128",
14496 b"DUPLICATE_POLICY",
14497 b"LAST",
14498 b"IGNORE",
14499 b"10",
14500 b"0.5",
14501 b"LABELS",
14502 b"room",
14503 b"kitchen"
14504 ]),
14505 "+OK\r\n"
14506 );
14507 let info = f.run(&[b"TS.INFO", b"t"]);
14508 assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
14509 assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
14510 assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
14511 assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
14512 assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
14513 assert!(
14516 info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
14517 "{info}"
14518 );
14519 assert!(
14520 info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
14521 "{info}"
14522 );
14523
14524 assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
14526 assert_eq!(
14530 f.run(&[
14531 b"TS.CREATE",
14532 b"g",
14533 b"LABELS",
14534 b"a",
14535 b"b",
14536 b"RETENTION",
14537 b"5"
14538 ]),
14539 "+OK\r\n"
14540 );
14541 let greedy = f.run(&[b"TS.INFO", b"g"]);
14542 assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
14543 assert!(
14544 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"),
14545 "{greedy}"
14546 );
14547
14548 assert_eq!(
14550 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
14551 "-ERR TSDB: Couldn't parse LABELS\r\n"
14552 );
14553 assert_eq!(
14554 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
14555 "-ERR TSDB: Couldn't parse LABELS\r\n"
14556 );
14557 assert_eq!(
14558 f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
14559 "-ERR TSDB: Couldn't parse RETENTION\r\n"
14560 );
14561 assert_eq!(
14564 f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
14565 "-TSDB: Couldn't parse RETENTION\r\n"
14566 );
14567 assert_eq!(
14568 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
14569 "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
14570 );
14571 assert_eq!(
14572 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
14573 "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
14574 );
14575 assert_eq!(
14576 f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
14577 "-ERR TSDB: unknown ENCODING parameter\r\n"
14578 );
14579 assert!(
14582 f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
14583 .contains("wrong number of arguments for 'ts.create' command")
14584 );
14585 assert_eq!(
14586 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
14587 "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
14588 );
14589 assert_eq!(
14590 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
14591 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14592 );
14593 assert_eq!(
14594 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
14595 "-ERR TSDB: Couldn't parse IGNORE\r\n"
14596 );
14597 assert_eq!(
14598 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
14599 "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
14600 );
14601 assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
14602
14603 assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
14606 let after = f.run(&[b"TS.INFO", b"t"]);
14607 assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
14608 assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
14609 assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
14610 assert_eq!(
14611 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
14612 "-ERR TSDB: unknown ENCODING parameter\r\n"
14613 );
14614 assert_eq!(
14616 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
14617 "+OK\r\n"
14618 );
14619 assert!(
14620 f.run(&[b"TS.INFO", b"t"])
14621 .contains("+chunkType\r\n+uncompressed\r\n")
14622 );
14623 }
14624
14625 #[test]
14628 fn samples_land_where_they_are_put_and_the_newest_comes_back() {
14629 let mut f = Fixture::new();
14630 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
14631 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14633 assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
14634 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
14637 assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
14638 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
14639 assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
14642 assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
14643
14644 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14647 assert_eq!(
14648 f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
14649 "-ERR TSDB: invalid value\r\n"
14650 );
14651 for bad in [
14655 &b".5"[..],
14656 b"1.",
14657 b"+1",
14658 b" 1",
14659 b"0x10",
14660 b"inf",
14661 b"1e400",
14662 b"--1",
14663 b"1e",
14664 ] {
14665 assert_eq!(
14666 f.run(&[b"TS.ADD", b"v", b"1", bad]),
14667 "-ERR TSDB: invalid value\r\n",
14668 "{}",
14669 String::from_utf8_lossy(bad)
14670 );
14671 }
14672 assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
14674
14675 assert_eq!(
14678 f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
14679 "-ERR TSDB: invalid timestamp\r\n"
14680 );
14681 assert_eq!(
14682 f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
14683 "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
14684 );
14685
14686 assert_eq!(
14689 f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
14690 "-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"
14691 );
14692 assert_eq!(
14693 f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
14694 ":300\r\n"
14695 );
14696 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
14697 assert_eq!(
14700 f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
14701 ":1\r\n"
14702 );
14703 assert_eq!(
14704 f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
14705 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14706 );
14707
14708 assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
14711 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
14712 assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
14713 assert_eq!(
14714 f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
14715 "-ERR TSDB: Timestamp is older than retention\r\n"
14716 );
14717 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
14719 assert!(
14720 f.run(&[b"TS.INFO", b"r"])
14721 .contains("+totalSamples\r\n:1\r\n")
14722 );
14723
14724 assert_eq!(
14727 f.run(&[
14728 b"TS.CREATE",
14729 b"i",
14730 b"DUPLICATE_POLICY",
14731 b"LAST",
14732 b"IGNORE",
14733 b"10",
14734 b"0.5"
14735 ]),
14736 "+OK\r\n"
14737 );
14738 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
14739 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
14740 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
14741 }
14742
14743 #[test]
14746 fn a_madd_answers_each_triple_and_creates_nothing() {
14747 let mut f = Fixture::new();
14748 assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
14749 assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
14750 assert_eq!(
14751 f.run(&[
14752 b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
14753 ]),
14754 "*3\r\n:100\r\n:100\r\n:200\r\n"
14755 );
14756 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14759 assert_eq!(
14760 f.run(&[
14761 b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
14762 ]),
14763 "*3\r\n\
14764 -ERR TSDB: the key is not a TSDB key\r\n\
14765 -ERR TSDB: the key is not a TSDB key\r\n\
14766 :300\r\n"
14767 );
14768 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
14769 assert_eq!(
14771 f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
14772 "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
14773 );
14774 assert!(
14776 f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
14777 .contains("wrong number of arguments for 'ts.madd' command")
14778 );
14779 }
14780
14781 #[test]
14783 fn an_increment_walks_the_newest_value_up_and_down() {
14784 let mut f = Fixture::new();
14785 assert_eq!(
14786 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
14787 ":100\r\n"
14788 );
14789 assert_eq!(
14790 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
14791 ":100\r\n"
14792 );
14793 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
14796 assert_eq!(
14797 f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
14798 ":200\r\n"
14799 );
14800 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
14801 assert_eq!(
14804 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
14805 "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
14806 );
14807 assert_eq!(
14811 f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
14812 ":1\r\n"
14813 );
14814 assert_eq!(
14815 f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
14816 ":1\r\n"
14817 );
14818 assert_eq!(
14819 f.run(&[b"TS.INCRBY", b"t", b"nan"]),
14820 "-ERR TSDB: invalid increase/decrease value\r\n"
14821 );
14822 assert_eq!(
14823 f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
14824 "-ERR TSDB: invalid increase/decrease value\r\n"
14825 );
14826 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14829 assert_eq!(
14830 f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
14831 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
14832 );
14833 assert_eq!(
14838 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
14839 "-ERR TSDB: invalid timestamp\r\n"
14840 );
14841 assert_eq!(
14844 f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
14845 format!(":{}\r\n", f.server.now_ms())
14846 );
14847 assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
14849 assert_eq!(
14850 f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
14851 "-ERR TSDB: cannot increment/decrement NaN value\r\n"
14852 );
14853 }
14854
14855 #[test]
14857 fn deleting_takes_out_a_span_and_answers_how_many_went() {
14858 let mut f = Fixture::new();
14859 for at in [b"100".as_slice(), b"200", b"300", b"400"] {
14860 f.run(&[b"TS.ADD", b"t", at, b"1"]);
14861 }
14862 assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
14863 assert!(
14864 f.run(&[b"TS.INFO", b"t"])
14865 .contains("+totalSamples\r\n:2\r\n")
14866 );
14867 assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
14869 assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
14871 let empty = f.run(&[b"TS.INFO", b"t"]);
14874 assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
14875 assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
14876 assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
14877 assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
14878 assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
14879 assert_eq!(
14881 f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
14882 "-ERR TSDB: wrong fromTimestamp\r\n"
14883 );
14884 assert_eq!(
14885 f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
14886 "-ERR TSDB: wrong toTimestamp\r\n"
14887 );
14888 assert_eq!(
14889 f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
14890 "-ERR TSDB: wrong fromTimestamp\r\n"
14891 );
14892 }
14893
14894 #[test]
14897 fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
14898 let mut f = Fixture::new();
14899 f.out = Out::new(Proto::Resp3);
14900 assert_eq!(
14901 f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
14902 "+OK\r\n"
14903 );
14904 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
14905 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
14907 assert_eq!(
14908 without_memory(&f.run(&[b"TS.INFO", b"t"])),
14909 "%14\r\n\
14910 +totalSamples\r\n:1\r\n\
14911 +memoryUsage\r\n:\r\n\
14912 +firstTimestamp\r\n:100\r\n\
14913 +lastTimestamp\r\n:100\r\n\
14914 +retentionTime\r\n:0\r\n\
14915 +chunkCount\r\n:1\r\n\
14916 +chunkSize\r\n:4096\r\n\
14917 +chunkType\r\n+compressed\r\n\
14918 +duplicatePolicy\r\n+block\r\n\
14919 +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
14920 +sourceKey\r\n_\r\n\
14921 +rules\r\n%0\r\n\
14922 +ignoreMaxTimeDiff\r\n:0\r\n\
14923 +ignoreMaxValDiff\r\n,0\r\n"
14924 );
14925 }
14926
14927 #[test]
14930 fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
14931 let mut f = Fixture::new();
14932 for (at, v) in [
14933 (b"100".as_slice(), b"1".as_slice()),
14934 (b"200", b"2"),
14935 (b"300", b"3"),
14936 (b"400", b"4"),
14937 ] {
14938 f.run(&[b"TS.ADD", b"t", at, v]);
14939 }
14940 assert_eq!(
14941 f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
14942 "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
14943 *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
14944 );
14945 assert_eq!(
14947 f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
14948 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
14949 );
14950 assert_eq!(
14953 f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
14954 "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
14955 );
14956 assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
14958 assert_eq!(
14960 f.run(&[
14961 b"TS.RANGE",
14962 b"t",
14963 b"-",
14964 b"+",
14965 b"FILTER_BY_VALUE",
14966 b"2",
14967 b"3"
14968 ]),
14969 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
14970 );
14971 assert_eq!(
14972 f.run(&[
14973 b"TS.RANGE",
14974 b"t",
14975 b"-",
14976 b"+",
14977 b"FILTER_BY_TS",
14978 b"100",
14979 b"400"
14980 ]),
14981 "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
14982 );
14983 assert_eq!(
14985 f.run(&[
14986 b"TS.RANGE",
14987 b"t",
14988 b"-",
14989 b"+",
14990 b"ZZZ",
14991 b"FILTER_BY_TS",
14992 b"400"
14993 ]),
14994 "*1\r\n*2\r\n:400\r\n+4\r\n"
14995 );
14996 assert_eq!(
14998 f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
14999 "*1\r\n*2\r\n:100\r\n+1\r\n"
15000 );
15001 }
15002
15003 #[test]
15005 fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
15006 let mut f = Fixture::new();
15007 for (at, v) in [
15008 (b"100".as_slice(), b"1".as_slice()),
15009 (b"200", b"2"),
15010 (b"300", b"3"),
15011 (b"400", b"4"),
15012 ] {
15013 f.run(&[b"TS.ADD", b"t", at, v]);
15014 }
15015 assert_eq!(
15016 f.run(&[
15017 b"TS.RANGE",
15018 b"t",
15019 b"-",
15020 b"+",
15021 b"AGGREGATION",
15022 b"avg",
15023 b"200"
15024 ]),
15025 "*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"
15026 );
15027 assert_eq!(
15030 f.run(&[
15031 b"TS.RANGE",
15032 b"t",
15033 b"-",
15034 b"+",
15035 b"AGGREGATION",
15036 b"min,max,count",
15037 b"200"
15038 ]),
15039 "*3\r\n\
15040 *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15041 *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15042 *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15043 );
15044 assert_eq!(
15046 f.run(&[
15047 b"TS.RANGE",
15048 b"t",
15049 b"-",
15050 b"+",
15051 b"AGGREGATION",
15052 b"avg",
15053 b"200",
15054 b"BUCKETTIMESTAMP",
15055 b"+"
15056 ]),
15057 "*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"
15058 );
15059 assert_eq!(
15061 f.run(&[
15062 b"TS.RANGE",
15063 b"t",
15064 b"100",
15065 b"400",
15066 b"ALIGN",
15067 b"100",
15068 b"AGGREGATION",
15069 b"sum",
15070 b"200"
15071 ]),
15072 "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15073 );
15074 assert_eq!(
15077 f.run(&[
15078 b"TS.RANGE",
15079 b"t",
15080 b"-",
15081 b"+",
15082 b"AGGREGATION",
15083 b"count",
15084 b"200"
15085 ]),
15086 "*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"
15087 );
15088 assert_eq!(
15089 f.run(&[
15090 b"TS.RANGE",
15091 b"t",
15092 b"-",
15093 b"+",
15094 b"AGGREGATION",
15095 b"count",
15096 b"200",
15097 b"COUNT",
15098 b"1"
15099 ]),
15100 "*1\r\n*2\r\n:0\r\n+1\r\n"
15101 );
15102 }
15103
15104 #[test]
15107 fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15108 let mut f = Fixture::new();
15109 for (at, v) in [
15110 (b"0".as_slice(), b"1".as_slice()),
15111 (b"100", b"2"),
15112 (b"500", b"nan"),
15113 (b"600", b"3"),
15114 ] {
15115 f.run(&[b"TS.ADD", b"g", at, v]);
15116 }
15117 assert_eq!(
15120 f.run(&[
15121 b"TS.RANGE",
15122 b"g",
15123 b"-",
15124 b"+",
15125 b"AGGREGATION",
15126 b"avg",
15127 b"100"
15128 ]),
15129 "*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"
15130 );
15131 assert_eq!(
15133 f.run(&[
15134 b"TS.RANGE",
15135 b"g",
15136 b"-",
15137 b"+",
15138 b"AGGREGATION",
15139 b"sum",
15140 b"100",
15141 b"EMPTY"
15142 ]),
15143 "*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\
15144 *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\
15145 *2\r\n:600\r\n+3\r\n"
15146 );
15147 assert_eq!(
15152 f.run(&[
15153 b"TS.RANGE",
15154 b"g",
15155 b"-",
15156 b"+",
15157 b"AGGREGATION",
15158 b"last",
15159 b"100",
15160 b"EMPTY"
15161 ]),
15162 "*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\
15163 *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\
15164 *2\r\n:600\r\n+3\r\n"
15165 );
15166 assert_eq!(
15167 f.run(&[
15168 b"TS.REVRANGE",
15169 b"g",
15170 b"-",
15171 b"+",
15172 b"AGGREGATION",
15173 b"last",
15174 b"100",
15175 b"EMPTY"
15176 ]),
15177 "*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\
15178 *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\
15179 *2\r\n:0\r\n+1\r\n"
15180 );
15181 assert_eq!(
15184 f.run(&[
15185 b"TS.RANGE",
15186 b"g",
15187 b"500",
15188 b"600",
15189 b"AGGREGATION",
15190 b"last",
15191 b"100",
15192 b"EMPTY"
15193 ]),
15194 "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15195 );
15196 }
15197
15198 #[test]
15201 fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15202 let mut f = Fixture::new();
15203 f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15204 f.run(&[b"SET", b"str", b"x"]);
15205 let cases: &[(&[&[u8]], &str)] = &[
15206 (
15207 &[b"TS.RANGE", b"t"],
15208 "-ERR wrong number of arguments for 'ts.range' command\r\n",
15209 ),
15210 (
15212 &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15213 "-ERR TSDB: the key does not exist\r\n",
15214 ),
15215 (
15216 &[b"TS.RANGE", b"str", b"-", b"+"],
15217 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15218 ),
15219 (
15220 &[b"TS.RANGE", b"t", b"abc", b"+"],
15221 "-ERR TSDB: wrong fromTimestamp\r\n",
15222 ),
15223 (
15224 &[b"TS.RANGE", b"t", b"-", b"abc"],
15225 "-ERR TSDB: wrong toTimestamp\r\n",
15226 ),
15227 (
15228 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15229 "-ERR TSDB: COUNT argument is missing\r\n",
15230 ),
15231 (
15232 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15233 "-ERR TSDB: Couldn't parse COUNT\r\n",
15234 ),
15235 (
15236 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15237 "-ERR TSDB: Invalid COUNT value\r\n",
15238 ),
15239 (
15240 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15241 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15242 ),
15243 (
15244 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15245 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15246 ),
15247 (
15248 &[
15249 b"TS.RANGE",
15250 b"t",
15251 b"-",
15252 b"+",
15253 b"AGGREGATION",
15254 b"nope",
15255 b"100",
15256 ],
15257 "-ERR TSDB: Unknown aggregation type\r\n",
15258 ),
15259 (
15260 &[
15261 b"TS.RANGE",
15262 b"t",
15263 b"-",
15264 b"+",
15265 b"AGGREGATION",
15266 b"avg,,min",
15267 b"100",
15268 ],
15269 "-ERR TSDB: Empty aggregation type in list\r\n",
15270 ),
15271 (
15273 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15274 "-ERR TSDB: Unknown aggregation type\r\n",
15275 ),
15276 (
15277 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15278 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15279 ),
15280 (
15281 &[
15282 b"TS.RANGE",
15283 b"t",
15284 b"-",
15285 b"+",
15286 b"AGGREGATION",
15287 b"avg",
15288 b"100",
15289 b"X",
15290 b"EMPTY",
15291 ],
15292 "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15293 ),
15294 (
15295 &[
15296 b"TS.RANGE",
15297 b"t",
15298 b"-",
15299 b"+",
15300 b"AGGREGATION",
15301 b"avg",
15302 b"100",
15303 b"BUCKETTIMESTAMP",
15304 b"z",
15305 ],
15306 "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15307 ),
15308 (
15309 &[
15310 b"TS.RANGE",
15311 b"t",
15312 b"-",
15313 b"+",
15314 b"AGGREGATION",
15315 b"avg",
15316 b"100",
15317 b"X",
15318 b"Y",
15319 b"BUCKETTIMESTAMP",
15320 b"-",
15321 ],
15322 "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15323 AGGREGATION flag\r\n",
15324 ),
15325 (
15326 &[
15327 b"TS.RANGE",
15328 b"t",
15329 b"-",
15330 b"+",
15331 b"ALIGN",
15332 b"z",
15333 b"AGGREGATION",
15334 b"avg",
15335 b"100",
15336 ],
15337 "-ERR TSDB: unknown ALIGN parameter\r\n",
15338 ),
15339 (
15340 &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15341 "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15342 ),
15343 (
15344 &[
15345 b"TS.RANGE",
15346 b"t",
15347 b"-",
15348 b"+",
15349 b"ALIGN",
15350 b"-",
15351 b"AGGREGATION",
15352 b"avg",
15353 b"100",
15354 ],
15355 "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15356 ),
15357 (
15358 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15359 "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15360 ),
15361 (
15362 &[
15363 b"TS.RANGE",
15364 b"t",
15365 b"-",
15366 b"+",
15367 b"FILTER_BY_VALUE",
15368 b"x",
15369 b"2",
15370 ],
15371 "-ERR TSDB: Couldn't parse MIN\r\n",
15372 ),
15373 (
15374 &[
15375 b"TS.RANGE",
15376 b"t",
15377 b"-",
15378 b"+",
15379 b"FILTER_BY_VALUE",
15380 b"1",
15381 b"y",
15382 ],
15383 "-ERR TSDB: Couldn't parse MAX\r\n",
15384 ),
15385 (
15386 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15387 "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15388 ),
15389 ];
15390 for (argv, want) in cases {
15391 let got = f.run(argv);
15392 assert_eq!(&got, want, "{:?}", argv.last());
15393 }
15394 f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
15398 f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
15399 assert_eq!(
15400 f.run(&[
15401 b"TS.RANGE",
15402 b"wide",
15403 b"-",
15404 b"+",
15405 b"AGGREGATION",
15406 b"avg",
15407 b"1",
15408 b"EMPTY"
15409 ]),
15410 "-ERR TSDB: the requested range holds too many empty buckets\r\n"
15411 );
15412 }
15413
15414 #[test]
15416 fn resp3_writes_a_read_value_as_a_double() {
15417 let mut f = Fixture::new();
15418 f.out = Out::new(Proto::Resp3);
15419 for (at, v) in [
15420 (b"0".as_slice(), b"1".as_slice()),
15421 (b"100", b"2"),
15422 (b"500", b"nan"),
15423 (b"600", b"3"),
15424 ] {
15425 f.run(&[b"TS.ADD", b"g", at, v]);
15426 }
15427 assert_eq!(
15428 f.run(&[
15429 b"TS.RANGE",
15430 b"g",
15431 b"0",
15432 b"100",
15433 b"AGGREGATION",
15434 b"avg,min",
15435 b"200"
15436 ]),
15437 "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
15438 );
15439 assert_eq!(
15440 f.run(&[
15441 b"TS.RANGE",
15442 b"g",
15443 b"500",
15444 b"600",
15445 b"AGGREGATION",
15446 b"last",
15447 b"100",
15448 b"EMPTY"
15449 ]),
15450 "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
15451 );
15452 }
15453
15454 fn joined() -> Fixture {
15457 let mut f = Fixture::new();
15458 f.run(&[b"TS.CREATE", b"z"]);
15459 for (at, v) in [
15460 (b"10".as_slice(), b"1".as_slice()),
15461 (b"20", b"2"),
15462 (b"40", b"4"),
15463 (b"50", b"5"),
15464 ] {
15465 f.run(&[b"TS.ADD", b"x", at, v]);
15466 }
15467 for (at, v) in [
15468 (b"20".as_slice(), b"20".as_slice()),
15469 (b"30", b"30"),
15470 (b"50", b"50"),
15471 (b"60", b"60"),
15472 ] {
15473 f.run(&[b"TS.ADD", b"y", at, v]);
15474 }
15475 f
15476 }
15477
15478 #[test]
15482 fn an_nrange_joins_its_keys_on_the_timestamp() {
15483 let mut f = joined();
15484 assert_eq!(
15486 f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
15487 "*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\
15488 *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
15489 );
15490 assert_eq!(
15492 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
15493 "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
15494 *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15495 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15496 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15497 *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
15498 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15499 );
15500 assert_eq!(
15503 f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
15504 "*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"
15505 );
15506 assert_eq!(
15507 f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
15508 "*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"
15509 );
15510 assert_eq!(
15513 f.run(&[
15514 b"TS.NREVRANGE",
15515 b"2",
15516 b"x",
15517 b"y",
15518 b"-",
15519 b"+",
15520 b"COUNT",
15521 b"1"
15522 ]),
15523 "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15524 );
15525 assert_eq!(
15526 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
15527 "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
15528 );
15529 assert_eq!(
15531 f.run(&[
15532 b"TS.NRANGE",
15533 b"2",
15534 b"x",
15535 b"y",
15536 b"-",
15537 b"+",
15538 b"FILTER_BY_VALUE",
15539 b"2",
15540 b"30"
15541 ]),
15542 "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15543 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15544 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15545 *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
15546 );
15547 }
15548
15549 #[test]
15553 fn an_nrange_aggregation_names_one_reduction_a_key() {
15554 let mut f = joined();
15555 assert_eq!(
15556 f.run(&[
15557 b"TS.NRANGE",
15558 b"2",
15559 b"x",
15560 b"y",
15561 b"-",
15562 b"+",
15563 b"AGGREGATION",
15564 b"sum",
15565 b"sum",
15566 b"20"
15567 ]),
15568 "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
15569 *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
15570 *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
15571 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15572 );
15573 assert_eq!(
15575 f.run(&[
15576 b"TS.NRANGE",
15577 b"2",
15578 b"x",
15579 b"y",
15580 b"-",
15581 b"+",
15582 b"AGGREGATION",
15583 b"sum,count",
15584 b"avg",
15585 b"20"
15586 ]),
15587 "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
15588 *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
15589 *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
15590 *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
15591 );
15592 assert_eq!(
15595 f.run(&[
15596 b"TS.NRANGE",
15597 b"2",
15598 b"x",
15599 b"y",
15600 b"-",
15601 b"+",
15602 b"AGGREGATION",
15603 b"avg",
15604 b"sum",
15605 b"100",
15606 b"EMPTY",
15607 b"BUCKETTIMESTAMP",
15608 b"end"
15609 ]),
15610 "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
15611 );
15612 assert_eq!(
15615 f.run(&[
15616 b"TS.NRANGE",
15617 b"2",
15618 b"x",
15619 b"y",
15620 b"-",
15621 b"+",
15622 b"AGGREGATION",
15623 b"avg",
15624 b"COUNT",
15625 b"100"
15626 ]),
15627 "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
15628 );
15629 }
15630
15631 #[test]
15634 fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
15635 let mut f = joined();
15636 f.run(&[b"SET", b"str", b"hi"]);
15637 let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
15638 let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
15639 must be equal to numkeys\r\n";
15640 let cases: &[(&[&[u8]], &str)] = &[
15641 (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
15642 (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
15643 (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
15644 (
15647 &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
15648 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15649 ),
15650 (
15651 &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
15652 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15653 ),
15654 (
15657 &[
15658 b"TS.NRANGE",
15659 b"2",
15660 b"x",
15661 b"y",
15662 b"abc",
15663 b"+",
15664 b"AGGREGATION",
15665 b"nope",
15666 b"sum",
15667 b"100",
15668 ],
15669 "-ERR TSDB: Unknown aggregation type\r\n",
15670 ),
15671 (
15672 &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
15673 "-ERR TSDB: wrong fromTimestamp\r\n",
15674 ),
15675 (
15676 &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
15677 "-ERR TSDB: wrong toTimestamp\r\n",
15678 ),
15679 (
15683 &[
15684 b"TS.NRANGE",
15685 b"2",
15686 b"x",
15687 b"y",
15688 b"-",
15689 b"+",
15690 b"AGGREGATION",
15691 b"avg",
15692 ],
15693 numkeys,
15694 ),
15695 (
15696 &[
15697 b"TS.NRANGE",
15698 b"2",
15699 b"x",
15700 b"y",
15701 b"-",
15702 b"+",
15703 b"AGGREGATION",
15704 b"100",
15705 b"sum",
15706 b"100",
15707 ],
15708 numkeys,
15709 ),
15710 (
15711 &[
15712 b"TS.NRANGE",
15713 b"2",
15714 b"x",
15715 b"y",
15716 b"-",
15717 b"+",
15718 b"AGGREGATION",
15719 b"avg",
15720 b"sum",
15721 b"sum",
15722 b"100",
15723 ],
15724 numkeys,
15725 ),
15726 (
15727 &[
15728 b"TS.NRANGE",
15729 b"2",
15730 b"x",
15731 b"y",
15732 b"-",
15733 b"+",
15734 b"AGGREGATION",
15735 b"avg",
15736 b"sum",
15737 b"abc",
15738 ],
15739 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15740 ),
15741 (
15742 &[
15743 b"TS.NRANGE",
15744 b"2",
15745 b"x",
15746 b"y",
15747 b"-",
15748 b"+",
15749 b"AGGREGATION",
15750 b"avg",
15751 b"sum",
15752 b"0",
15753 ],
15754 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15755 ),
15756 (
15759 &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
15760 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15761 ),
15762 (
15763 &[
15764 b"TS.NRANGE",
15765 b"1",
15766 b"x",
15767 b"-",
15768 b"+",
15769 b"AGGREGATION",
15770 b"100",
15771 b"200",
15772 ],
15773 "-ERR TSDB: Unknown aggregation type\r\n",
15774 ),
15775 (
15777 &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
15778 "-ERR TSDB: the key does not exist\r\n",
15779 ),
15780 (
15781 &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
15782 "-ERR WRONGTYPE Operation against a key \
15783 holding the wrong kind of value\r\n",
15784 ),
15785 ];
15786 for (argv, want) in cases {
15787 let got = f.run(argv);
15788 assert_eq!(&got, want, "{argv:?}");
15789 }
15790 }
15791
15792 #[test]
15794 fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
15795 let mut f = joined();
15796 assert_eq!(
15797 f.run(&[b"TS.READ", b"x", b"-"]),
15798 "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
15799 *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
15800 );
15801 assert_eq!(
15804 f.run(&[b"TS.READ", b"x", b"+"]),
15805 "*1\r\n*2\r\n:50\r\n+5\r\n"
15806 );
15807 assert_eq!(
15808 f.run(&[b"TS.READ", b"x", b"25"]),
15809 "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
15810 );
15811 assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
15814 assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
15815 assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
15816 assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
15817 assert_eq!(
15821 f.run(&[b"TS.READ", b"x", b"abc"]),
15822 "-TSDB: invalid timestamp\r\n"
15823 );
15824 assert_eq!(
15825 f.run(&[b"TS.READ", b"x", b"-1"]),
15826 "-TSDB: invalid timestamp\r\n"
15827 );
15828 f.run(&[b"SET", b"str", b"hi"]);
15829 assert_eq!(
15830 f.run(&[b"TS.READ", b"str", b"-"]),
15831 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15832 );
15833 assert_eq!(
15836 f.run(&[b"TS.READ", b"x"]),
15837 "-ERR wrong number of arguments for 'ts.read' command\r\n"
15838 );
15839 assert_eq!(
15840 f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
15841 "-ERR wrong number of arguments for 'ts.read' command\r\n"
15842 );
15843 }
15844
15845 #[test]
15848 fn getkeys_reads_the_count_of_a_joined_read() {
15849 let mut f = Fixture::new();
15850 assert_eq!(
15851 f.run(&[
15852 b"COMMAND",
15853 b"GETKEYS",
15854 b"TS.NRANGE",
15855 b"2",
15856 b"a",
15857 b"b",
15858 b"-",
15859 b"+"
15860 ]),
15861 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
15862 );
15863 assert_eq!(
15864 f.run(&[
15865 b"COMMAND",
15866 b"GETKEYS",
15867 b"TS.NREVRANGE",
15868 b"1",
15869 b"a",
15870 b"-",
15871 b"+"
15872 ]),
15873 "*1\r\n$1\r\na\r\n"
15874 );
15875 for n in [b"0".as_slice(), b"9", b"abc"] {
15878 assert_eq!(
15879 f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
15880 "-ERR Invalid arguments specified for command\r\n"
15881 );
15882 }
15883 }
15884
15885 fn labelled() -> Fixture {
15887 let mut f = Fixture::new();
15888 f.run(&[
15889 b"TS.CREATE",
15890 b"a",
15891 b"LABELS",
15892 b"room",
15893 b"kitchen",
15894 b"x",
15895 b"1",
15896 ]);
15897 f.run(&[
15898 b"TS.CREATE",
15899 b"b",
15900 b"LABELS",
15901 b"room",
15902 b"bedroom",
15903 b"x",
15904 b"2",
15905 ]);
15906 f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
15907 f.run(&[b"TS.CREATE", b"d"]);
15908 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
15909 f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
15910 f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
15911 f
15912 }
15913
15914 #[test]
15917 fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
15918 let mut f = labelled();
15919 let cases: &[(&[&[u8]], &str)] = &[
15920 (
15923 &[b"TS.QUERYINDEX", b"room=kitchen"],
15924 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15925 ),
15926 (
15927 &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
15928 "*1\r\n$1\r\na\r\n",
15929 ),
15930 (
15931 &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
15932 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
15933 ),
15934 (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
15937 (
15939 &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
15940 "*1\r\n$1\r\nc\r\n",
15941 ),
15942 (
15943 &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
15944 "*1\r\n$1\r\na\r\n",
15945 ),
15946 (
15947 &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
15948 "-ERR TSDB: please provide at least one matcher\r\n",
15949 ),
15950 (
15953 &[b"TS.QUERYINDEX", b"room==kitchen"],
15954 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15955 ),
15956 (
15957 &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
15958 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15959 ),
15960 (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
15961 (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
15964 (
15965 &[b"TS.QUERYINDEX", b"=(1)"],
15966 "-ERR TSDB: failed parsing labels\r\n",
15967 ),
15968 (
15969 &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
15970 "-ERR TSDB: failed parsing labels\r\n",
15971 ),
15972 (
15973 &[b"TS.QUERYINDEX", b"room=(kitchen"],
15974 "-ERR TSDB: failed parsing labels\r\n",
15975 ),
15976 (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
15977 (
15978 &[b"TS.QUERYINDEX", b"nonsense"],
15979 "-ERR TSDB: failed parsing labels\r\n",
15980 ),
15981 (
15983 &[b"TS.QUERYINDEX", b"room!=kitchen"],
15984 "-ERR TSDB: please provide at least one matcher\r\n",
15985 ),
15986 (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
15988 (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
15989 (
15990 &[b"TS.QUERYINDEX"],
15991 "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
15992 ),
15993 ];
15994 for (argv, want) in cases {
15995 let got = f.run(argv);
15996 assert_eq!(&got, want, "{:?}", argv.last());
15997 }
15998 }
15999
16000 #[test]
16002 fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
16003 let mut f = labelled();
16004 let cases: &[(&[&[u8]], &str)] = &[
16005 (
16006 &[b"TS.QUERYLABELS", b"LABELS"],
16007 "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16008 ),
16009 (
16010 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16011 "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16012 ),
16013 (
16014 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16015 "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16016 ),
16017 (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16020 (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16021 (
16022 &[b"TS.QUERYLABELS", b"VALUES"],
16023 "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16024 ),
16025 (
16026 &[b"TS.QUERYLABELS", b"ZZZ"],
16027 "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16028 ),
16029 (
16030 &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16031 "-ERR TSDB: unknown argument, expected FILTER\r\n",
16032 ),
16033 (
16034 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16035 "-ERR TSDB: FILTER given with no filter expressions\r\n",
16036 ),
16037 (
16041 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16042 "-ERR TSDB: please provide at least one matcher\r\n",
16043 ),
16044 (
16045 &[
16046 b"TS.QUERYLABELS",
16047 b"LABELS",
16048 b"FILTER",
16049 b"room=kitchen",
16050 b"x=",
16051 ],
16052 "*1\r\n$4\r\nroom\r\n",
16053 ),
16054 ];
16055 for (argv, want) in cases {
16056 let got = f.run(argv);
16057 assert_eq!(&got, want, "{:?}", argv.last());
16058 }
16059 }
16060
16061 #[test]
16064 fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16065 let mut f = labelled();
16066 let cases: &[(&[&[u8]], &str)] = &[
16067 (
16070 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16071 "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16072 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16073 ),
16074 (
16075 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16076 "*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\
16077 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16078 *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",
16079 ),
16080 (
16082 &[
16083 b"TS.MGET",
16084 b"SELECTED_LABELS",
16085 b"x",
16086 b"FILTER",
16087 b"room=kitchen",
16088 ],
16089 "*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\
16090 *2\r\n:100\r\n+1.5\r\n\
16091 *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",
16092 ),
16093 (
16096 &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16097 "*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",
16098 ),
16099 (
16100 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16101 "*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\
16102 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16103 ),
16104 (
16107 &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16108 "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16109 ),
16110 (
16111 &[b"TS.MGET", b"a", b"b", b"c"],
16112 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16113 ),
16114 (
16115 &[b"TS.MGET", b"FILTER"],
16116 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16117 ),
16118 (
16121 &[
16122 b"TS.MGET",
16123 b"WITHLABELS",
16124 b"SELECTED_LABELS",
16125 b"x",
16126 b"FILTER",
16127 b"bad",
16128 ],
16129 "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16130 ),
16131 (
16132 &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16133 "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16134 ),
16135 ];
16136 for (argv, want) in cases {
16137 let got = f.run(argv);
16138 assert_eq!(&got, want, "{:?}", argv.last());
16139 }
16140 }
16141
16142 #[test]
16145 fn resp3_writes_the_label_surface_as_sets_and_maps() {
16146 let mut f = labelled();
16147 f.out = Out::new(Proto::Resp3);
16148 let cases: &[(&[&[u8]], &str)] = &[
16149 (
16150 &[b"TS.QUERYINDEX", b"room=kitchen"],
16151 "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16152 ),
16153 (
16154 &[b"TS.QUERYLABELS", b"LABELS"],
16155 "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16156 ),
16157 (
16158 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16159 "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16160 ),
16161 (
16164 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16165 "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16166 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16167 ),
16168 (
16169 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16170 "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16171 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16172 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16173 ),
16174 (
16175 &[
16176 b"TS.MGET",
16177 b"SELECTED_LABELS",
16178 b"x",
16179 b"FILTER",
16180 b"room=kitchen",
16181 ],
16182 "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16183 *2\r\n:100\r\n,1.5\r\n\
16184 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16185 ),
16186 (
16189 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16190 "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16191 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16192 ),
16193 ];
16194 for (argv, want) in cases {
16195 let got = f.run(argv);
16196 assert_eq!(&got, want, "{:?}", argv.last());
16197 }
16198 }
16199
16200 fn spanned() -> Fixture {
16203 let mut f = labelled();
16204 f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16205 f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16206 f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16207 f
16208 }
16209
16210 #[test]
16213 fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16214 let mut f = spanned();
16215 let cases: &[(&[&[u8]], &str)] = &[
16216 (
16217 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16218 "*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\
16219 *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",
16220 ),
16221 (
16224 &[
16225 b"TS.MREVRANGE",
16226 b"-",
16227 b"+",
16228 b"WITHLABELS",
16229 b"FILTER",
16230 b"room=kitchen",
16231 ],
16232 "*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\
16233 *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\
16234 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16235 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16236 ),
16237 (
16240 &[
16241 b"TS.MRANGE",
16242 b"-",
16243 b"+",
16244 b"SELECTED_LABELS",
16245 b"x",
16246 b"FILTER",
16247 b"room=kitchen",
16248 ],
16249 "*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\
16250 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16251 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16252 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16253 ),
16254 (
16257 &[
16258 b"TS.MRANGE",
16259 b"-",
16260 b"+",
16261 b"FILTER",
16262 b"room=kitchen",
16263 b"GROUPBY",
16264 b"room",
16265 b"REDUCE",
16266 b"sum",
16267 ],
16268 "*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\
16269 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16270 ),
16271 (
16274 &[
16275 b"TS.MRANGE",
16276 b"-",
16277 b"+",
16278 b"WITHLABELS",
16279 b"FILTER",
16280 b"room=kitchen",
16281 b"GROUPBY",
16282 b"room",
16283 b"REDUCE",
16284 b"max",
16285 ],
16286 "*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\
16287 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16288 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16289 *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",
16290 ),
16291 (
16293 &[
16294 b"TS.MREVRANGE",
16295 b"-",
16296 b"+",
16297 b"COUNT",
16298 b"1",
16299 b"FILTER",
16300 b"room=kitchen",
16301 b"GROUPBY",
16302 b"room",
16303 b"REDUCE",
16304 b"count",
16305 ],
16306 "*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",
16307 ),
16308 (
16310 &[
16311 b"TS.MRANGE",
16312 b"-",
16313 b"+",
16314 b"FILTER",
16315 b"room=kitchen",
16316 b"GROUPBY",
16317 b"nope",
16318 b"REDUCE",
16319 b"sum",
16320 ],
16321 "*0\r\n",
16322 ),
16323 (
16324 &[
16325 b"TS.MRANGE",
16326 b"-",
16327 b"+",
16328 b"AGGREGATION",
16329 b"sum,avg",
16330 b"100",
16331 b"FILTER",
16332 b"room=bedroom",
16333 ],
16334 "*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",
16335 ),
16336 (
16338 &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16339 "-ERR TSDB: missing FILTER argument\r\n",
16340 ),
16341 (
16342 &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16343 "-ERR TSDB: missing labels for filter argument\r\n",
16344 ),
16345 (
16346 &[
16347 b"TS.MRANGE",
16348 b"-",
16349 b"+",
16350 b"GROUPBY",
16351 b"room",
16352 b"REDUCE",
16353 b"sum",
16354 b"FILTER",
16355 b"room=kitchen",
16356 ],
16357 "-ERR TSDB: GROUPBY should always come after filter\r\n",
16358 ),
16359 (
16362 &[
16363 b"TS.MRANGE",
16364 b"-",
16365 b"+",
16366 b"FILTER",
16367 b"room=kitchen",
16368 b"GROUPBY",
16369 b"room",
16370 b"REDUCE",
16371 b"sum",
16372 b"x",
16373 ],
16374 "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16375 ),
16376 (
16378 &[
16379 b"TS.MRANGE",
16380 b"-",
16381 b"+",
16382 b"FILTER",
16383 b"nope",
16384 b"GROUPBY",
16385 b"room",
16386 b"REDUCE",
16387 b"sum",
16388 b"x",
16389 ],
16390 "-ERR TSDB: failed parsing labels\r\n",
16391 ),
16392 (
16393 &[
16394 b"TS.MRANGE",
16395 b"-",
16396 b"+",
16397 b"FILTER",
16398 b"room=kitchen",
16399 b"GROUPBY",
16400 b"room",
16401 b"REDUCE",
16402 b"twa",
16403 ],
16404 "-ERR TSDB: Invalid reducer type\r\n",
16405 ),
16406 (
16407 &[
16408 b"TS.MRANGE",
16409 b"-",
16410 b"+",
16411 b"AGGREGATION",
16412 b"sum,avg",
16413 b"100",
16414 b"FILTER",
16415 b"room=kitchen",
16416 b"GROUPBY",
16417 b"room",
16418 b"REDUCE",
16419 b"sum",
16420 ],
16421 "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
16422 ),
16423 (
16426 &[
16427 b"TS.MRANGE",
16428 b"-",
16429 b"+",
16430 b"SELECTED_LABELS",
16431 b"COUNT",
16432 b"FILTER",
16433 b"room=kitchen",
16434 ],
16435 "-ERR TSDB: Couldn't parse COUNT\r\n",
16436 ),
16437 ];
16438 for (argv, want) in cases {
16439 let got = f.run(argv);
16440 assert_eq!(&got, want, "{argv:?}");
16441 }
16442 }
16443
16444 #[test]
16447 fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
16448 let mut f = spanned();
16449 f.out = Out::new(Proto::Resp3);
16450 let cases: &[(&[&[u8]], &str)] = &[
16451 (
16452 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
16453 "%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\
16454 *1\r\n*2\r\n:200\r\n,2\r\n",
16455 ),
16456 (
16459 &[
16460 b"TS.MRANGE",
16461 b"-",
16462 b"+",
16463 b"AGGREGATION",
16464 b"sum,avg",
16465 b"100",
16466 b"FILTER",
16467 b"room=bedroom",
16468 ],
16469 "%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\
16470 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
16471 ),
16472 (
16473 &[
16474 b"TS.MRANGE",
16475 b"-",
16476 b"+",
16477 b"FILTER",
16478 b"room=kitchen",
16479 b"GROUPBY",
16480 b"room",
16481 b"REDUCE",
16482 b"sum",
16483 ],
16484 "%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\
16485 $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\
16486 *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",
16487 ),
16488 (
16491 &[
16492 b"TS.MRANGE",
16493 b"-",
16494 b"+",
16495 b"WITHLABELS",
16496 b"FILTER",
16497 b"room=kitchen",
16498 b"GROUPBY",
16499 b"room",
16500 b"REDUCE",
16501 b"max",
16502 ],
16503 "%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\
16504 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
16505 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16506 *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",
16507 ),
16508 (
16509 &[
16510 b"TS.MRANGE",
16511 b"-",
16512 b"+",
16513 b"FILTER",
16514 b"room=kitchen",
16515 b"GROUPBY",
16516 b"nope",
16517 b"REDUCE",
16518 b"sum",
16519 ],
16520 "%0\r\n",
16521 ),
16522 ];
16523 for (argv, want) in cases {
16524 let got = f.run(argv);
16525 assert_eq!(&got, want, "{argv:?}");
16526 }
16527 }
16528
16529 #[test]
16531 fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
16532 let mut f = Fixture::new();
16533 f.run(&[b"TS.CREATE", b"src"]);
16534 f.run(&[b"TS.CREATE", b"dst"]);
16535 f.run(&[b"SET", b"plain", b"v"]);
16536 let cases: &[(&[&[u8]], &str)] = &[
16537 (
16541 &[
16542 b"TS.CREATERULE",
16543 b"src",
16544 b"dst",
16545 b"AGGREGATION",
16546 b"nope",
16547 b"x",
16548 ],
16549 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16550 ),
16551 (
16552 &[
16553 b"TS.CREATERULE",
16554 b"src",
16555 b"dst",
16556 b"AGGREGATION",
16557 b"nope",
16558 b"10",
16559 ],
16560 "-ERR TSDB: Unknown aggregation type\r\n",
16561 ),
16562 (
16563 &[
16564 b"TS.CREATERULE",
16565 b"src",
16566 b"dst",
16567 b"AGGREGATION",
16568 b"avg",
16569 b"0",
16570 ],
16571 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16572 ),
16573 (
16574 &[
16575 b"TS.CREATERULE",
16576 b"src",
16577 b"dst",
16578 b"AGGREGATION",
16579 b"avg",
16580 b"10",
16581 b"x",
16582 ],
16583 "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
16584 ),
16585 (
16586 &[
16587 b"TS.CREATERULE",
16588 b"src",
16589 b"src",
16590 b"AGGREGATION",
16591 b"avg",
16592 b"10",
16593 ],
16594 "-ERR TSDB: the source key and destination key should be different\r\n",
16595 ),
16596 (
16600 &[
16601 b"TS.CREATERULE",
16602 b"nope",
16603 b"plain",
16604 b"AGGREGATION",
16605 b"avg",
16606 b"10",
16607 ],
16608 "-ERR TSDB: the key does not exist\r\n",
16609 ),
16610 (
16611 &[
16612 b"TS.CREATERULE",
16613 b"src",
16614 b"nope",
16615 b"AGGREGATION",
16616 b"avg",
16617 b"10",
16618 ],
16619 "-ERR TSDB: the key does not exist\r\n",
16620 ),
16621 (
16624 &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
16625 "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
16626 ),
16627 (
16628 &[
16629 b"TS.CREATERULE",
16630 b"src",
16631 b"dst",
16632 b"AGGREGATION",
16633 b"avg",
16634 b"10",
16635 ],
16636 "+OK\r\n",
16637 ),
16638 (
16641 &[
16642 b"TS.CREATERULE",
16643 b"src",
16644 b"dst",
16645 b"AGGREGATION",
16646 b"avg",
16647 b"10",
16648 ],
16649 "-ERR TSDB: the destination key already has a src rule\r\n",
16650 ),
16651 (
16654 &[
16655 b"TS.CREATERULE",
16656 b"dst",
16657 b"src",
16658 b"AGGREGATION",
16659 b"avg",
16660 b"10",
16661 ],
16662 "-ERR TSDB: the source key already has a source rule\r\n",
16663 ),
16664 (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
16665 (
16666 &[b"TS.DELETERULE", b"src", b"dst"],
16667 "-ERR TSDB: compaction rule does not exist\r\n",
16668 ),
16669 (
16673 &[b"TS.DELETERULE", b"src", b"nope"],
16674 "-ERR TSDB: compaction rule does not exist\r\n",
16675 ),
16676 (
16677 &[b"TS.DELETERULE", b"nope", b"dst"],
16678 "-ERR TSDB: the key does not exist\r\n",
16679 ),
16680 ];
16681 for (argv, want) in cases {
16682 let got = f.run(argv);
16683 assert_eq!(&got, want, "{argv:?}");
16684 }
16685 }
16686
16687 #[test]
16689 fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
16690 let mut f = Fixture::new();
16691 f.run(&[b"TS.CREATE", b"src"]);
16692 f.run(&[b"TS.CREATE", b"dst"]);
16693 f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
16696 f.run(&[
16697 b"TS.CREATERULE",
16698 b"src",
16699 b"dst",
16700 b"AGGREGATION",
16701 b"sum",
16702 b"100",
16703 ]);
16704 f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
16705 assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
16706 assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
16709 f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
16710 assert_eq!(
16711 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16712 "*1\r\n*2\r\n:0\r\n+2\r\n"
16713 );
16714 f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
16717 assert_eq!(
16718 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16719 "*1\r\n*2\r\n:0\r\n+11\r\n"
16720 );
16721 assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
16724 assert_eq!(
16725 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16726 "*1\r\n*2\r\n:0\r\n+8\r\n"
16727 );
16728 assert_eq!(
16729 f.run(&[b"TS.GET", b"dst", b"LATEST"]),
16730 "*2\r\n:100\r\n+4\r\n"
16731 );
16732 assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
16734 f.run(&[b"DEL", b"dst"]);
16735 assert_eq!(
16736 f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
16737 "-ERR TSDB: compaction rule does not exist\r\n"
16738 );
16739 }
16740
16741 #[test]
16744 fn xadd_ids_only_ever_go_up() {
16745 let mut f = Fixture::new();
16746 assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
16748 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
16750 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
16751 assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
16752 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
16753
16754 assert!(
16755 f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
16756 .contains("equal or smaller")
16757 );
16758 assert!(
16759 f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
16760 .contains("must be greater than 0-0")
16761 );
16762 assert!(
16763 f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
16764 .contains("Invalid stream ID")
16765 );
16766 assert!(
16769 f.run(&[b"XADD", b"s", b"*", b"a"])
16770 .contains("wrong number of arguments")
16771 );
16772
16773 assert_eq!(
16776 f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
16777 "$-1\r\n"
16778 );
16779 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
16780 assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
16781 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
16782 }
16783
16784 #[test]
16787 fn trimming_reads_its_options_the_way_redis_does() {
16788 let mut f = Fixture::new();
16789 for i in 1..=10u32 {
16790 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
16791 }
16792 assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
16793 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
16794 assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
16795 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
16796
16797 assert!(
16801 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
16802 .contains("not an integer")
16803 );
16804 assert!(
16805 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
16806 .contains("MAXLEN argument must be >= 0")
16807 );
16808 assert!(
16811 f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
16812 .contains("without specifying a trimming strategy")
16813 );
16814 assert!(
16815 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
16816 .contains("without the special ~ option")
16817 );
16818 assert!(
16819 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
16820 .contains("at the same time are not compatible")
16821 );
16822 assert!(
16824 f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
16825 .contains("syntax error")
16826 );
16827 assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
16828 }
16829
16830 #[test]
16832 fn xrange_looks_the_key_up_before_it_reads_the_count() {
16833 let mut f = Fixture::new();
16834 f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
16835 f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
16836
16837 assert_eq!(
16838 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
16839 "*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\
16840 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
16841 );
16842 assert_eq!(
16843 f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
16844 "*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"
16845 );
16846 assert_eq!(
16850 f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
16851 "*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\
16852 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
16853 );
16854 assert_eq!(
16855 f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
16856 "*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"
16857 );
16858 assert!(
16859 f.run(&[b"XRANGE", b"s", b"(-", b"+"])
16860 .contains("Invalid stream ID")
16861 );
16862
16863 assert_eq!(
16867 f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
16868 "*0\r\n"
16869 );
16870 assert_eq!(
16871 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
16872 "*-1\r\n"
16873 );
16874 f.run(&[b"SET", b"str", b"v"]);
16875 assert!(
16876 f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
16877 .starts_with("-WRONGTYPE")
16878 );
16879 assert_eq!(
16881 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
16882 "*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"
16883 );
16884 }
16885
16886 #[test]
16888 fn a_bad_id_late_in_the_list_stops_the_whole_command() {
16889 let mut f = Fixture::new();
16890 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
16891 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
16892 assert!(
16893 f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
16894 .contains("Invalid stream ID")
16895 );
16896 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
16897 assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
16898 assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
16899 assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
16900 assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
16901 }
16902
16903 #[test]
16905 fn xgroup_has_an_arity_per_subcommand() {
16906 let mut f = Fixture::new();
16907 assert!(
16908 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
16909 .contains("requires the key")
16910 );
16911 assert_eq!(
16912 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
16913 "+OK\r\n"
16914 );
16915 assert!(
16918 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
16919 .starts_with("-BUSYGROUP")
16920 );
16921 assert_eq!(
16922 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
16923 ":1\r\n"
16924 );
16925 assert_eq!(
16926 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
16927 ":0\r\n"
16928 );
16929 assert_eq!(
16930 f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
16931 ":0\r\n"
16932 );
16933
16934 let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
16936 assert!(
16937 short.contains("wrong number of arguments for 'xgroup|destroy' command"),
16938 "{short}"
16939 );
16940 let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
16942 assert!(
16943 odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
16944 "{odd}"
16945 );
16946 assert!(
16947 f.run(&[b"XGROUP", b"NOSUCH", b"s"])
16948 .contains("Try XGROUP HELP")
16949 );
16950
16951 assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
16952 assert!(
16953 f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
16954 .starts_with("-NOGROUP")
16955 );
16956 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
16957 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
16958 assert!(
16959 f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
16960 .contains("requires the key")
16961 );
16962 }
16963
16964 #[test]
16966 fn xreadgroup_hands_out_and_xack_takes_back() {
16967 let mut f = Fixture::new();
16968 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
16969 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
16970 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
16971
16972 let first = f.run(&[
16973 b"XREADGROUP",
16974 b"GROUP",
16975 b"g",
16976 b"c1",
16977 b"COUNT",
16978 b"1",
16979 b"STREAMS",
16980 b"s",
16981 b">",
16982 ]);
16983 assert_eq!(
16984 first,
16985 "*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"
16986 );
16987 assert_eq!(
16990 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
16991 "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
16992 );
16993 assert_eq!(
16994 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
16995 "*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"
16996 );
16997
16998 assert_eq!(
16999 f.run(&[b"XPENDING", b"s", b"g"]),
17000 "*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"
17001 );
17002 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
17003 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
17004 assert_eq!(
17006 f.run(&[b"XPENDING", b"s", b"g"]),
17007 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
17008 );
17009
17010 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17013 f.run(&[b"XDEL", b"s", b"2-1"]);
17014 assert_eq!(
17015 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17016 "*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"
17017 );
17018
17019 assert!(
17022 f.run(&[
17023 b"XREADGROUP",
17024 b"GROUP",
17025 b"nope",
17026 b"c",
17027 b"STREAMS",
17028 b"s",
17029 b"+"
17030 ])
17031 .starts_with("-NOGROUP")
17032 );
17033 assert!(
17034 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17035 .contains("meaningless in the context of XREADGROUP")
17036 );
17037 assert!(
17038 f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17039 .contains("only supported by XREADGROUP")
17040 );
17041 assert!(
17042 f.run(&[
17043 b"XREADGROUP",
17044 b"GROUP",
17045 b"g",
17046 b"c",
17047 b"STREAMS",
17048 b"s",
17049 b"a",
17050 b"b"
17051 ])
17052 .contains("Unbalanced 'xreadgroup' list of streams")
17053 );
17054 }
17055
17056 #[test]
17059 fn xread_with_no_block_writes_the_null_itself() {
17060 let mut f = Fixture::new();
17061 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17062 assert_eq!(
17063 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17064 "*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"
17065 );
17066 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17069 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17070 f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17071 assert_eq!(
17072 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17073 "*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"
17074 );
17075 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17077 assert_eq!(
17079 f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17080 "*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"
17081 );
17082 assert_eq!(
17085 f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17086 "*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"
17087 );
17088 assert!(
17090 f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17091 .contains("not an integer")
17092 );
17093 assert!(
17094 f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17095 .contains("timeout is negative")
17096 );
17097 assert!(
17098 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17099 .contains("Unbalanced 'xread' list of streams")
17100 );
17101 }
17102
17103 #[test]
17105 fn a_blocked_xread_wakes_on_the_next_entry() {
17106 let mut f = Fixture::new();
17107 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17108 let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17109 assert_eq!(flow, Flow::Block);
17110 assert!(reply.is_empty());
17111
17112 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17115 assert_eq!(flow, Flow::Block);
17116 assert_eq!(f.server.waiters().len(), 2);
17117
17118 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17119 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";
17120 for at in 0..2 {
17121 let mut out = Out::new(Proto::Resp2);
17122 assert!(f.server.serve_waiter(at, 0, &mut out));
17123 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17124 }
17125
17126 f.server.waiters_mut().forget(7);
17129 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17130 assert_eq!(flow, Flow::Block);
17131 let mut out = Out::new(Proto::Resp2);
17132 assert!(!f.server.serve_waiter(0, 0, &mut out));
17133 assert!(out.as_slice().is_empty());
17134 assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
17135 assert_eq!(
17136 core::str::from_utf8(out.as_slice()).expect("ascii"),
17137 "*-1\r\n"
17138 );
17139 }
17140
17141 #[test]
17143 fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17144 let mut f = Fixture::new();
17145 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17146 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17147 let (flow, _) = f.flow(&[
17148 b"XREADGROUP",
17149 b"GROUP",
17150 b"g",
17151 b"c",
17152 b"BLOCK",
17153 b"0",
17154 b"STREAMS",
17155 b"s",
17156 b">",
17157 ]);
17158 assert_eq!(flow, Flow::Block);
17159
17160 f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17161 let mut out = Out::new(Proto::Resp2);
17162 assert!(f.server.serve_waiter(0, 0, &mut out));
17163 assert_eq!(
17166 core::str::from_utf8(out.as_slice()).expect("ascii"),
17167 "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17168 );
17169 }
17170
17171 #[test]
17173 fn xclaim_reads_ids_until_one_will_not_parse() {
17174 let mut f = Fixture::new();
17175 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17176 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17177 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17178 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17179
17180 assert!(
17183 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17184 .contains("Unrecognized XCLAIM option '-'")
17185 );
17186 assert_eq!(
17187 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17188 "*1\r\n$3\r\n1-1\r\n"
17189 );
17190 f.run(&[b"XDEL", b"s", b"2-1"]);
17193 assert_eq!(
17194 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17195 "*0\r\n"
17196 );
17197 assert!(
17198 f.run(&[b"XPENDING", b"s", b"g"])
17199 .starts_with("*4\r\n:1\r\n")
17200 );
17201 assert!(
17202 f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17203 .starts_with("-NOGROUP")
17204 );
17205 assert!(
17206 f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17207 .contains("Invalid min-idle-time argument for XCLAIM")
17208 );
17209 }
17210
17211 #[test]
17213 fn xautoclaim_reports_what_it_dropped() {
17214 let mut f = Fixture::new();
17215 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17216 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17217 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17218 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17219 f.run(&[b"XDEL", b"s", b"1-1"]);
17220
17221 assert_eq!(
17224 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17225 "*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"
17226 );
17227 assert!(
17228 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17229 .contains("COUNT must be > 0")
17230 );
17231 assert!(
17232 f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17233 .starts_with("-NOGROUP")
17234 );
17235 }
17236
17237 #[test]
17239 fn xdelex_answers_one_integer_an_id() {
17240 let mut f = Fixture::new();
17241 for i in 1..=4 {
17242 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17243 }
17244 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17245 f.run(&[
17246 b"XREADGROUP",
17247 b"GROUP",
17248 b"g",
17249 b"c",
17250 b"COUNT",
17251 b"2",
17252 b"STREAMS",
17253 b"s",
17254 b">",
17255 ]);
17256
17257 assert_eq!(
17259 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17260 "*2\r\n:1\r\n:-1\r\n"
17261 );
17262 assert!(
17265 f.run(&[b"XPENDING", b"s", b"g"])
17266 .starts_with("*4\r\n:2\r\n")
17267 );
17268 assert_eq!(
17270 f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17271 "*1\r\n:1\r\n"
17272 );
17273 assert_eq!(
17275 f.run(&[b"XPENDING", b"s", b"g"]),
17276 "*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"
17277 );
17278
17279 assert_eq!(
17283 f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17284 "*2\r\n:2\r\n:2\r\n"
17285 );
17286
17287 assert_eq!(
17289 f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17290 "*2\r\n:-1\r\n:-1\r\n"
17291 );
17292 assert!(
17294 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17295 .starts_with("-ERR Invalid stream ID")
17296 );
17297 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17298
17299 assert!(
17300 f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17301 .contains("Number of IDs must be a positive integer")
17302 );
17303 assert!(
17304 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17305 .contains("The `numids` parameter must match the number of arguments")
17306 );
17307 assert!(
17310 f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17311 .starts_with("-ERR syntax error")
17312 );
17313 assert!(
17314 f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17315 .starts_with("-ERR syntax error")
17316 );
17317 f.run(&[b"SET", b"str", b"v"]);
17319 assert!(
17320 f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17321 .starts_with("-WRONGTYPE")
17322 );
17323 }
17324
17325 #[test]
17327 fn xackdel_reports_what_the_group_was_holding() {
17328 let mut f = Fixture::new();
17329 for i in 1..=3 {
17330 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17331 }
17332 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17333 f.run(&[
17334 b"XREADGROUP",
17335 b"GROUP",
17336 b"g",
17337 b"c",
17338 b"COUNT",
17339 b"1",
17340 b"STREAMS",
17341 b"s",
17342 b">",
17343 ]);
17344
17345 assert_eq!(
17349 f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17350 "*2\r\n:1\r\n:-1\r\n"
17351 );
17352 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17353
17354 assert_eq!(
17356 f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17357 "*1\r\n:-1\r\n"
17358 );
17359 assert_eq!(
17360 f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17361 "*1\r\n:-1\r\n"
17362 );
17363
17364 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17367 f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17368 assert_eq!(
17369 f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17370 "*1\r\n:2\r\n"
17371 );
17372 assert_eq!(
17373 f.run(&[b"XPENDING", b"s", b"g"]),
17374 "*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"
17375 );
17376 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17377 }
17378
17379 #[test]
17381 fn xnack_releases_an_entry_for_the_next_claim() {
17382 let mut f = Fixture::new();
17383 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17384 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17385 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17386 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17387 f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17390
17391 assert_eq!(
17392 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
17393 ":1\r\n"
17394 );
17395 assert_eq!(
17399 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
17400 "*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"
17401 );
17402 assert_eq!(
17404 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17405 "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
17406 );
17407 assert_eq!(
17409 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17410 "*-1\r\n"
17411 );
17412 assert_eq!(
17414 f.run(&[
17415 b"XAUTOCLAIM",
17416 b"s",
17417 b"g",
17418 b"c2",
17419 b"99999999",
17420 b"-",
17421 b"JUSTID"
17422 ]),
17423 "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
17424 );
17425
17426 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17430 assert!(
17431 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17432 .contains(":-1\r\n:1\r\n")
17433 );
17434 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17436 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17437 assert!(
17438 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17439 .contains(":-1\r\n:0\r\n")
17440 );
17441 f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
17443 assert!(
17444 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17445 .contains(":9223372036854775807\r\n")
17446 );
17447 f.run(&[
17448 b"XNACK",
17449 b"s",
17450 b"g",
17451 b"FATAL",
17452 b"IDS",
17453 b"1",
17454 b"1-1",
17455 b"RETRYCOUNT",
17456 b"3",
17457 ]);
17458 assert!(
17459 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17460 .contains(":-1\r\n:3\r\n")
17461 );
17462
17463 f.run(&[b"XACK", b"s", b"g", b"2-1"]);
17467 assert_eq!(
17468 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
17469 ":0\r\n"
17470 );
17471 assert_eq!(
17472 f.run(&[
17473 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
17474 ]),
17475 ":1\r\n"
17476 );
17477 assert!(
17478 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17479 .contains(":-1\r\n:0\r\n")
17480 );
17481 assert_eq!(
17483 f.run(&[
17484 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
17485 ]),
17486 ":0\r\n"
17487 );
17488
17489 assert_eq!(
17492 f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
17493 "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
17494 );
17495 assert!(
17496 f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
17497 .starts_with("-ERR")
17498 );
17499 assert!(
17501 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
17502 .contains("numids must be a positive integer")
17503 );
17504 assert!(
17505 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
17506 .contains("number of IDs doesn't match numids")
17507 );
17508 assert!(
17511 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
17512 .contains("Unrecognized XNACK option '2-1'")
17513 );
17514 }
17515
17516 #[test]
17518 fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
17519 let mut f = Fixture::new();
17520 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17521 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17522 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17523 f.run(&[
17524 b"XREADGROUP",
17525 b"GROUP",
17526 b"g",
17527 b"c1",
17528 b"COUNT",
17529 b"1",
17530 b"STREAMS",
17531 b"s",
17532 b">",
17533 ]);
17534
17535 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17536 assert!(info.starts_with("*20\r\n"), "{info}");
17539 assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
17540 assert!(
17541 info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
17542 "{info}"
17543 );
17544 assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
17545 assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
17546
17547 let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
17548 assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
17549 assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
17550 assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
17551 assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
17552
17553 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
17557 let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
17558 assert!(consumers.starts_with("*2\r\n"), "{consumers}");
17559 assert!(
17560 consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
17561 "{consumers}"
17562 );
17563 let c1 = consumers.find("c1").unwrap();
17565 let c2 = consumers.find("c2").unwrap();
17566 assert!(c1 < c2, "{consumers}");
17567
17568 let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
17569 assert!(full.starts_with("*18\r\n"), "{full}");
17570 assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
17571 assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
17572
17573 assert!(
17574 f.run(&[b"XINFO", b"STREAM", b"missing"])
17575 .contains("no such key")
17576 );
17577 assert!(
17578 f.run(&[b"XINFO", b"GROUPS", b"missing"])
17579 .contains("no such key")
17580 );
17581 assert!(
17582 f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
17583 .starts_with("-NOGROUP")
17584 );
17585 assert!(
17586 f.run(&[b"XINFO", b"NOSUCH", b"s"])
17587 .contains("Try XINFO HELP")
17588 );
17589 assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
17590 assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
17591 }
17592
17593 #[test]
17595 fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
17596 let mut f = Fixture::new();
17597 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17598 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17599 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17600
17601 let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
17602 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");
17603 assert_eq!(
17604 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17605 "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
17606 );
17607 assert_eq!(
17609 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
17610 "*0\r\n"
17611 );
17612 assert_eq!(
17613 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
17614 list
17615 );
17616 assert!(
17618 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
17619 .contains("syntax error")
17620 );
17621 assert!(
17622 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
17623 .contains("syntax error")
17624 );
17625 assert_eq!(
17626 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
17627 "*0\r\n"
17628 );
17629 assert!(
17630 f.run(&[b"XPENDING", b"missing", b"g"])
17631 .starts_with("-NOGROUP")
17632 );
17633 }
17634
17635 #[test]
17637 fn xsetid_will_not_go_below_what_is_there() {
17638 let mut f = Fixture::new();
17639 f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
17640 assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
17641 assert_eq!(
17642 f.run(&[
17643 b"XSETID",
17644 b"s",
17645 b"10-1",
17646 b"ENTRIESADDED",
17647 b"7",
17648 b"MAXDELETEDID",
17649 b"9-1"
17650 ]),
17651 "+OK\r\n"
17652 );
17653 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17654 assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
17655 assert!(
17656 info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
17657 "{info}"
17658 );
17659
17660 assert!(
17661 f.run(&[b"XSETID", b"s", b"1-1"])
17662 .contains("smaller than the target stream top item")
17663 );
17664 assert!(
17665 f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
17666 .contains("entries_added must be positive")
17667 );
17668 assert!(
17669 f.run(&[b"XSETID", b"missing", b"1-1"])
17670 .contains("no such key")
17671 );
17672 }
17673
17674 #[test]
17676 fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
17677 let mut f = Fixture::new();
17678 f.run(&[b"HELLO", b"3"]);
17679 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17680 assert_eq!(
17683 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17684 "%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"
17685 );
17686 assert_eq!(
17689 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17690 "*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"
17691 );
17692 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
17693 }
17694
17695 struct Mem {
17701 blobs: Vec<Vec<u8>>,
17702 }
17703
17704 impl yo_kv::cold::Blocks for Mem {
17705 fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
17706 self.blobs.push(bytes.to_vec());
17707 Ok(yo_common::Addr::new(
17708 yo_common::Space::Log,
17709 (self.blobs.len() - 1) as u64,
17710 ))
17711 }
17712
17713 fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
17714 self.blobs
17715 .get(at.offset() as usize)
17716 .map(Vec::as_slice)
17717 .ok_or_else(|| {
17718 yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
17719 })
17720 }
17721
17722 fn bytes(&self) -> u64 {
17723 self.blobs.iter().map(|b| b.len() as u64).sum()
17724 }
17725 }
17726
17727 fn filled(attach: bool) -> (Fixture, usize) {
17731 let mut f = Fixture::new();
17732 if attach {
17733 f.server
17734 .striped(0)
17735 .stripe_mut(0)
17736 .attach(Box::new(Mem { blobs: Vec::new() }));
17737 }
17738 let val = vec![b'v'; 256];
17739 for i in 0..24000u32 {
17740 let k = format!("key:{i:08}");
17741 f.run(&[b"SET", k.as_bytes(), &val]);
17742 }
17743 let full = f.server.memory_bytes();
17744 assert!(full > 3 * 1024 * 1024, "the arena is several segments");
17745 (f, full)
17746 }
17747
17748 fn press(f: &mut Fixture, limit: usize) {
17754 let val = vec![b'v'; 256];
17755 for i in 0..3000u32 {
17756 let k = format!("new:{i:08}");
17757 assert_eq!(
17758 f.run(&[b"SET", k.as_bytes(), &val]),
17759 "+OK\r\n",
17760 "write {i} was refused"
17761 );
17762 f.server.refresh_memory();
17763 if f.server.memory_bytes() <= limit {
17764 return;
17765 }
17766 }
17767 panic!(
17768 "it never got under: {} against {limit}",
17769 f.server.memory_bytes()
17770 );
17771 }
17772
17773 #[test]
17774 fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
17775 let mut f = Fixture::new();
17776 assert_eq!(
17777 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
17778 "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
17779 "no limit is the default"
17780 );
17781 for (typed, bytes) in [
17784 (&b"0"[..], "0"),
17785 (b"1024", "1024"),
17786 (b"1k", "1000"),
17787 (b"1gb", "1073741824"),
17788 (b"-1", "-1"),
17789 ] {
17790 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
17791 assert_eq!(
17792 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
17793 format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
17794 "set {}",
17795 String::from_utf8_lossy(typed)
17796 );
17797 }
17798 for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
17799 assert_eq!(
17800 f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
17801 "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
17802 "refused {}",
17803 String::from_utf8_lossy(bad)
17804 );
17805 }
17806 let info = f.run(&[b"INFO", b"memory"]);
17808 assert!(info.contains("maxstore:-1"), "{info}");
17809 assert!(info.contains("yo_memory_regime:evict"), "{info}");
17810 assert!(info.contains("yo_store_bytes:0"), "{info}");
17811 }
17812
17813 #[test]
17814 fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
17815 let (mut f, full) = filled(true);
17819 let keys = f.run(&[b"DBSIZE"]);
17820 assert!(
17821 f.run(&[b"INFO", b"memory"])
17822 .contains("yo_memory_regime:migrate"),
17823 "a database with somewhere to put values migrates"
17824 );
17825
17826 let limit = full - 2 * 1024 * 1024;
17827 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17828 f.run(&[
17829 b"CONFIG",
17830 b"SET",
17831 b"maxmemory",
17832 limit.to_string().as_bytes(),
17833 ]);
17834 press(&mut f, limit);
17835
17836 assert!(
17837 f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17838 "nothing was thrown away"
17839 );
17840 let after: usize = f.run(&[b"DBSIZE"])[1..]
17841 .trim_end()
17842 .parse()
17843 .expect("a count");
17844 let before: usize = keys[1..].trim_end().parse().expect("a count");
17845 assert!(after > before, "the keys that came in are all still here");
17846 assert!(
17847 f.server.store_bytes() > 0,
17848 "and what came out of memory went to the file"
17849 );
17850 let val = format!("$256\r\n{}\r\n", "v".repeat(256));
17853 assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
17854 assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
17855 }
17856
17857 #[test]
17858 fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
17859 let (mut f, full) = filled(true);
17863 f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
17864 assert!(
17865 f.run(&[b"INFO", b"memory"])
17866 .contains("yo_memory_regime:evict"),
17867 "nothing may go to the file"
17868 );
17869
17870 let limit = full - 2 * 1024 * 1024;
17871 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17872 f.run(&[
17873 b"CONFIG",
17874 b"SET",
17875 b"maxmemory",
17876 limit.to_string().as_bytes(),
17877 ]);
17878 press(&mut f, limit);
17879
17880 assert!(
17881 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17882 "keys were thrown away, which is what was asked for"
17883 );
17884 assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
17885 }
17886
17887 #[test]
17888 fn a_full_file_goes_back_to_evicting() {
17889 let (mut f, full) = filled(true);
17893 f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
17894 let limit = full - 2 * 1024 * 1024;
17895 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17896 f.run(&[
17897 b"CONFIG",
17898 b"SET",
17899 b"maxmemory",
17900 limit.to_string().as_bytes(),
17901 ]);
17902 press(&mut f, limit);
17903
17904 assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
17905 assert!(
17906 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17907 "and then it started evicting"
17908 );
17909 assert!(
17910 f.run(&[b"INFO", b"memory"])
17911 .contains("yo_memory_regime:evict"),
17912 "and it says so"
17913 );
17914 }
17915 #[test]
17931 fn the_string_group_answers_the_same_however_many_stripes_there_are() {
17932 let script: &[&[&[u8]]] = &[
17933 &[b"SET", b"k1", b"v1"],
17936 &[b"SET", b"k2", b"v2"],
17937 &[b"GET", b"k1"],
17938 &[b"GET", b"nothing"],
17939 &[b"GETSET", b"k1", b"v1b"],
17940 &[b"SETNX", b"k1", b"no"],
17941 &[b"SETNX", b"k3", b"yes"],
17942 &[b"APPEND", b"k3", b"!"],
17943 &[b"STRLEN", b"k3"],
17944 &[b"SETRANGE", b"k3", b"1", b"XY"],
17945 &[b"GETRANGE", b"k3", b"0", b"-1"],
17946 &[b"INCR", b"n1"],
17947 &[b"INCRBY", b"n1", b"41"],
17948 &[b"DECRBY", b"n1", b"2"],
17949 &[b"INCRBYFLOAT", b"f1", b"1.5"],
17950 &[b"SETEX", b"e1", b"100", b"v"],
17951 &[b"PSETEX", b"e2", b"100000", b"v"],
17952 &[b"GETEX", b"e1", b"PERSIST"],
17953 &[b"GETDEL", b"k2"],
17954 &[b"GET", b"k2"],
17955 &[b"DIGEST", b"k1"],
17956 &[b"DELEX", b"k3"],
17957 &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
17960 &[b"MGET", b"a", b"b", b"c", b"missing"],
17961 &[b"MSETNX", b"d", b"4", b"e", b"5"],
17962 &[b"MSETNX", b"e", b"6", b"f", b"7"],
17963 &[b"MGET", b"d", b"e", b"f"],
17964 &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
17965 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
17966 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
17967 &[b"MGET", b"g", b"h"],
17968 &[b"SET", b"s1", b"ohmytext"],
17969 &[b"SET", b"s2", b"mynewtext"],
17970 &[b"LCS", b"s1", b"s2"],
17971 &[b"LCS", b"s1", b"s2", b"LEN"],
17972 &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
17973 &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
17974 &[b"LCS", b"s1", b"gone"],
17975 &[b"MSET", b"odd"],
17977 &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
17978 &[b"MGET"],
17979 ];
17980
17981 let mut one = Fixture::new();
17982 let mut many = Fixture::striped(8);
17983 for parts in script {
17984 let a = one.run(parts);
17985 let b = many.run(parts);
17986 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
17987 }
17988 }
17989
17990 #[test]
17995 fn a_striped_database_spreads_the_keys_it_is_given() {
17996 let mut f = Fixture::striped(8);
17997 for i in 0..256 {
17998 let key = format!("key:{i}");
17999 f.run(&[b"SET", key.as_bytes(), b"v"]);
18000 }
18001 assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
18002 }
18003
18004 #[test]
18007 fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
18008 let mut one = Fixture::new();
18009 let mut many = Fixture::striped(8);
18010 for f in [&mut one, &mut many] {
18011 f.run(&[b"SET", b"str", b"v"]);
18012 f.server
18018 .striped(0)
18019 .at(b"list")
18020 .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18021 .expect("a new list");
18022 }
18023 assert_eq!(
18024 one.run(&[b"MGET", b"str", b"list", b"gone"]),
18025 many.run(&[b"MGET", b"str", b"list", b"gone"])
18026 );
18027 }
18028
18029 #[test]
18037 fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18038 let script: &[&[&[u8]]] = &[
18039 &[b"SET", b"k1", b"v1"],
18040 &[b"SET", b"k2", b"v2"],
18041 &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18042 &[b"TYPE", b"k1"],
18043 &[b"TYPE", b"gone"],
18044 &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18045 &[b"EXPIRE", b"k1", b"100"],
18046 &[b"TTL", b"k1"],
18047 &[b"EXPIRE", b"k1", b"200", b"NX"],
18048 &[b"PERSIST", b"k1"],
18049 &[b"TTL", b"k1"],
18050 &[b"PEXPIREAT", b"k2", b"1900000000000"],
18051 &[b"EXPIRETIME", b"k2"],
18052 &[b"PEXPIRETIME", b"k2"],
18053 &[b"PERSIST", b"k2"],
18054 &[b"OBJECT", b"ENCODING", b"k1"],
18055 &[b"OBJECT", b"REFCOUNT", b"k1"],
18056 &[b"OBJECT", b"IDLETIME", b"k1"],
18057 &[b"OBJECT", b"FREQ", b"k1"],
18058 &[b"OBJECT", b"ENCODING", b"gone"],
18059 &[b"OBJECT", b"HELP"],
18060 &[b"RENAME", b"k1", b"k9"],
18061 &[b"GET", b"k9"],
18062 &[b"RENAME", b"gone", b"x"],
18063 &[b"RENAMENX", b"k9", b"k2"],
18064 &[b"RENAMENX", b"k9", b"k8"],
18065 &[b"GET", b"k8"],
18066 &[b"COPY", b"k8", b"c1"],
18067 &[b"COPY", b"k8", b"c1"],
18068 &[b"COPY", b"k8", b"c1", b"REPLACE"],
18069 &[b"COPY", b"k8", b"k8"],
18070 &[b"COPY", b"gone", b"c2"],
18071 &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18072 &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18073 &[b"MOVE", b"c1", b"1"],
18074 &[b"MOVE", b"c1", b"1"],
18075 &[b"MOVE", b"k8", b"0"],
18076 &[b"DEL", b"k2", b"gone"],
18077 &[b"UNLINK", b"k8", b"k8"],
18078 &[b"DBSIZE"],
18079 ];
18080
18081 let mut one = Fixture::new();
18082 let mut many = Fixture::striped(8);
18083 for parts in script {
18084 let a = one.run(parts);
18085 let b = many.run(parts);
18086 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18087 }
18088
18089 for f in [&mut one, &mut many] {
18095 f.run(&[b"SET", b"d1", b"payload"]);
18096 let payload = f
18097 .server
18098 .striped(0)
18099 .at(b"d1")
18100 .dump(b"d1")
18101 .expect("a key that is there");
18102 assert!(
18103 f.run(&[b"DUMP", b"d1"])
18104 .starts_with(&format!("${}", payload.len())),
18105 "a payload of the length the store gave"
18106 );
18107 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18108 assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18109 assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18110 assert_eq!(
18111 f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18112 "-BUSYKEY Target key name already exists.\r\n"
18113 );
18114 assert_eq!(
18115 f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18116 "-ERR DUMP payload version or checksum are wrong\r\n"
18117 );
18118 }
18119 }
18120
18121 #[test]
18128 fn a_scan_of_a_striped_database_walks_all_of_it() {
18129 let mut f = Fixture::striped(8);
18130 for i in 0..500 {
18131 let key = format!("key:{i}");
18132 f.run(&[b"SET", key.as_bytes(), b"v"]);
18133 }
18134
18135 let mut seen = Vec::new();
18136 let mut cursor = "0".to_owned();
18137 let mut calls = 0;
18138 loop {
18139 let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18140 let (next, keys) = scan_reply(&reply);
18141 seen.extend(keys);
18142 cursor = next;
18143 calls += 1;
18144 assert!(calls < 5_000, "a scan that will not finish");
18145 if cursor == "0" {
18146 break;
18147 }
18148 }
18149 seen.sort();
18150 assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18151 assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18152
18153 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18157 let (_, keys) = scan_reply(&reply);
18158 assert_eq!(keys.len(), 10, "key:40 through key:49");
18159 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18160 let (_, keys) = scan_reply(&reply);
18161 assert!(keys.is_empty(), "nothing here is a list");
18162 }
18163
18164 #[test]
18170 fn a_random_key_can_come_from_any_stripe() {
18171 let mut f = Fixture::striped(8);
18172 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18173 for i in 0..200 {
18174 let key = format!("key:{i}");
18175 f.run(&[b"SET", key.as_bytes(), b"v"]);
18176 }
18177 let mut homes = std::collections::HashSet::new();
18178 for _ in 0..200 {
18179 let got = f.run(&[b"RANDOMKEY"]);
18180 let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18181 assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18182 homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18183 }
18184 assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18185 }
18186
18187 fn apart(f: &mut Fixture, src: &str) -> String {
18191 let home = f.server.striped(0).stripe_of(src.as_bytes());
18192 for i in 0..1_000 {
18193 let dst = format!("dst:{i}");
18194 if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18195 return dst;
18196 }
18197 }
18198 panic!("eight stripes and a thousand keys all landed in one place");
18199 }
18200
18201 #[test]
18204 fn a_rename_across_stripes_takes_everything_with_it() {
18205 let mut f = Fixture::striped(8);
18206 let dst = apart(&mut f, "src");
18207 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18208
18209 f.run(&[b"SET", src, b"v"]);
18210 f.run(&[b"EXPIRE", src, b"100"]);
18211 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18212 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18213 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18214 assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18215
18216 f.server
18221 .striped(0)
18222 .at(src)
18223 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18224 .expect("a new list");
18225 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18226 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18227 assert_eq!(
18228 f.server.striped(0).at(dst).llen(dst).expect("a list"),
18229 2,
18230 "the members are on the stripe the key moved to"
18231 );
18232
18233 f.run(&[b"SET", src, b"v"]);
18236 assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18237 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18238 assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18239 }
18240
18241 #[test]
18243 fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18244 let mut f = Fixture::striped(8);
18245 let dst = apart(&mut f, "src");
18246 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18247
18248 f.run(&[b"SET", src, b"v"]);
18249 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18250 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18251 assert_eq!(
18252 f.run(&[b"COPY", src, dst]),
18253 ":0\r\n",
18254 "the destination is taken"
18255 );
18256 f.run(&[b"SET", src, b"w"]);
18257 assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18258 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18259
18260 f.run(&[b"DEL", src, dst]);
18264 f.server
18265 .striped(0)
18266 .at(src)
18267 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18268 .expect("a new list");
18269 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18270 f.server
18271 .striped(0)
18272 .at(src)
18273 .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18274 .expect("a list that is there");
18275 assert_eq!(f.server.striped(0).at(src).llen(src).expect("a list"), 3);
18276 assert_eq!(f.server.striped(0).at(dst).llen(dst).expect("a list"), 2);
18277 }
18278
18279 #[test]
18287 fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18288 let script: &[&[&[u8]]] = &[
18289 &[b"SET", b"k1", b"foobar"],
18290 &[b"SETBIT", b"b1", b"7", b"1"],
18291 &[b"SETBIT", b"b1", b"7", b"0"],
18292 &[b"GETBIT", b"k1", b"6"],
18293 &[b"GETBIT", b"k1", b"100"],
18294 &[b"BITCOUNT", b"k1"],
18295 &[b"BITCOUNT", b"k1", b"0", b"0"],
18296 &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18297 &[b"BITPOS", b"k1", b"1"],
18298 &[b"BITPOS", b"k1", b"0", b"2"],
18299 &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18300 &[
18301 b"BITFIELD",
18302 b"bf",
18303 b"SET",
18304 b"u8",
18305 b"0",
18306 b"255",
18307 b"GET",
18308 b"u8",
18309 b"0",
18310 ],
18311 &[
18312 b"BITFIELD",
18313 b"bf",
18314 b"OVERFLOW",
18315 b"SAT",
18316 b"INCRBY",
18317 b"u8",
18318 b"0",
18319 b"10",
18320 ],
18321 &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18322 &[b"SET", b"s1", b"abc"],
18325 &[b"SET", b"s2", b"abd"],
18326 &[b"SET", b"s3", b"a"],
18327 &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18328 &[b"GET", b"d1"],
18329 &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18330 &[b"GET", b"d2"],
18331 &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18332 &[b"STRLEN", b"d3"],
18333 &[b"BITOP", b"NOT", b"d4", b"s1"],
18334 &[b"STRLEN", b"d4"],
18335 &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18336 &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18337 &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18338 &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18339 &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18342 &[b"EXISTS", b"d1"],
18343 &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18344 &[b"GET", b"d9"],
18345 &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18349 &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18350 &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18351 &[b"BITOP", b"DIFF", b"d1", b"s1"],
18352 &[b"BITOP", b"NOPE", b"d1", b"s1"],
18353 &[b"BITCOUNT", b"list"],
18354 &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18355 ];
18356
18357 let mut one = Fixture::new();
18358 let mut many = Fixture::striped(8);
18359 for f in [&mut one, &mut many] {
18360 plant_list(f, b"list");
18361 }
18362 for parts in script {
18363 let a = one.run(parts);
18364 let b = many.run(parts);
18365 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18366 }
18367 }
18368
18369 fn plant_list(f: &mut Fixture, key: &[u8]) {
18375 f.server
18376 .striped(0)
18377 .at(key)
18378 .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18379 .expect("a new list");
18380 }
18381
18382 #[test]
18389 fn a_bitop_across_stripes_reads_every_source() {
18390 let mut f = Fixture::striped(8);
18391 let other = apart(&mut f, "src");
18392 let (src, far) = (b"src".as_slice(), other.as_bytes());
18393 assert_ne!(
18394 f.server.striped(0).stripe_of(src),
18395 f.server.striped(0).stripe_of(far),
18396 "the two keys are the point of the test"
18397 );
18398
18399 f.run(&[b"SET", src, b"abc"]);
18400 f.run(&[b"SET", far, b"abd"]);
18401 assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
18402 assert_eq!(
18403 f.run(&[b"GET", far]),
18404 "$3\r\nab`\r\n",
18405 "a destination that is also a source"
18406 );
18407 f.run(&[b"SET", far, b"abd"]);
18408 assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
18409 assert_eq!(
18410 f.run(&[b"GET", src]),
18411 "$3\r\n\0\0\x07\r\n",
18412 "and the other way round"
18413 );
18414
18415 f.run(&[b"SET", src, b"abc"]);
18419 f.run(&[b"DEL", far]);
18420 assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
18421 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
18422 f.run(&[b"SET", src, b"abc"]);
18423 f.run(&[b"DEL", far]);
18424 plant_list(&mut f, far);
18425 assert_eq!(
18426 f.run(&[b"BITOP", b"OR", b"out", src, far]),
18427 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18428 );
18429 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
18430 }
18431
18432 #[test]
18434 fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
18435 let script: &[&[&[u8]]] = &[
18436 &[b"PFADD", b"h1", b"a", b"b", b"c"],
18437 &[b"PFADD", b"h1", b"a"],
18438 &[b"PFADD", b"h2"],
18439 &[b"PFADD", b"h2", b"c", b"d", b"e"],
18440 &[b"PFCOUNT", b"h1"],
18441 &[b"PFCOUNT", b"h2"],
18442 &[b"PFCOUNT", b"missing"],
18443 &[b"PFCOUNT", b"h1", b"h2"],
18445 &[b"PFCOUNT", b"h1", b"missing"],
18446 &[b"PFMERGE", b"m", b"h1", b"h2"],
18447 &[b"PFCOUNT", b"m"],
18448 &[b"STRLEN", b"m"],
18449 &[b"PFMERGE", b"m"],
18450 &[b"PFCOUNT", b"m"],
18451 &[b"PFMERGE", b"m2", b"missing"],
18452 &[b"PFCOUNT", b"m2"],
18453 &[b"PFDEBUG", b"ENCODING", b"h1"],
18456 &[b"PFDEBUG", b"DECODE", b"h1"],
18457 &[b"PFDEBUG", b"TODENSE", b"h1"],
18458 &[b"PFDEBUG", b"ENCODING", b"h1"],
18459 &[b"PFDEBUG", b"TODENSE", b"h1"],
18460 &[b"PFCOUNT", b"h1", b"h2"],
18461 &[b"PFSELFTEST"],
18462 &[b"SET", b"plain", b"not a sketch at all"],
18464 &[b"PFADD", b"plain", b"a"],
18465 &[b"PFCOUNT", b"plain"],
18466 &[b"PFCOUNT", b"h1", b"plain"],
18467 &[b"PFMERGE", b"plain", b"h1"],
18468 &[b"PFMERGE", b"m", b"plain"],
18469 &[b"PFDEBUG", b"ENCODING", b"gone"],
18470 &[b"PFDEBUG", b"NOPE", b"h1"],
18471 ];
18472
18473 let mut one = Fixture::new();
18474 let mut many = Fixture::striped(8);
18475 for parts in script {
18476 let a = one.run(parts);
18477 let b = many.run(parts);
18478 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18479 }
18480 }
18481
18482 #[test]
18491 fn the_set_group_answers_the_same_however_many_stripes_there_are() {
18492 const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
18493 let script: &[&[&[u8]]] = &[
18494 &[b"SADD", b"s1", b"a", b"b", b"c"],
18495 &[b"SADD", b"s1", b"a"],
18496 &[b"SADD", b"s2", b"b", b"c", b"d"],
18497 &[b"SADD", b"ints", b"1", b"2", b"3"],
18498 &[b"SCARD", b"s1"],
18499 &[b"SISMEMBER", b"s1", b"a"],
18500 &[b"SISMEMBER", b"s1", b"z"],
18501 &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
18502 &[b"SMEMBERS", b"s1"],
18503 &[b"SREM", b"s1", b"c"],
18504 &[b"SADD", b"s1", b"c"],
18505 &[b"SSCAN", b"s1", b"0"],
18506 &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
18507 &[b"SADD", b"one", b"m"],
18510 &[b"SRANDMEMBER", b"one"],
18511 &[b"SRANDMEMBER", b"one", b"-3"],
18512 &[b"SRANDMEMBER", b"gone"],
18513 &[b"SPOP", b"one"],
18514 &[b"SPOP", b"one"],
18515 &[b"SPOP", b"gone", b"2"],
18516 &[b"SMOVE", b"s1", b"s2", b"a"],
18518 &[b"SMOVE", b"s1", b"s2", b"zzz"],
18519 &[b"SMOVE", b"gone", b"s2", b"a"],
18520 &[b"SMEMBERS", b"s1"],
18521 &[b"SMEMBERS", b"s2"],
18522 &[b"SINTER", b"s1", b"s2"],
18524 &[b"SUNION", b"s1", b"s2"],
18525 &[b"SDIFF", b"s2", b"s1"],
18526 &[b"SINTER", b"s1", b"gone"],
18527 &[b"SUNION", b"s1", b"gone"],
18528 &[b"SDIFF", b"gone", b"s1"],
18529 &[b"SINTER", b"ints", b"s1"],
18530 &[b"SINTERCARD", b"2", b"s1", b"s2"],
18531 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
18532 &[b"SUNIONCARD", b"2", b"s1", b"s2"],
18533 &[b"SDIFFCARD", b"2", b"s2", b"s1"],
18534 &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
18535 &[b"SMEMBERS", b"d1"],
18536 &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
18537 &[b"SCARD", b"d2"],
18538 &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
18539 &[b"SCARD", b"d3"],
18540 &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
18543 &[b"EXISTS", b"d4"],
18544 &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
18546 &[b"SCARD", b"s2"],
18547 &[b"SET", b"str", b"v"],
18549 &[b"SADD", b"str", b"a"],
18550 &[b"SINTER", b"s1", b"str"],
18551 &[b"SINTERSTORE", b"d5", b"s1", b"str"],
18552 &[b"EXISTS", b"d5"],
18553 &[b"SMOVE", b"str", b"s2", b"a"],
18554 &[b"SMOVE", b"s1", b"str", b"b"],
18555 &[b"SMOVE", b"gone", b"str", b"b"],
18556 &[b"SINTERCARD", b"0", b"s1"],
18557 &[b"SINTERCARD", b"3", b"s1", b"s2"],
18558 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
18559 &[b"SPOP", b"s1", b"-1"],
18560 ];
18561
18562 let mut one = Fixture::new();
18563 let mut many = Fixture::striped(8);
18564 for parts in script {
18565 let a = one.run(parts);
18566 let b = many.run(parts);
18567 let name = String::from_utf8_lossy(parts[0]).to_uppercase();
18568 if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
18569 assert_eq!(sorted(&a), sorted(&b), "{name}");
18570 } else {
18571 assert_eq!(a, b, "{name}");
18572 }
18573 }
18574 }
18575
18576 #[test]
18578 fn a_set_operation_across_stripes_reads_every_set() {
18579 let mut f = Fixture::striped(8);
18580 let second = apart(&mut f, "s1");
18581 let third = apart(&mut f, &second);
18582 let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
18583
18584 f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
18585 f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
18586 assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
18587 assert_eq!(
18588 sorted(&f.run(&[b"SUNION", s1, s2])),
18589 ["a", "b", "c", "d"],
18590 "a union of two stripes is both of them"
18591 );
18592 assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
18593 assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
18594 assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
18595 assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
18596
18597 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
18599 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
18600 assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
18601 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
18602 assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
18603 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
18604
18605 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
18608 assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
18609 f.run(&[b"SET", s3, b"v"]);
18610 assert_eq!(
18611 f.run(&[b"SINTER", s1, s3]),
18612 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18613 );
18614 assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
18615 }
18616
18617 #[test]
18619 fn a_move_across_stripes_takes_the_member_with_it() {
18620 let mut f = Fixture::striped(8);
18621 let other = apart(&mut f, "src");
18622 let (src, dst) = (b"src".as_slice(), other.as_bytes());
18623
18624 f.run(&[b"SADD", src, b"a", b"b"]);
18625 f.run(&[b"SADD", dst, b"c"]);
18626 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
18627 assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
18628 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
18629 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
18630
18631 f.run(&[b"DEL", dst]);
18634 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
18635 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
18636 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
18637
18638 f.run(&[b"SET", dst, b"v"]);
18642 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
18643 f.run(&[b"SADD", src, b"b"]);
18644 assert_eq!(
18645 f.run(&[b"SMOVE", src, dst, b"b"]),
18646 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18647 );
18648 }
18649
18650 #[test]
18652 fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
18653 let mut f = Fixture::striped(8);
18654 let other = apart(&mut f, "src");
18655 let (src, far) = (b"src".as_slice(), other.as_bytes());
18656
18657 for i in 0..150 {
18658 let ele = format!("e:{i}");
18659 f.run(&[b"PFADD", src, ele.as_bytes()]);
18660 }
18661 for i in 150..200 {
18662 let ele = format!("e:{i}");
18663 f.run(&[b"PFADD", far, ele.as_bytes()]);
18664 }
18665 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
18668 assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
18669 assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
18670
18671 let dest = apart(&mut f, &other);
18674 assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
18675 assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
18676 assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
18677 assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
18678 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
18679 }
18680
18681 #[test]
18688 fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
18689 let script: &[&[&[u8]]] = &[
18690 &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
18691 &[b"ZADD", b"z1", b"NX", b"9", b"a"],
18692 &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
18693 &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
18694 &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
18695 &[b"ZINCRBY", b"z1", b"1.5", b"b"],
18696 &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
18697 &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
18698 &[b"ZADD", b"one", b"1", b"m"],
18699 &[b"ZCARD", b"z1"],
18700 &[b"ZCARD", b"gone"],
18701 &[b"ZSCORE", b"z1", b"a"],
18702 &[b"ZSCORE", b"z1", b"zz"],
18703 &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
18704 &[b"ZRANK", b"z1", b"c"],
18705 &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
18706 &[b"ZREVRANK", b"z1", b"c"],
18707 &[b"ZRANK", b"z1", b"gone"],
18708 &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
18709 &[b"ZCOUNT", b"z1", b"(1", b"3"],
18710 &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
18711 &[b"ZRANGE", b"z1", b"0", b"-1"],
18713 &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
18714 &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
18715 &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
18716 &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
18717 &[b"ZREVRANGE", b"z1", b"0", b"-1"],
18718 &[
18719 b"ZRANGEBYSCORE",
18720 b"z1",
18721 b"-inf",
18722 b"+inf",
18723 b"LIMIT",
18724 b"1",
18725 b"1",
18726 ],
18727 &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
18728 &[b"ZSCAN", b"z1", b"0"],
18729 &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
18730 &[b"ZRANDMEMBER", b"one"],
18733 &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
18734 &[b"ZRANDMEMBER", b"gone"],
18735 &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
18737 &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
18738 &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
18739 &[b"EXISTS", b"d0"],
18740 &[b"ZUNION", b"2", b"z1", b"z2"],
18742 &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
18743 &[
18744 b"ZUNION",
18745 b"2",
18746 b"z1",
18747 b"z2",
18748 b"WEIGHTS",
18749 b"2",
18750 b"3",
18751 b"AGGREGATE",
18752 b"MAX",
18753 b"WITHSCORES",
18754 ],
18755 &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
18756 &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
18757 &[b"ZDIFF", b"2", b"gone", b"z1"],
18758 &[b"ZINTERCARD", b"2", b"z1", b"z2"],
18759 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
18760 &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
18761 &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
18762 &[
18763 b"ZINTERSTORE",
18764 b"d2",
18765 b"2",
18766 b"z1",
18767 b"z2",
18768 b"AGGREGATE",
18769 b"MIN",
18770 ],
18771 &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
18772 &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
18773 &[b"ZCARD", b"d3"],
18774 &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
18777 &[b"EXISTS", b"d4"],
18778 &[b"SADD", b"plain", b"a", b"x"],
18781 &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
18782 &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
18783 &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
18785 &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
18786 &[b"ZREM", b"d5", b"x", b"nothere"],
18788 &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
18789 &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
18790 &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
18791 &[b"ZPOPMIN", b"z1"],
18792 &[b"ZPOPMAX", b"z1", b"2"],
18793 &[b"ZPOPMIN", b"gone"],
18794 &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
18795 &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
18796 &[b"SET", b"str", b"v"],
18798 &[b"ZADD", b"str", b"1", b"a"],
18799 &[b"ZSCORE", b"str", b"a"],
18800 &[b"ZADD", b"z1", b"nan", b"a"],
18801 &[b"ZUNION", b"2", b"z1", b"str"],
18802 &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
18803 &[b"EXISTS", b"d6"],
18804 &[b"ZINTERCARD", b"0", b"z1"],
18805 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
18806 &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
18807 &[b"ZMPOP", b"1", b"str", b"MIN"],
18808 &[b"ZPOPMIN", b"z1", b"-1"],
18809 ];
18810
18811 let mut one = Fixture::new();
18812 let mut many = Fixture::striped(8);
18813 for parts in script {
18814 let a = one.run(parts);
18815 let b = many.run(parts);
18816 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18817 }
18818 }
18819
18820 #[test]
18822 fn a_sorted_set_operation_across_stripes_reads_every_input() {
18823 let mut f = Fixture::striped(8);
18824 let second = apart(&mut f, "z1");
18825 let third = apart(&mut f, &second);
18826 let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
18827
18828 f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
18829 f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
18830 assert_eq!(
18833 f.run(&[b"ZUNION", b"2", z1, z2]),
18834 "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
18835 );
18836 assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
18837 assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
18838 assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
18839 assert_eq!(
18840 f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
18841 ":1\r\n"
18842 );
18843
18844 assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
18847 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
18848 assert_eq!(
18849 f.run(&[
18850 b"ZUNIONSTORE",
18851 z3,
18852 b"2",
18853 z1,
18854 z2,
18855 b"WEIGHTS",
18856 b"2",
18857 b"3",
18858 b"AGGREGATE",
18859 b"MAX"
18860 ]),
18861 ":3\r\n"
18862 );
18863 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
18864 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
18865 assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
18866 assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
18867 assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
18868
18869 let popped = format!(
18872 "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
18873 second.len()
18874 );
18875 assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
18876 f.run(&[b"ZADD", z2, b"3", b"b"]);
18877
18878 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
18881 assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
18882 f.run(&[b"SET", z3, b"v"]);
18883 assert_eq!(
18884 f.run(&[b"ZUNION", b"2", z1, z3]),
18885 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18886 );
18887 assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
18888
18889 assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
18893 assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
18894 assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
18895 }
18896
18897 #[test]
18899 fn a_range_store_across_stripes_copies_the_window() {
18900 let mut f = Fixture::striped(8);
18901 let other = apart(&mut f, "src");
18902 let third = apart(&mut f, &other);
18903 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
18904
18905 f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
18906 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
18907 assert_eq!(
18908 f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
18909 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
18910 );
18911 assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
18912
18913 assert_eq!(
18916 f.run(&[
18917 b"ZRANGESTORE",
18918 dst,
18919 src,
18920 b"+inf",
18921 b"-inf",
18922 b"BYSCORE",
18923 b"REV",
18924 b"LIMIT",
18925 b"0",
18926 b"2"
18927 ]),
18928 ":2\r\n"
18929 );
18930 assert_eq!(
18931 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
18932 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
18933 );
18934
18935 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
18938 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
18939 f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
18940 f.run(&[b"SET", plain, b"v"]);
18941 assert_eq!(
18942 f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
18943 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18944 );
18945 assert_eq!(
18946 f.run(&[b"ZCARD", dst]),
18947 ":3\r\n",
18948 "and left the destination"
18949 );
18950 }
18951
18952 #[test]
18959 fn the_list_group_answers_the_same_however_many_stripes_there_are() {
18960 let script: &[&[&[u8]]] = &[
18961 &[b"RPUSH", b"l1", b"a", b"b", b"c"],
18962 &[b"LPUSH", b"l1", b"z"],
18963 &[b"RPUSHX", b"l1", b"d"],
18964 &[b"LPUSHX", b"gone", b"x"],
18965 &[b"RPUSHX", b"gone", b"x"],
18966 &[b"LLEN", b"l1"],
18967 &[b"LLEN", b"gone"],
18968 &[b"LRANGE", b"l1", b"0", b"-1"],
18969 &[b"LRANGE", b"l1", b"1", b"2"],
18970 &[b"LRANGE", b"l1", b"5", b"9"],
18971 &[b"LINDEX", b"l1", b"0"],
18972 &[b"LINDEX", b"l1", b"-1"],
18973 &[b"LINDEX", b"l1", b"99"],
18974 &[b"LSET", b"l1", b"0", b"y"],
18975 &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
18976 &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
18977 &[b"LPOS", b"l1", b"b"],
18978 &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
18979 &[b"LPOS", b"l1", b"nothere"],
18980 &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
18981 &[b"LREM", b"l1", b"1", b"aa"],
18982 &[b"LTRIM", b"l1", b"0", b"3"],
18983 &[b"LRANGE", b"l1", b"0", b"-1"],
18984 &[b"LPOP", b"l1"],
18985 &[b"RPOP", b"l1"],
18986 &[b"LPOP", b"l1", b"2"],
18987 &[b"LPOP", b"gone"],
18988 &[b"LPOP", b"gone", b"2"],
18989 &[b"EXISTS", b"l1"],
18990 &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
18993 &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
18994 &[b"RPOPLPUSH", b"src", b"dst"],
18995 &[b"LRANGE", b"dst", b"0", b"-1"],
18996 &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
18997 &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
18998 &[
18999 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
19000 ],
19001 &[
19002 b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
19003 ],
19004 &[b"LRANGE", b"dst", b"0", b"-1"],
19005 &[
19006 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
19007 ],
19008 &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19009 &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19010 &[b"LMPOP", b"1", b"gone", b"LEFT"],
19011 &[b"RPUSH", b"q", b"a", b"b", b"c"],
19014 &[b"BLPOP", b"gone", b"q", b"0"],
19015 &[b"BRPOP", b"q", b"0"],
19016 &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19017 &[b"RPUSH", b"q", b"x", b"y", b"z"],
19018 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19019 &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19020 &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19021 &[b"BLPOP", b"q", b"0"],
19022 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19023 &[b"SET", b"plain", b"v"],
19025 &[b"LPUSH", b"plain", b"a"],
19026 &[b"LLEN", b"plain"],
19027 &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19028 &[b"LRANGE", b"dst", b"0", b"-1"],
19029 &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19030 &[b"LSET", b"gone", b"0", b"v"],
19031 &[b"LSET", b"dst", b"99", b"v"],
19032 &[b"LPOP", b"dst", b"-1"],
19033 &[b"LMPOP", b"0", b"dst", b"LEFT"],
19034 &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19035 ];
19036
19037 let mut one = Fixture::new();
19038 let mut many = Fixture::striped(8);
19039 for parts in script {
19040 let a = one.run(parts);
19041 let b = many.run(parts);
19042 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19043 }
19044 }
19045
19046 #[test]
19048 fn a_list_move_across_stripes_takes_the_elements_with_it() {
19049 let mut f = Fixture::striped(8);
19050 let other = apart(&mut f, "src");
19051 let third = apart(&mut f, &other);
19052 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19053
19054 f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19055 assert_eq!(
19056 f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19057 "$1\r\na\r\n"
19058 );
19059 assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19060 assert_eq!(
19061 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19062 "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19063 "one went on each end of the destination"
19064 );
19065 assert_eq!(
19066 f.run(&[b"LRANGE", src, b"0", b"-1"]),
19067 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19068 );
19069
19070 assert_eq!(
19072 f.run(&[
19073 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19074 ]),
19075 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19076 );
19077 assert_eq!(
19078 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19079 "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19080 );
19081 assert_eq!(
19082 f.run(&[b"EXISTS", src]),
19083 ":0\r\n",
19084 "and the source is gone with its last element"
19085 );
19086
19087 f.run(&[b"RPUSH", src, b"e", b"f"]);
19090 assert_eq!(
19091 f.run(&[
19092 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19093 ]),
19094 "*-1\r\n"
19095 );
19096 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19097 assert_eq!(
19098 f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19099 "$-1\r\n"
19100 );
19101 assert_eq!(
19102 f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19103 "*-1\r\n"
19104 );
19105
19106 f.run(&[b"SET", plain, b"v"]);
19110 assert_eq!(
19111 f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19112 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19113 );
19114 assert_eq!(
19115 f.run(&[b"LLEN", src]),
19116 ":2\r\n",
19117 "and left the source alone"
19118 );
19119 assert_eq!(
19120 f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19121 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19122 );
19123 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19124 }
19125
19126 #[test]
19132 fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19133 let mut f = Fixture::striped(8);
19134 let other = apart(&mut f, "q");
19135 let (q, far) = (b"q".as_slice(), other.as_bytes());
19136
19137 assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19138 assert_eq!(f.server.waiters().len(), 1);
19139 f.run(&[b"RPUSH", far, b"v"]);
19140 let mut out = Out::new(Proto::Resp2);
19141 assert!(f.server.serve_waiter(0, 0, &mut out));
19142 let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19143 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19144 assert_eq!(
19145 f.run(&[b"EXISTS", far]),
19146 ":0\r\n",
19147 "and it took the element with it"
19148 );
19149
19150 f.server.waiters_mut().forget(7);
19153 assert_eq!(
19154 f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19155 Flow::Block
19156 );
19157 f.run(&[b"RPUSH", q, b"w"]);
19158 let mut out = Out::new(Proto::Resp2);
19159 assert!(f.server.serve_waiter(0, 0, &mut out));
19160 assert_eq!(
19161 core::str::from_utf8(out.as_slice()).expect("ascii"),
19162 "$1\r\nw\r\n"
19163 );
19164 assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19165 }
19166
19167 #[test]
19173 fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19174 let script: &[&[&[u8]]] = &[
19175 &[b"XADD", b"s", b"1-1", b"a", b"1"],
19176 &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19177 &[b"XADD", b"s", b"3-1", b"d", b"4"],
19178 &[b"XADD", b"s", b"1-1", b"e", b"5"],
19179 &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19180 &[b"XLEN", b"s"],
19181 &[b"XLEN", b"gone"],
19182 &[b"XRANGE", b"s", b"-", b"+"],
19183 &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19184 &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19185 &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19186 &[b"XREVRANGE", b"s", b"+", b"-"],
19187 &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19188 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19189 &[b"XREAD", b"STREAMS", b"s", b"$"],
19190 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19192 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19193 &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19194 &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19195 &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19196 &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19197 &[
19198 b"XREADGROUP",
19199 b"GROUP",
19200 b"g",
19201 b"c1",
19202 b"COUNT",
19203 b"1",
19204 b"STREAMS",
19205 b"s",
19206 b"0",
19207 ],
19208 &[
19209 b"XREADGROUP",
19210 b"GROUP",
19211 b"nope",
19212 b"c1",
19213 b"STREAMS",
19214 b"s",
19215 b">",
19216 ],
19217 &[b"XPENDING", b"s", b"g"],
19218 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19219 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19220 &[b"XPENDING", b"s", b"nope"],
19221 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19222 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19223 &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19224 &[b"XACK", b"s", b"g", b"1-1"],
19225 &[b"XACK", b"s", b"g", b"1-1"],
19226 &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19227 &[b"XPENDING", b"s", b"g"],
19228 &[b"XINFO", b"STREAM", b"s"],
19229 &[b"XINFO", b"GROUPS", b"s"],
19230 &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19231 &[b"XINFO", b"STREAM", b"gone"],
19232 &[b"XDEL", b"s", b"3-1"],
19234 &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19235 &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19236 &[b"XADD", b"s", b"9-1", b"z", b"9"],
19237 &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19238 &[b"XTRIM", b"s", b"MINID", b"9"],
19239 &[b"XSETID", b"s", b"99-1"],
19240 &[b"XSETID", b"s", b"1-1"],
19241 &[b"XLEN", b"s"],
19242 &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19243 &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19244 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19245 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19246 &[b"SET", b"plain", b"v"],
19248 &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19249 &[b"XLEN", b"plain"],
19250 &[b"XREAD", b"STREAMS", b"plain", b"0"],
19251 &[b"XRANGE", b"s", b"bogus", b"+"],
19252 &[b"XADD", b"s", b"1-1", b"a"],
19253 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19254 &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19255 ];
19256
19257 let mut one = Fixture::new();
19258 let mut many = Fixture::striped(8);
19259 for parts in script {
19260 let a = one.run(parts);
19261 let b = many.run(parts);
19262 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19263 }
19264 }
19265
19266 #[test]
19272 fn a_stream_read_across_stripes_reads_every_key() {
19273 let mut f = Fixture::striped(8);
19274 let other = apart(&mut f, "s1");
19275 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19276
19277 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19278 f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19279 let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19280 assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19281 assert!(got.contains("1-1"), "the first one is in there: {got}");
19282 assert!(got.contains("2-1"), "and so is the second: {got}");
19283
19284 f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19287 let got = f.run(&[
19288 b"XREADGROUP",
19289 b"GROUP",
19290 b"g",
19291 b"c",
19292 b"STREAMS",
19293 s1,
19294 s2,
19295 b">",
19296 b">",
19297 ]);
19298 assert!(got.starts_with("-NOGROUP"), "{got}");
19299 assert_eq!(
19300 f.run(&[b"XPENDING", s1, b"g"]),
19301 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19302 "and read nothing from the key that did have the group"
19303 );
19304
19305 f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19306 let got = f.run(&[
19307 b"XREADGROUP",
19308 b"GROUP",
19309 b"g",
19310 b"c",
19311 b"STREAMS",
19312 s1,
19313 s2,
19314 b">",
19315 b">",
19316 ]);
19317 assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19318 }
19319
19320 #[test]
19322 fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19323 let mut f = Fixture::striped(8);
19324 let other = apart(&mut f, "s1");
19325 let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19326 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19327 f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19328
19329 assert_eq!(
19330 f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19331 .0,
19332 Flow::Block
19333 );
19334 f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19335 let mut out = Out::new(Proto::Resp2);
19336 assert!(f.server.serve_waiter(0, 0, &mut out));
19337 let want = format!(
19338 "*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",
19339 other.len()
19340 );
19341 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19342 }
19343
19344 #[test]
19346 fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19347 let script: &[&[&[u8]]] = &[
19348 &[
19349 b"JSON.SET",
19350 b"d",
19351 b"$",
19352 br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19353 ],
19354 &[b"JSON.SET", b"d", b"$.a", b"2"],
19355 &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19356 &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19357 &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19358 &[b"JSON.GET", b"d"],
19359 &[b"JSON.GET", b"d", b"$.b"],
19360 &[b"JSON.GET", b"gone", b"$"],
19361 &[b"JSON.TYPE", b"d", b"$.b"],
19362 &[b"JSON.TYPE", b"d", b"$.s"],
19363 &[b"JSON.TOGGLE", b"d", b"$.t"],
19364 &[b"JSON.ARRLEN", b"d", b"$.b"],
19365 &[b"JSON.OBJLEN", b"d", b"$"],
19366 &[b"JSON.OBJKEYS", b"d", b"$"],
19367 &[b"JSON.STRLEN", b"d", b"$.s"],
19368 &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19369 &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19370 &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19371 &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19372 &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19373 &[b"JSON.ARRPOP", b"d", b"$.b"],
19374 &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19375 &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19376 &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19377 &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19378 &[b"JSON.RESP", b"d", b"$.b"],
19379 &[b"JSON.DEBUG", b"MEMORY", b"d"],
19380 &[b"JSON.CLEAR", b"d", b"$.b"],
19381 &[b"JSON.DEL", b"d", b"$.m"],
19382 &[b"JSON.FORGET", b"d", b"$.nothere"],
19383 &[
19385 b"JSON.MSET",
19386 b"m1",
19387 b"$",
19388 b"1",
19389 b"m2",
19390 b"$",
19391 b"2",
19392 b"m3",
19393 b"$",
19394 b"3",
19395 ],
19396 &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
19397 &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
19398 &[b"JSON.GET", b"m1", b"$"],
19399 &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
19400 &[b"JSON.GET", b"m2", b"$"],
19401 &[b"SET", b"plain", b"v"],
19403 &[b"JSON.GET", b"plain", b"$"],
19404 &[b"JSON.SET", b"plain", b"$", b"1"],
19405 &[b"JSON.MGET", b"m1", b"plain", b"$"],
19406 &[b"JSON.SET", b"d", b"$.b", b"["],
19407 &[b"JSON.DEL", b"plain"],
19408 ];
19409
19410 let mut one = Fixture::new();
19411 let mut many = Fixture::striped(8);
19412 for parts in script {
19413 let a = one.run(parts);
19414 let b = many.run(parts);
19415 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19416 }
19417 }
19418
19419 #[test]
19426 fn a_json_multi_write_across_stripes_reaches_every_key() {
19427 let mut f = Fixture::striped(8);
19428 let second = apart(&mut f, "m1");
19429 let third = apart(&mut f, &second);
19430 let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
19431
19432 assert_eq!(
19433 f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
19434 "+OK\r\n"
19435 );
19436 assert_eq!(
19437 f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
19438 "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
19439 );
19440
19441 assert_eq!(
19444 f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
19445 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
19446 );
19447 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
19448
19449 assert_eq!(
19453 f.run(&[
19454 b"JSON.MSET",
19455 m1,
19456 b"$",
19457 b"9",
19458 m2,
19459 b"$.deep",
19460 b"9",
19461 m3,
19462 b"$",
19463 b"7"
19464 ]),
19465 "$-1\r\n"
19466 );
19467 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
19468 assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
19469 assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
19470 }
19471
19472 #[test]
19474 fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
19475 let script: &[&[&[u8]]] = &[
19476 &[
19477 b"GEOADD",
19478 b"g",
19479 b"13.361389",
19480 b"38.115556",
19481 b"palermo",
19482 b"15.087269",
19483 b"37.502669",
19484 b"catania",
19485 ],
19486 &[
19487 b"GEOADD",
19488 b"g",
19489 b"NX",
19490 b"13.361389",
19491 b"38.115556",
19492 b"palermo",
19493 ],
19494 &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
19495 &[b"GEOPOS", b"g", b"palermo", b"nothere"],
19496 &[b"GEOHASH", b"g", b"palermo", b"catania"],
19497 &[b"GEODIST", b"g", b"palermo", b"catania"],
19498 &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
19499 &[b"GEODIST", b"g", b"palermo", b"nothere"],
19500 &[
19501 b"GEOSEARCH",
19502 b"g",
19503 b"FROMLONLAT",
19504 b"15",
19505 b"37",
19506 b"BYRADIUS",
19507 b"200",
19508 b"KM",
19509 b"ASC",
19510 b"WITHCOORD",
19511 b"WITHDIST",
19512 b"WITHHASH",
19513 ],
19514 &[
19515 b"GEOSEARCH",
19516 b"g",
19517 b"FROMMEMBER",
19518 b"palermo",
19519 b"BYBOX",
19520 b"400",
19521 b"400",
19522 b"KM",
19523 b"DESC",
19524 ],
19525 &[
19526 b"GEORADIUS",
19527 b"g",
19528 b"15",
19529 b"37",
19530 b"200",
19531 b"KM",
19532 b"COUNT",
19533 b"1",
19534 ],
19535 &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
19536 &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
19537 &[
19538 b"GEOSEARCHSTORE",
19539 b"dst",
19540 b"g",
19541 b"FROMLONLAT",
19542 b"15",
19543 b"37",
19544 b"BYRADIUS",
19545 b"200",
19546 b"KM",
19547 ],
19548 &[b"ZRANGE", b"dst", b"0", b"-1"],
19549 &[
19550 b"GEOSEARCHSTORE",
19551 b"dst",
19552 b"g",
19553 b"FROMLONLAT",
19554 b"15",
19555 b"37",
19556 b"BYRADIUS",
19557 b"1",
19558 b"M",
19559 b"STOREDIST",
19560 ],
19561 &[b"EXISTS", b"dst"],
19562 &[
19563 b"GEORADIUS",
19564 b"g",
19565 b"15",
19566 b"37",
19567 b"200",
19568 b"KM",
19569 b"STORE",
19570 b"dst",
19571 ],
19572 &[b"ZCARD", b"dst"],
19573 &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
19575 &[b"SET", b"plain", b"v"],
19576 &[b"GEOPOS", b"plain", b"a"],
19577 &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
19578 &[
19579 b"GEOSEARCHSTORE",
19580 b"dst",
19581 b"g",
19582 b"FROMLONLAT",
19583 b"15",
19584 b"37",
19585 b"BYRADIUS",
19586 b"200",
19587 b"KM",
19588 b"WITHCOORD",
19589 ],
19590 ];
19591
19592 let mut one = Fixture::new();
19593 let mut many = Fixture::striped(8);
19594 for parts in script {
19595 let a = one.run(parts);
19596 let b = many.run(parts);
19597 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19598 }
19599 }
19600
19601 #[test]
19603 fn a_geo_search_store_across_stripes_writes_what_it_found() {
19604 let mut f = Fixture::striped(8);
19605 let other = apart(&mut f, "g");
19606 let third = apart(&mut f, &other);
19607 let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
19608
19609 f.run(&[
19610 b"GEOADD",
19611 g,
19612 b"13.361389",
19613 b"38.115556",
19614 b"palermo",
19615 b"15.087269",
19616 b"37.502669",
19617 b"catania",
19618 ]);
19619 assert_eq!(
19620 f.run(&[
19621 b"GEOSEARCHSTORE",
19622 dst,
19623 g,
19624 b"FROMLONLAT",
19625 b"15",
19626 b"37",
19627 b"BYRADIUS",
19628 b"200",
19629 b"KM",
19630 b"ASC",
19631 ]),
19632 ":2\r\n"
19633 );
19634 assert_eq!(
19635 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19636 "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
19637 "the geohash is the score, so the order is not the search order"
19638 );
19639 assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
19640
19641 assert_eq!(
19644 f.run(&[
19645 b"GEOSEARCHSTORE",
19646 dst,
19647 g,
19648 b"FROMMEMBER",
19649 b"palermo",
19650 b"BYRADIUS",
19651 b"200",
19652 b"KM",
19653 b"STOREDIST",
19654 ]),
19655 ":2\r\n"
19656 );
19657 assert_eq!(
19658 f.run(&[b"ZSCORE", dst, b"palermo"]),
19659 "$1\r\n0\r\n",
19660 "the centre is nought away from itself"
19661 );
19662
19663 assert_eq!(
19667 f.run(&[
19668 b"GEOSEARCHSTORE",
19669 dst,
19670 g,
19671 b"FROMLONLAT",
19672 b"0",
19673 b"0",
19674 b"BYRADIUS",
19675 b"1",
19676 b"M",
19677 ]),
19678 ":0\r\n"
19679 );
19680 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19681 f.run(&[
19682 b"GEOSEARCHSTORE",
19683 dst,
19684 g,
19685 b"FROMLONLAT",
19686 b"15",
19687 b"37",
19688 b"BYRADIUS",
19689 b"200",
19690 b"KM",
19691 ]);
19692 f.run(&[b"SET", plain, b"v"]);
19693 assert_eq!(
19694 f.run(&[
19695 b"GEOSEARCHSTORE",
19696 dst,
19697 plain,
19698 b"FROMLONLAT",
19699 b"15",
19700 b"37",
19701 b"BYRADIUS",
19702 b"200",
19703 b"KM",
19704 ]),
19705 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19706 );
19707 assert_eq!(
19708 f.run(&[b"ZCARD", dst]),
19709 ":2\r\n",
19710 "and left the destination"
19711 );
19712 }
19713
19714 #[test]
19720 fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
19721 let script: &[&[&[u8]]] = &[
19722 &[
19723 b"TS.CREATE",
19724 b"ts:a",
19725 b"LABELS",
19726 b"sensor",
19727 b"a",
19728 b"room",
19729 b"1",
19730 ],
19731 &[b"TS.CREATE", b"ts:a"],
19732 &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
19733 &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
19734 &[
19735 b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
19736 ],
19737 &[
19738 b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
19739 ],
19740 &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
19741 &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
19742 &[b"TS.GET", b"ts:a"],
19743 &[b"TS.GET", b"gone"],
19744 &[b"TS.RANGE", b"ts:a", b"-", b"+"],
19745 &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
19746 &[
19747 b"TS.RANGE",
19748 b"ts:a",
19749 b"-",
19750 b"+",
19751 b"AGGREGATION",
19752 b"avg",
19753 b"2000",
19754 ],
19755 &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
19756 &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
19757 &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
19758 &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
19759 &[b"TS.READ", b"ts:a", b"0"],
19760 &[b"TS.READ", b"ts:a", b"+"],
19761 &[b"TS.QUERYINDEX", b"sensor=a"],
19763 &[b"TS.QUERYINDEX", b"room=1"],
19764 &[b"TS.QUERYINDEX", b"room=9"],
19765 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
19766 &[
19767 b"TS.QUERYLABELS",
19768 b"VALUES",
19769 b"sensor",
19770 b"FILTER",
19771 b"room=1",
19772 ],
19773 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
19774 &[
19775 b"TS.MGET",
19776 b"SELECTED_LABELS",
19777 b"sensor",
19778 b"FILTER",
19779 b"sensor=a",
19780 ],
19781 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
19782 &[
19783 b"TS.MREVRANGE",
19784 b"-",
19785 b"+",
19786 b"WITHLABELS",
19787 b"FILTER",
19788 b"sensor=a",
19789 ],
19790 &[
19791 b"TS.MRANGE",
19792 b"-",
19793 b"+",
19794 b"FILTER",
19795 b"room=1",
19796 b"GROUPBY",
19797 b"room",
19798 b"REDUCE",
19799 b"max",
19800 ],
19801 &[b"TS.INFO", b"ts:a"],
19802 &[
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.CREATE", b"ts:down"],
19812 &[
19813 b"TS.CREATERULE",
19814 b"ts:a",
19815 b"ts:down",
19816 b"AGGREGATION",
19817 b"avg",
19818 b"1000",
19819 ],
19820 &[b"TS.ADD", b"ts:a", b"5000", b"4"],
19821 &[b"TS.ADD", b"ts:a", b"6000", b"5"],
19822 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
19823 &[b"TS.GET", b"ts:down", b"LATEST"],
19824 &[b"TS.INFO", b"ts:down"],
19825 &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
19826 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
19827 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
19828 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
19829 &[b"TS.DEL", b"ts:a", b"0", b"1000"],
19830 &[b"SET", b"plain", b"v"],
19832 &[b"TS.ADD", b"plain", b"1", b"1"],
19833 &[b"TS.GET", b"plain"],
19834 &[b"TS.READ", b"plain", b"0"],
19835 &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
19836 &[b"TS.RANGE", b"gone", b"-", b"+"],
19837 &[b"TS.INFO", b"gone"],
19838 ];
19839
19840 let mut one = Fixture::new();
19841 let mut many = Fixture::striped(8);
19842 for parts in script {
19843 let a = one.run(parts);
19844 let b = many.run(parts);
19845 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19846 }
19847 }
19848
19849 #[test]
19859 fn a_compaction_rule_across_stripes_reaches_both_ends() {
19860 let mut many = Fixture::striped(8);
19861 let other = apart(&mut many, "src");
19862 let (src, dst) = (b"src".as_slice(), other.as_bytes());
19863 let mut one = Fixture::new();
19864 let mut both = |parts: &[&[u8]]| {
19865 let a = one.run(parts);
19866 let b = many.run(parts);
19867 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19868 a
19869 };
19870
19871 both(&[b"TS.CREATE", src]);
19872 both(&[b"TS.CREATE", dst]);
19873 assert_eq!(
19874 both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
19875 "+OK\r\n"
19876 );
19877 both(&[b"TS.ADD", src, b"1000", b"1"]);
19878 both(&[b"TS.ADD", src, b"1500", b"3"]);
19879 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
19882 let open = both(&[b"TS.GET", dst, b"LATEST"]);
19883 assert!(open.contains(":1000"), "the open bucket is folded: {open}");
19884
19885 both(&[b"TS.ADD", src, b"2000", b"5"]);
19888 let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
19889 assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
19890 assert!(got.contains(":1000"), "{got}");
19891
19892 both(&[b"TS.DEL", src, b"1000", b"1999"]);
19894 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
19895
19896 assert!(
19899 both(&[b"TS.INFO", dst]).contains("src"),
19900 "the source is named"
19901 );
19902 assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
19903 assert_eq!(
19904 both(&[b"TS.DELETERULE", src, dst]),
19905 "-ERR TSDB: compaction rule does not exist\r\n"
19906 );
19907 }
19908
19909 #[test]
19911 fn a_label_query_across_stripes_finds_every_series() {
19912 let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
19913 let mut many = Fixture::striped(8);
19914 let mut homes: Vec<usize> = names
19915 .iter()
19916 .map(|name| many.server.striped(0).stripe_of(name))
19917 .collect();
19918 homes.sort_unstable();
19919 homes.dedup();
19920 assert!(homes.len() > 1, "the six keys are not all on one stripe");
19921
19922 let mut one = Fixture::new();
19923 let mut both = |parts: &[&[u8]]| {
19924 let a = one.run(parts);
19925 let b = many.run(parts);
19926 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19927 a
19928 };
19929 for name in &names {
19930 both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
19931 both(&[b"TS.ADD", name, b"1000", b"1"]);
19932 }
19933
19934 let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
19935 assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
19936 assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
19937 assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
19938 assert_eq!(
19939 both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
19940 "*1\r\n$4\r\nroom\r\n"
19941 );
19942 }
19943
19944 #[test]
19951 fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
19952 let script: &[&[&[u8]]] = &[
19953 &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
19954 &[b"HMSET", b"h", b"c", b"3"],
19955 &[b"HSETNX", b"h", b"a", b"9"],
19956 &[b"HSETNX", b"h", b"d", b"4"],
19957 &[b"HGET", b"h", b"a"],
19958 &[b"HGET", b"h", b"nope"],
19959 &[b"HMGET", b"h", b"a", b"nope"],
19960 &[b"HLEN", b"h"],
19961 &[b"HEXISTS", b"h", b"a"],
19962 &[b"HSTRLEN", b"h", b"a"],
19963 &[b"HGETALL", b"h"],
19964 &[b"HKEYS", b"h"],
19965 &[b"HVALS", b"h"],
19966 &[b"HINCRBY", b"h", b"a", b"5"],
19967 &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
19968 &[b"HSCAN", b"h", b"0"],
19969 &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
19970 &[b"HSCAN", b"h", b"0", b"NOVALUES"],
19971 &[b"HDEL", b"h", b"d"],
19972 &[b"HSET", b"one", b"f", b"v"],
19973 &[b"HRANDFIELD", b"one"],
19974 &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
19975 &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
19977 &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
19978 &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
19979 &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
19980 &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
19981 &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
19982 &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
19983 &[b"HGET", b"h", b"b"],
19984 &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
19986 &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
19987 &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
19988 &[b"HGET", b"h", b"e"],
19989 &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
19991 &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
19992 &[b"HGETALL", b"imp"],
19993 &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
19994 &[b"HIMPORT", b"DISCARD", b"fs"],
19995 &[b"SET", b"plain", b"v"],
19997 &[b"HSET", b"plain", b"a", b"1"],
19998 &[b"HGETALL", b"plain"],
19999 &[b"HGET", b"gone", b"a"],
20000 &[b"HINCRBY", b"h", b"a", b"nan"],
20001 ];
20002
20003 let mut one = Fixture::new();
20004 let mut many = Fixture::striped(8);
20005 one.server.set_clock_ms(1_700_000_000_000);
20009 many.server.set_clock_ms(1_700_000_000_000);
20010 for parts in script {
20011 let a = one.run(parts);
20012 let b = many.run(parts);
20013 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20014 }
20015 }
20016
20017 #[test]
20019 fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20020 let script: &[&[&[u8]]] = &[
20021 &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20022 &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20023 &[b"ARGET", b"a", b"1"],
20024 &[b"ARGET", b"a", b"99"],
20025 &[b"ARMGET", b"a", b"0", b"5", b"99"],
20026 &[b"ARGETRANGE", b"a", b"0", b"7"],
20027 &[b"ARLEN", b"a"],
20028 &[b"ARCOUNT", b"a"],
20029 &[b"ARINSERT", b"a", b"m", b"n"],
20030 &[b"ARSCAN", b"a", b"0", b"20"],
20031 &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20032 &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20033 &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20034 &[b"ARLASTITEMS", b"a", b"2"],
20035 &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20036 &[b"ARNEXT", b"a"],
20037 &[b"ARSEEK", b"a", b"3"],
20038 &[b"AROP", b"a", b"0", b"20", b"USED"],
20039 &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20040 &[b"ARINFO", b"a"],
20041 &[b"ARINFO", b"a", b"FULL"],
20042 &[b"ARDEL", b"a", b"0"],
20043 &[b"ARDELRANGE", b"a", b"1", b"2"],
20044 &[b"ARCOUNT", b"a"],
20045 &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20046 &[b"ARGETRANGE", b"r", b"0", b"9"],
20047 &[b"SET", b"plain", b"v"],
20049 &[b"ARGET", b"plain", b"0"],
20050 &[b"ARSET", b"plain", b"0", b"v"],
20051 &[b"ARGET", b"gone", b"0"],
20052 &[b"ARSET", b"a", b"bad", b"v"],
20053 ];
20054
20055 let mut one = Fixture::new();
20056 let mut many = Fixture::striped(8);
20057 for parts in script {
20058 let a = one.run(parts);
20059 let b = many.run(parts);
20060 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20061 }
20062 }
20063
20064 #[test]
20070 fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20071 let script: &[&[&[u8]]] = &[
20072 &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20073 &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20074 &[b"G.NADD", b"g", b"n3"],
20075 &[b"G.NGET", b"g", b"n1"],
20076 &[b"G.NGET", b"g", b"gone"],
20077 &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20078 &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20079 &[b"G.OUT", b"g", b"n1", b"knows"],
20080 &[b"G.IN", b"g", b"n2", b"knows"],
20081 &[b"G.DEG", b"g", b"n1", b"knows"],
20082 &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20083 &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20084 &[b"G.PATH", b"g", b"n1", b"n3"],
20085 &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20086 &[b"G.NDEL", b"g", b"n3"],
20087 &[b"G.NGET", b"g", b"n3"],
20088 &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20090 &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20091 &[b"VCARD", b"v"],
20092 &[b"VDIM", b"v"],
20093 &[b"VEMB", b"v", b"e1"],
20094 &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20095 &[b"VSIM", b"v", b"ELE", b"e1"],
20096 &[b"VISMEMBER", b"v", b"e1"],
20097 &[b"VISMEMBER", b"v", b"gone"],
20098 &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20099 &[b"VGETATTR", b"v", b"e1"],
20100 &[b"VRANGE", b"v", b"-", b"+"],
20101 &[b"VLINKS", b"v", b"e1"],
20102 &[b"VINFO", b"v"],
20103 &[b"VREM", b"v", b"e2"],
20104 &[b"VCARD", b"v"],
20105 &[b"SET", b"plain", b"v"],
20107 &[b"G.NGET", b"plain", b"n1"],
20108 &[b"VCARD", b"plain"],
20109 &[b"G.NADD", b"gone2", b"n"],
20110 &[b"VEMB", b"gone3", b"e"],
20111 ];
20112
20113 let mut one = Fixture::new();
20114 let mut many = Fixture::striped(8);
20115 for parts in script {
20116 let a = one.run(parts);
20117 let b = many.run(parts);
20118 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20119 }
20120 }
20121
20122 #[test]
20125 fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20126 let script: &[&[&[u8]]] = &[
20127 &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20129 &[b"BF.ADD", b"bf", b"a"],
20130 &[b"BF.ADD", b"bf", b"a"],
20131 &[b"BF.MADD", b"bf", b"b", b"c"],
20132 &[b"BF.EXISTS", b"bf", b"a"],
20133 &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20134 &[b"BF.CARD", b"bf"],
20135 &[b"BF.INFO", b"bf"],
20136 &[b"BF.INFO", b"bf", b"CAPACITY"],
20137 &[b"BF.DEBUG", b"bf"],
20138 &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20139 &[b"BF.EXISTS", b"made", b"x"],
20140 &[b"BF.SCANDUMP", b"bf", b"0"],
20141 &[b"CF.RESERVE", b"cf", b"100"],
20143 &[b"CF.ADD", b"cf", b"a"],
20144 &[b"CF.ADDNX", b"cf", b"a"],
20145 &[b"CF.COUNT", b"cf", b"a"],
20146 &[b"CF.EXISTS", b"cf", b"a"],
20147 &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20148 &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20149 &[b"CF.DEL", b"cf", b"a"],
20150 &[b"CF.COMPACT", b"cf"],
20151 &[b"CF.INFO", b"cf"],
20152 &[b"CF.DEBUG", b"cf"],
20153 &[b"CF.SCANDUMP", b"cf", b"0"],
20154 &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20156 &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20157 &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20158 &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20159 &[b"CMS.INFO", b"cms"],
20160 &[b"TOPK.RESERVE", b"tk", b"3"],
20162 &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20163 &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20164 &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20165 &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20166 &[b"TOPK.LIST", b"tk"],
20167 &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20168 &[b"TOPK.INFO", b"tk"],
20169 &[b"TDIGEST.CREATE", b"td"],
20171 &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20172 &[b"TDIGEST.MIN", b"td"],
20173 &[b"TDIGEST.MAX", b"td"],
20174 &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20175 &[b"TDIGEST.CDF", b"td", b"3"],
20176 &[b"TDIGEST.RANK", b"td", b"3"],
20177 &[b"TDIGEST.REVRANK", b"td", b"3"],
20178 &[b"TDIGEST.BYRANK", b"td", b"0"],
20179 &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20180 &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20181 &[b"TDIGEST.INFO", b"td"],
20182 &[b"TDIGEST.RESET", b"td"],
20183 &[b"TDIGEST.MIN", b"td"],
20184 &[b"SET", b"plain", b"v"],
20186 &[b"BF.ADD", b"plain", b"a"],
20187 &[b"CF.ADD", b"plain", b"a"],
20188 &[b"CMS.QUERY", b"plain", b"a"],
20189 &[b"TOPK.ADD", b"plain", b"a"],
20190 &[b"TDIGEST.ADD", b"plain", b"1"],
20191 &[b"CMS.INFO", b"gone"],
20192 &[b"TOPK.INFO", b"gone"],
20193 &[b"TDIGEST.INFO", b"gone"],
20194 ];
20195
20196 let mut one = Fixture::new();
20197 let mut many = Fixture::striped(8);
20198 for parts in script {
20199 let a = one.run(parts);
20200 let b = many.run(parts);
20201 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20202 }
20203 }
20204
20205 #[test]
20212 fn a_sketch_merge_across_stripes_reads_every_source() {
20213 let mut many = Fixture::striped(8);
20214 let other = apart(&mut many, "s1");
20215 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20216 let mut one = Fixture::new();
20217 let mut both = |parts: &[&[u8]]| {
20218 let a = one.run(parts);
20219 let b = many.run(parts);
20220 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20221 a
20222 };
20223
20224 for key in [b"cd".as_slice(), s1, s2] {
20228 both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20229 }
20230 both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20231 both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20232 assert_eq!(
20233 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20234 "+OK\r\n",
20235 "the merge took both sources"
20236 );
20237 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20238 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20240 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20241 both(&[b"SET", b"plain", b"v"]);
20243 assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20244 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20245
20246 both(&[b"FLUSHALL"]);
20249 both(&[b"TDIGEST.CREATE", b"td"]);
20250 both(&[b"TDIGEST.CREATE", s1]);
20251 both(&[b"TDIGEST.CREATE", s2]);
20252 both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20253 both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20254 assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20255 assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20256 assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20257 }
20258
20259 #[test]
20266 fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20267 let script: &[&[&[u8]]] = &[
20268 &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20269 &[b"SORT", b"l"],
20270 &[b"SORT", b"l", b"DESC"],
20271 &[b"SORT", b"l", b"ALPHA"],
20272 &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20273 &[b"SORT_RO", b"l"],
20274 &[
20277 b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20278 ],
20279 &[b"SORT", b"l", b"BY", b"w_*"],
20280 &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20281 &[b"DEL", b"w_2"],
20282 &[b"SORT", b"l", b"BY", b"w_*"],
20283 &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20286 &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20287 &[b"HSET", b"h_1", b"f", b"9"],
20289 &[b"HSET", b"h_2", b"f", b"8"],
20290 &[b"HSET", b"h_3", b"f", b"7"],
20291 &[b"HSET", b"h_10", b"f", b"6"],
20292 &[b"SORT", b"l", b"BY", b"h_*->f"],
20293 &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20294 &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20296 &[b"LRANGE", b"out", b"0", b"-1"],
20297 &[b"SORT", b"l", b"STORE", b"l"],
20298 &[b"LRANGE", b"l", b"0", b"-1"],
20299 &[b"SORT", b"missing", b"STORE", b"out"],
20302 &[b"EXISTS", b"out"],
20303 &[b"SADD", b"s", b"c", b"a", b"b"],
20306 &[b"SORT", b"s", b"ALPHA"],
20307 &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20308 &[b"LRANGE", b"out", b"0", b"-1"],
20309 &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20310 &[b"SORT", b"z", b"BY", b"nosort"],
20311 &[b"SORT", b"z", b"ALPHA", b"DESC"],
20312 &[b"SET", b"str", b"v"],
20315 &[b"SORT", b"str"],
20316 &[b"RPUSH", b"words", b"one", b"two"],
20317 &[b"SORT", b"words"],
20318 &[b"SORT_RO", b"l", b"STORE", b"out"],
20319 ];
20320
20321 let mut one = Fixture::new();
20322 let mut many = Fixture::striped(8);
20323 for parts in script {
20324 let a = one.run(parts);
20325 let b = many.run(parts);
20326 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20327 }
20328 }
20329
20330 #[test]
20337 fn a_sort_across_stripes_reads_every_pattern_key() {
20338 let mut f = Fixture::striped(8);
20339 let out = apart(&mut f, "l");
20340 let (list, dest) = (b"l".as_slice(), out.as_bytes());
20341
20342 f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20343 f.run(&[
20344 b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20345 ]);
20346 f.run(&[
20347 b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20348 ]);
20349
20350 let db = f.server.striped(0);
20353 let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20354 .iter()
20355 .map(|k| db.stripe_of(k.as_slice()))
20356 .collect();
20357 assert!(
20358 weights.iter().any(|s| *s != weights[0]),
20359 "the four weight keys all landed on one stripe, so this proves nothing"
20360 );
20361
20362 assert_eq!(
20363 f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
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 order came off the weights and the answer off the data keys"
20366 );
20367 assert_eq!(
20368 f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20369 ":4\r\n"
20370 );
20371 assert_eq!(
20372 f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20373 "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20374 "the destination is on a stripe of its own and got the whole answer"
20375 );
20376 }
20377
20378 #[test]
20386 fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20387 let mut f = Fixture::striped(8);
20388 let other = apart(&mut f, "h");
20389 let (first, second) = (b"h".as_slice(), other.as_bytes());
20390
20391 assert_eq!(
20392 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
20393 "+OK\r\n"
20394 );
20395 assert_eq!(
20396 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
20397 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
20398 "the read comes off one stripe and has to answer for all of them"
20399 );
20400 for key in [first, second] {
20401 f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
20402 assert_eq!(
20403 f.run(&[b"OBJECT", b"ENCODING", key]),
20404 "$8\r\nlistpack\r\n",
20405 "two fields is still under the ladder"
20406 );
20407 f.run(&[b"HSET", key, b"c", b"3"]);
20408 assert_eq!(
20409 f.run(&[b"OBJECT", b"ENCODING", key]),
20410 "$9\r\nhashtable\r\n",
20411 "three fields is over it, on whichever stripe the key is on"
20412 );
20413 }
20414
20415 assert_eq!(
20418 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
20419 "+OK\r\n"
20420 );
20421 let db = f.server.striped(0);
20422 assert!(
20423 db.stripes_mut().all(|s| s.policy().name() == "allkeys-lru"),
20424 "a stripe kept the old policy"
20425 );
20426 }
20427}