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 indexing;
69mod json;
70mod keyspace;
71mod lists;
72mod migrate;
73mod scan;
74mod scripting;
75mod search;
76mod server;
77mod sets;
78mod streams;
79mod strings;
80pub mod table;
81mod tdigest;
82mod topk;
83mod ts;
84mod vectors;
85mod vfilter;
86mod zsets;
87
88pub use args::Args;
89pub use blocking::{Parked, Waiters};
90pub use server::parse_memory;
91pub use table::{COMMANDS, Spec, arity_ok, lookup};
92
93use crate::reply::Out;
94use std::cell::Cell;
95use std::path::{Path, PathBuf};
96use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
97use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize};
98use yo_common::lock::{Held, Lock};
99use yo_common::{Code, Error};
100use yo_kv::cold::Blocks;
101use yo_kv::{Clock, Db, Keyspace};
102use yo_search::Registry;
103
104pub const DATABASES: usize = 16;
111
112const ALL_DATABASES: u64 = if DATABASES == 64 {
119 u64::MAX
120} else {
121 (1u64 << DATABASES) - 1
122};
123const _: () = assert!(DATABASES <= 64);
124
125const EVICT_BUDGET: usize = 64;
137
138const NO_MAXSTORE: u64 = u64::MAX;
146
147const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
152
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum Flow {
156 Continue,
158 Close,
160 Block,
167}
168
169#[derive(Debug, Default)]
179pub struct Counter(AtomicU64);
180
181impl Counter {
182 fn bump(&self) {
184 self.0.store(self.get().wrapping_add(1), Relaxed);
185 }
186
187 fn drop_one(&self) {
194 self.0.store(self.get().saturating_sub(1), Relaxed);
195 }
196
197 fn get(&self) -> u64 {
199 self.0.load(Relaxed)
200 }
201
202 fn zero(&self) {
204 self.0.store(0, Relaxed);
205 }
206}
207
208#[derive(Debug, Default)]
214pub struct Stats {
215 clients: Counter,
217 connections: Counter,
219 commands: Counter,
221}
222
223impl Stats {
224 pub fn opened(&self) {
226 self.clients.bump();
227 self.connections.bump();
228 }
229
230 pub fn closed(&self) {
232 self.clients.drop_one();
233 }
234}
235
236#[derive(Debug, Clone, Copy, Default)]
238pub struct Totals {
239 pub clients: u64,
241 pub connections: u64,
243 pub commands: u64,
245}
246
247thread_local! {
248 static SLOT: Cell<usize> = const { Cell::new(usize::MAX) };
256}
257
258#[derive(Debug, Default)]
269#[repr(align(64))]
270struct Local {
271 stats: Stats,
273 cmdstats: CommandStats,
275 dirty: AtomicU64,
284}
285
286impl Local {
287 fn mark(&self, dbs: u64) {
289 self.dirty.store(self.dirty.load(Relaxed) | dbs, Relaxed);
290 }
291}
292
293fn one_thread() -> Box<[Local]> {
295 slots(1)
296}
297
298fn slots(threads: usize) -> Box<[Local]> {
300 (0..threads.max(1)).map(|_| Local::default()).collect()
301}
302
303fn working_dir() -> PathBuf {
310 std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
311}
312
313#[derive(Debug, Clone, Copy, Default)]
322pub struct CommandStat {
323 pub calls: u64,
325 pub rejected: u64,
328 pub failed: u64,
330}
331
332impl CommandStat {
333 const fn seen(&self) -> bool {
339 self.calls != 0 || self.rejected != 0 || self.failed != 0
340 }
341}
342
343#[derive(Debug, Default)]
348struct Row {
349 calls: Counter,
351 rejected: Counter,
353 failed: Counter,
355}
356
357#[derive(Debug)]
364struct CommandStats(Box<[Row]>);
365
366impl Default for CommandStats {
367 fn default() -> CommandStats {
368 CommandStats((0..table::count()).map(|_| Row::default()).collect())
369 }
370}
371
372impl CommandStats {
373 fn at(&self, spec: &'static Spec) -> &Row {
375 &self.0[table::index_of(spec)]
376 }
377}
378
379pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
385
386pub struct Server {
393 dbs: Vec<Db>,
394 width: usize,
400 clock: Clock,
401 started_ms: u64,
402 next_db: usize,
405 dirty: u64,
420 conn_bytes: usize,
422 maxmemory: AtomicU64,
430 store: Option<Box<StoreSource>>,
441 maxstore: AtomicU64,
463 used: usize,
475 evict_db: usize,
481 expire_db: usize,
488 expire_ms: u64,
491 waiters: Lock<Waiters>,
499 parked: AtomicUsize,
512 peers: Lock<migrate::Peers>,
524 locals: Box<[Local]>,
530 claimed: AtomicUsize,
532 dir: PathBuf,
540 backup: Lock<backup::State>,
550 sealed: AtomicBool,
560 search: Lock<Registry>,
575 stopping: AtomicBool,
584}
585
586impl Server {
587 #[must_use]
589 pub fn new() -> Server {
590 let clock = Clock::system();
591 Server {
592 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
593 width: 1,
594 clock,
595 started_ms: clock.now_ms(),
596 next_db: 0,
597 dirty: ALL_DATABASES,
598 conn_bytes: 0,
599 maxmemory: AtomicU64::new(0),
600 store: None,
601 maxstore: AtomicU64::new(NO_MAXSTORE),
602 used: 0,
603 evict_db: 0,
604 expire_db: 0,
605 expire_ms: 0,
606 waiters: Lock::default(),
607 parked: AtomicUsize::new(0),
608 peers: Lock::default(),
609 locals: one_thread(),
610 claimed: AtomicUsize::new(0),
611 dir: working_dir(),
612 backup: Lock::default(),
613 sealed: AtomicBool::new(false),
614 search: Lock::new(Registry::new()),
615 stopping: AtomicBool::new(false),
616 }
617 }
618
619 #[must_use]
630 pub fn with_width(width: usize) -> Server {
631 let clock = Clock::system();
632 let mut server = Server::new();
633 server.dbs = (0..DATABASES)
634 .map(|_| Db::with_clock(clock, width))
635 .collect();
636 server.width = server.dbs[0].width();
637 server
638 }
639
640 #[must_use]
642 pub fn with_clock(clock: Clock) -> Server {
643 Server {
644 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
645 width: 1,
646 clock,
647 started_ms: clock.now_ms(),
648 next_db: 0,
649 dirty: ALL_DATABASES,
650 conn_bytes: 0,
651 maxmemory: AtomicU64::new(0),
652 store: None,
653 maxstore: AtomicU64::new(NO_MAXSTORE),
654 used: 0,
655 evict_db: 0,
656 expire_db: 0,
657 expire_ms: 0,
658 waiters: Lock::default(),
659 parked: AtomicUsize::new(0),
660 peers: Lock::default(),
661 locals: one_thread(),
662 claimed: AtomicUsize::new(0),
663 dir: working_dir(),
664 backup: Lock::default(),
665 sealed: AtomicBool::new(false),
666 search: Lock::new(Registry::new()),
667 stopping: AtomicBool::new(false),
668 }
669 }
670
671 pub fn striped(&self, i: usize) -> &Db {
695 self.mine().mark(1u64 << i);
696 &self.dbs[i]
697 }
698
699 fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
706 self.dbs
707 .iter()
708 .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
709 }
710
711 fn keyspaces_mut(&mut self) -> impl Iterator<Item = &mut Keyspace> {
713 self.dbs.iter_mut().flat_map(Db::stripes_mut)
714 }
715
716 const fn slots(&self) -> usize {
722 DATABASES * self.width
723 }
724
725 const fn slot_db(&self, i: usize) -> usize {
727 i / self.width
728 }
729
730 fn slot_mut(&mut self, i: usize) -> &mut Keyspace {
732 let (db, stripe) = (i / self.width, i % self.width);
733 self.dbs[db].stripe_mut(stripe)
734 }
735
736 fn slot(&self, i: usize) -> Held<'_, Keyspace> {
738 let (db, stripe) = (i / self.width, i % self.width);
739 self.dbs[db].hold_stripe(stripe)
740 }
741
742 #[must_use]
744 pub fn dir(&self) -> &Path {
745 &self.dir
746 }
747
748 pub fn set_dir(&mut self, dir: PathBuf) {
755 self.dir = dir;
756 }
757
758 pub fn backup_expire(&self) {
764 backup::expire(self);
765 }
766
767 pub fn stop(&self) {
773 self.stopping.store(true, Release);
774 }
775
776 #[must_use]
782 pub fn stopping(&self) -> bool {
783 self.stopping.load(Acquire)
784 }
785
786 #[must_use]
793 pub fn striped_ref(&self, i: usize) -> &Db {
794 &self.dbs[i]
795 }
796
797 fn settings(&self) -> Held<'_, Keyspace> {
805 self.dbs[0].hold_stripe(0)
806 }
807
808 pub fn refresh_clock(&mut self) {
814 self.clock.refresh();
815 let now = self.clock.now_ms();
816 for db in &mut self.dbs {
817 db.set_clock_ms(now);
818 }
819 }
820
821 pub fn advance_clock_ms(&mut self, ms: u64) {
827 let now = self.clock.now_ms() + ms;
828 self.set_clock_ms(now);
829 }
830
831 pub fn set_clock_ms(&mut self, ms: u64) {
839 self.clock.set(ms);
840 for db in &mut self.dbs {
841 db.set_clock_ms(ms);
842 }
843 }
844
845 #[must_use]
847 pub fn uptime_secs(&self) -> u64 {
848 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
849 }
850
851 #[must_use]
859 pub fn memory_bytes(&self) -> usize {
860 self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes
861 }
862
863 #[must_use]
869 pub fn dataset_bytes(&self) -> usize {
870 self.keyspaces()
871 .map(|db| db.map().arena().live_bytes() as usize)
872 .sum()
873 }
874
875 #[must_use]
877 pub fn arena_bytes(&self) -> usize {
878 self.keyspaces()
879 .map(|db| db.map().arena().reserved_bytes() as usize)
880 .sum()
881 }
882
883 #[must_use]
885 pub fn index_bytes(&self) -> usize {
886 self.keyspaces()
887 .map(|db| db.map().index().memory_bytes())
888 .sum()
889 }
890
891 #[must_use]
898 pub fn compaction(&self) -> yo_kv::Compaction {
899 self.keyspaces().map(|db| db.map().compaction()).fold(
900 yo_kv::Compaction::default(),
901 |a, b| yo_kv::Compaction {
902 walked: a.walked + b.walked,
903 moved: a.moved + b.moved,
904 bytes: a.bytes + b.bytes,
905 },
906 )
907 }
908
909 #[must_use]
911 pub fn segment_count(&self) -> usize {
912 self.keyspaces()
913 .map(|db| db.map().arena().resident_segments())
914 .sum()
915 }
916
917 #[must_use]
919 pub const fn conn_bytes(&self) -> usize {
920 self.conn_bytes
921 }
922
923 pub fn note_conn_bytes(&mut self, delta: isize) {
931 self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
932 }
933
934 #[must_use]
936 pub fn expired_keys(&self) -> u64 {
937 self.keyspaces().map(|db| db.expired_keys()).sum()
938 }
939
940 #[must_use]
942 pub fn evicted_keys(&self) -> u64 {
943 self.keyspaces().map(|db| db.evicted_keys()).sum()
944 }
945
946 pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
952 (0..table::count())
953 .map(|at| (table::name_at(at), self.command_stat(at)))
954 .filter(|(_, row)| row.seen())
955 }
956
957 fn command_stat(&self, at: usize) -> CommandStat {
959 let mut sum = CommandStat::default();
960 for thread in &self.locals {
961 let row = &thread.cmdstats.0[at];
962 sum.calls += row.calls.get();
963 sum.rejected += row.rejected.get();
964 sum.failed += row.failed.get();
965 }
966 sum
967 }
968
969 pub fn counted(&self) -> &Stats {
978 &self.mine().stats
979 }
980
981 fn mine(&self) -> &Local {
983 let mut slot = SLOT.get();
984 if slot == usize::MAX {
985 slot = self.claimed.fetch_add(1, Relaxed);
986 SLOT.set(slot);
987 }
988 &self.locals[slot % self.locals.len()]
989 }
990
991 #[must_use]
993 pub fn totals(&self) -> Totals {
994 let mut sum = Totals::default();
995 for thread in &self.locals {
996 sum.clients += thread.stats.clients.get();
997 sum.connections += thread.stats.connections.get();
998 sum.commands += thread.stats.commands.get();
999 }
1000 sum
1001 }
1002
1003 pub fn reset_stats(&self) {
1010 for thread in &self.locals {
1011 thread.stats.connections.zero();
1012 thread.stats.commands.zero();
1013 }
1014 }
1015
1016 pub fn set_threads(&mut self, threads: usize) {
1023 self.locals = slots(threads);
1024 self.claimed = AtomicUsize::new(0);
1025 }
1026
1027 #[must_use]
1029 pub fn maxmemory(&self) -> u64 {
1030 self.maxmemory.load(Relaxed)
1031 }
1032
1033 pub fn set_maxmemory(&mut self, bytes: u64) {
1047 self.maxmemory.store(bytes, Relaxed);
1048 for db in &mut self.dbs {
1049 db.track_memory(bytes != 0);
1050 }
1051 self.used = self.settled_memory();
1052 }
1053
1054 pub fn set_store_source(
1064 &mut self,
1065 source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
1066 ) {
1067 self.store = Some(Box::new(source));
1068 }
1069
1070 #[must_use]
1072 pub const fn has_store_source(&self) -> bool {
1073 self.store.is_some()
1074 }
1075
1076 fn attach_store(&mut self, at: usize) {
1083 if self.slot(at).store_bytes().is_some() {
1084 return;
1085 }
1086 let Some(source) = self.store.as_mut() else {
1087 return;
1088 };
1089 if let Some(blocks) = source(at) {
1090 self.slot_mut(at).attach(blocks);
1091 }
1092 }
1093
1094 #[must_use]
1096 pub fn maxstore(&self) -> Option<u64> {
1097 match self.maxstore.load(Relaxed) {
1098 NO_MAXSTORE => None,
1099 bytes => Some(bytes),
1100 }
1101 }
1102
1103 pub fn set_maxstore(&self, bytes: Option<u64>) {
1109 self.maxstore.store(bytes.unwrap_or(NO_MAXSTORE), Relaxed);
1110 }
1111
1112 #[must_use]
1118 pub fn store_bytes(&self) -> u64 {
1119 self.keyspaces().filter_map(|db| db.store_bytes()).sum()
1120 }
1121
1122 #[must_use]
1137 pub fn cold_stats(&self) -> yo_kv::tier::Stats {
1138 let mut total = yo_kv::tier::Stats::default();
1139 for db in self.keyspaces() {
1140 let Some(tier) = db.tier() else { continue };
1141 let s = tier.stats();
1142 total.demoted += s.demoted;
1143 total.promoted += s.promoted;
1144 total.faults += s.faults;
1145 total.served += s.served;
1146 total.bytes_out += s.bytes_out;
1147 total.bytes_in += s.bytes_in;
1148 }
1149 total
1150 }
1151
1152 #[must_use]
1159 pub fn regime(&self) -> &'static str {
1160 if (0..self.slots()).any(|at| self.migrates(at)) {
1161 "migrate"
1162 } else {
1163 "evict"
1164 }
1165 }
1166
1167 fn migrates(&self, at: usize) -> bool {
1179 let cap = self.maxstore();
1180 if cap == Some(0) {
1181 return false;
1182 }
1183 let bytes = self.slot(at).store_bytes();
1187 match bytes {
1188 Some(held) => cap.is_none_or(|cap| held < cap),
1189 None => self.store.is_some(),
1193 }
1194 }
1195
1196 pub fn refresh_memory(&mut self) {
1201 if self.maxmemory() != 0 {
1202 self.used = self.settled_memory();
1203 }
1204 }
1205
1206 fn settled_memory(&mut self) -> usize {
1213 self.keyspaces_mut()
1214 .map(Keyspace::settled_memory_bytes)
1215 .sum::<usize>()
1216 + self.conn_bytes
1217 }
1218
1219 pub fn make_room(&mut self) -> bool {
1253 let limit = self.maxmemory();
1254 if limit == 0 || self.used as u64 <= limit {
1255 return true;
1256 }
1257 self.used = self.settled_memory();
1262 let mut budget = EVICT_BUDGET;
1263 while self.used as u64 > limit {
1264 let over = self.used - limit as usize;
1265 if !self.relieve_step(over) {
1266 return false;
1267 }
1268 self.compact_hard_step();
1269 self.used = self.settled_memory();
1270 budget -= 1;
1271 if budget == 0 {
1272 break;
1273 }
1274 }
1275 true
1276 }
1277
1278 fn relieve_step(&mut self, over: usize) -> bool {
1295 for turn in 0..self.slots() {
1296 let i = (self.evict_db + turn) % self.slots();
1297 let used = !self.slot(i).is_empty();
1300 let gave = if used && self.migrates(i) {
1301 self.attach_store(i);
1302 self.slot_mut(i)
1307 .relieve(over)
1308 .is_ok_and(yo_kv::tier::Relief::made_room)
1309 } else {
1310 self.slot_mut(i).evict_one()
1311 };
1312 if gave {
1313 self.evict_db = (i + 1) % self.slots();
1314 self.dirty |= 1u64 << self.slot_db(i);
1315 return true;
1316 }
1317 }
1318 false
1319 }
1320
1321 pub fn expire_slice(&mut self, budget: usize) -> usize {
1336 let now = self.clock.now_ms();
1337 if now == self.expire_ms {
1338 return 0;
1339 }
1340 self.expire_ms = now;
1341 self.expire_step(budget)
1342 }
1343
1344 pub fn expire_step(&mut self, budget: usize) -> usize {
1360 let mut spent = 0;
1361 for turn in 0..self.slots() {
1362 if spent >= budget {
1363 break;
1364 }
1365 let i = (self.expire_db + turn) % self.slots();
1366 let c = self.slot_mut(i).expire_cycle(budget - spent);
1367 spent += c.examined;
1368 if c.expired > 0 {
1369 self.expire_db = (i + 1) % self.slots();
1370 self.dirty |= 1u64 << self.slot_db(i);
1371 }
1372 }
1373 spent
1374 }
1375
1376 fn compact_hard_step(&mut self) -> Option<usize> {
1382 for turn in 0..self.slots() {
1383 let i = (self.next_db + turn) % self.slots();
1384 if let Some(moved) = self.slot_mut(i).compact_hard() {
1385 self.next_db = (i + 1) % self.slots();
1386 return Some(moved);
1387 }
1388 }
1389 None
1390 }
1391
1392 fn collect_marks(&mut self) {
1401 let mut marked = 0;
1402 for thread in &self.locals {
1403 marked |= thread.dirty.swap(0, Relaxed);
1404 }
1405 self.dirty |= marked;
1406 }
1407
1408 pub fn compact_step(&mut self) -> Option<usize> {
1421 self.collect_marks();
1422 for turn in 0..self.slots() {
1423 let i = (self.next_db + turn) % self.slots();
1424 let at = self.slot_db(i);
1428 if self.dirty & (1 << at) == 0 {
1429 continue;
1430 }
1431 if let Some(moved) = self.slot_mut(i).compact_step() {
1432 self.next_db = (i + 1) % self.slots();
1433 return Some(moved);
1434 }
1435 if i % self.width == self.width - 1 {
1439 self.dirty &= !(1u64 << at);
1440 }
1441 }
1442 None
1443 }
1444}
1445
1446impl Default for Server {
1447 fn default() -> Server {
1448 Server::new()
1449 }
1450}
1451
1452pub struct Session {
1454 db: usize,
1455 id: u64,
1456 name: Vec<u8>,
1457 sets: himport::Fieldsets,
1463}
1464
1465impl Session {
1466 #[must_use]
1468 pub fn new(id: u64) -> Session {
1469 Session {
1470 db: 0,
1471 id,
1472 name: Vec::new(),
1473 sets: himport::Fieldsets::default(),
1474 }
1475 }
1476
1477 #[must_use]
1479 pub const fn id(&self) -> u64 {
1480 self.id
1481 }
1482
1483 #[must_use]
1485 pub const fn db(&self) -> usize {
1486 self.db
1487 }
1488
1489 #[must_use]
1491 pub fn name(&self) -> &[u8] {
1492 &self.name
1493 }
1494
1495 pub fn reset(&mut self) {
1500 self.db = 0;
1501 self.name.clear();
1502 self.sets.clear();
1506 }
1507
1508 fn set_name(&mut self, name: &[u8]) {
1510 yo_alloc::allow(|| {
1511 self.name.clear();
1512 self.name.extend_from_slice(name);
1513 });
1514 }
1515}
1516
1517pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1522 if args.is_empty() {
1525 return Flow::Continue;
1526 }
1527 resolved(server, session, lookup(args.name()), args, out)
1528}
1529
1530pub fn resolved(
1542 server: &mut Server,
1543 session: &mut Session,
1544 spec: Option<&'static Spec>,
1545 args: Args<'_>,
1546 out: &mut Out,
1547) -> Flow {
1548 if args.is_empty() {
1549 return Flow::Continue;
1550 }
1551 server.mine().stats.commands.bump();
1552
1553 let Some(spec) = spec else {
1554 write_error(out, &args::unknown_command(args));
1555 return Flow::Continue;
1556 };
1557 if !arity_ok(spec, args.len()) {
1558 server.mine().cmdstats.at(spec).rejected.bump();
1559 write_error(out, &args::wrong_arity(spec.name));
1560 return Flow::Continue;
1561 }
1562
1563 if server.maxmemory() != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1573 server.mine().cmdstats.at(spec).rejected.bump();
1574 out.error_line(b"OOM ", OOM);
1575 return Flow::Continue;
1576 }
1577
1578 server.mine().mark(match spec.group {
1585 "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1586 | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1587 1u64 << session.db
1588 }
1589 _ => ALL_DATABASES,
1590 });
1591
1592 let mark = out.len();
1593 let done = if spec.flags.contains(&"blocking") {
1600 blocking::execute(server, session, spec, args, out)
1601 } else {
1602 match spec.group {
1603 "string" => {
1604 let db = session.db;
1605 strings::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1606 }
1607 "bitmap" => {
1611 let db = session.db;
1612 bits::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1613 }
1614 "hyperloglog" => {
1617 let db = session.db;
1618 hll::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1619 }
1620 "set" => {
1621 let db = session.db;
1622 sets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1623 }
1624 "hash" if spec.name == "himport" => {
1629 let db = session.db;
1630 himport::execute(&server.dbs[db], &mut session.sets, args, out)
1631 .map(|()| Flow::Continue)
1632 }
1633 "hash" => {
1638 let db = session.db;
1639 let changed = hashes::execute(&server.dbs[db], spec, args, out);
1640 changed.map(|changed| {
1641 indexing::changed(server, db, args.get(1), changed);
1642 Flow::Continue
1643 })
1644 }
1645 "list" => {
1646 let db = session.db;
1647 lists::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1648 }
1649 "zset" => {
1650 let db = session.db;
1651 zsets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1652 }
1653 "geo" => {
1657 let db = session.db;
1658 geo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1659 }
1660 "array" => {
1661 let db = session.db;
1662 arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1663 }
1664 "graph" => {
1665 let db = session.db;
1666 graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1667 }
1668 "json" => {
1672 let db = session.db;
1673 json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1674 }
1675 "vector" => {
1676 let db = session.db;
1677 vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1678 }
1679 "bloom" => {
1680 let db = session.db;
1681 bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1682 }
1683 "cuckoo" => {
1684 let db = session.db;
1685 cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1686 }
1687 "cms" => {
1688 let db = session.db;
1689 cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1690 }
1691 "topk" => {
1692 let db = session.db;
1693 topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1694 }
1695 "tdigest" => {
1696 let db = session.db;
1697 tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1698 }
1699 "ts" => {
1700 let db = session.db;
1701 ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1702 }
1703 "stream" => {
1708 let db = session.db;
1709 let now = server.now_ms();
1710 streams::execute(&server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1711 }
1712 "keyspace" if spec.name == "migrate" => {
1716 migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1717 }
1718 "keyspace" => {
1721 keyspace::execute(&server.dbs, session.db, spec, args, out).map(|()| Flow::Continue)
1722 }
1723 "search" => {
1730 let db = session.db;
1731 let made = search::execute(&mut server.search.lock(), db, spec, args, out);
1732 made.map(|made| {
1733 if let Some(name) = made {
1734 indexing::scan(server, db, name);
1735 }
1736 Flow::Continue
1737 })
1738 }
1739 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1740 _ => server::execute(server, session, spec, args, out),
1741 }
1742 };
1743 let flow = match done {
1744 Ok(flow) => flow,
1745 Err(e) => {
1746 out.truncate(mark);
1747 write_error(out, &e);
1748 Flow::Continue
1749 }
1750 };
1751
1752 let row = server.mine().cmdstats.at(spec);
1763 row.calls.bump();
1764 if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1765 row.failed.bump();
1766 }
1767 flow
1768}
1769
1770fn write_error(out: &mut Out, e: &Error) {
1780 let prefix: &[u8] = match e.code() {
1781 Code::WrongType => b"WRONGTYPE ",
1782 Code::Corrupt => b"INVALIDOBJ ",
1786 _ => b"ERR ",
1787 };
1788 out.error_line(prefix, e.message().as_bytes());
1789}
1790
1791#[cfg(test)]
1792mod tests {
1793 use super::*;
1794 use crate::proto::{Limits, Proto};
1795 use crate::request::Argv;
1796
1797 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1802 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1803 for p in parts {
1804 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1805 wire.extend_from_slice(p);
1806 wire.extend_from_slice(b"\r\n");
1807 }
1808 wire
1809 }
1810
1811 struct Fixture {
1813 server: Server,
1814 session: Session,
1815 argv: Argv,
1816 out: Out,
1817 }
1818
1819 impl Fixture {
1820 fn new() -> Fixture {
1821 Fixture::on(Server::new())
1822 }
1823
1824 fn striped(width: usize) -> Fixture {
1826 Fixture::on(Server::with_width(width))
1827 }
1828
1829 fn on(server: Server) -> Fixture {
1830 Fixture {
1831 server,
1832 session: Session::new(7),
1833 argv: Argv::new(),
1834 out: Out::new(Proto::Resp2),
1835 }
1836 }
1837
1838 fn run(&mut self, parts: &[&[u8]]) -> String {
1840 self.flow(parts).1
1841 }
1842
1843 fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1849 let wire = encode(parts);
1850 self.argv.decode(&wire, &Limits::default()).unwrap();
1851 self.out.clear();
1852 execute(
1853 &mut self.server,
1854 &mut self.session,
1855 Args::new(&self.argv, &wire),
1856 &mut self.out,
1857 );
1858 self.out.as_slice().to_vec()
1859 }
1860
1861 fn advance(&mut self, ms: u64) {
1863 self.server.advance_clock_ms(ms);
1864 }
1865
1866 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1868 let wire = encode(parts);
1869 self.argv.decode(&wire, &Limits::default()).unwrap();
1870 self.out.clear();
1871 let flow = execute(
1872 &mut self.server,
1873 &mut self.session,
1874 Args::new(&self.argv, &wire),
1875 &mut self.out,
1876 );
1877 (
1878 flow,
1879 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1880 )
1881 }
1882 }
1883
1884 #[test]
1888 fn rewriting_the_same_keys_does_not_grow_the_server() {
1889 let mut f = Fixture::new();
1890 let val = vec![b'v'; 1024];
1891 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1892
1893 for k in &keys {
1894 f.run(&[b"SET", k, &val]);
1895 }
1896 f.server.compact_step();
1897 let after_first = f.server.memory_bytes();
1898
1899 for _ in 0..500 {
1904 for k in &keys {
1905 f.run(&[b"SET", k, &val]);
1906 }
1907 f.server.compact_step();
1908 }
1909
1910 assert!(
1911 f.server.memory_bytes() <= after_first * 2,
1912 "held {} after five hundred passes against {after_first} after one",
1913 f.server.memory_bytes()
1914 );
1915 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1916 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1917 }
1918
1919 #[test]
1933 fn a_database_nobody_started_on_is_still_collected() {
1934 let mut f = Fixture::new();
1935 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1936 let val = vec![b'v'; 1024];
1937 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1938
1939 for k in &keys {
1940 f.run(&[b"SET", k, &val]);
1941 }
1942 while f.server.compact_step().is_some() {}
1943 assert_eq!(
1944 f.server.dirty & (1 << 9),
1945 0,
1946 "database nine was drained and should not be asked again until it is written to"
1947 );
1948 let after_first = f.server.memory_bytes();
1949
1950 for _ in 0..500 {
1951 for k in &keys {
1952 f.run(&[b"SET", k, &val]);
1953 }
1954 f.server.compact_step();
1955 }
1956
1957 assert!(
1958 f.server.memory_bytes() <= after_first * 2,
1959 "held {} after five hundred passes against {after_first} after one",
1960 f.server.memory_bytes()
1961 );
1962 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1963 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1964 f.run(&[b"SELECT", b"0"]);
1966 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1967 }
1968
1969 #[test]
1970 fn a_command_goes_from_bytes_to_bytes() {
1971 let mut f = Fixture::new();
1972 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1973 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1974 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1975 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1976 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1978 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1979 }
1980
1981 #[test]
1982 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1983 let mut f = Fixture::new();
1984 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1985 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1988 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1989 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1990 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1992 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1993 }
1994
1995 #[test]
1996 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1997 let mut f = Fixture::new();
1998 f.run(&[b"SET", b"k", b"v"]);
1999 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
2002 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
2003 }
2004
2005 #[test]
2006 fn touch_counts_the_way_exists_counts() {
2007 let mut f = Fixture::new();
2008 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2009 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
2010 assert_eq!(
2011 f.run(&[b"TOUCH", b"a", b"a"]),
2012 ":2\r\n",
2013 "twice counts twice"
2014 );
2015 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
2016 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
2017 }
2018
2019 #[test]
2020 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
2021 let mut f = Fixture::new();
2022 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2023 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
2024
2025 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
2026 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2027 assert_eq!(
2028 f.run(&[b"TTL", b"b"]),
2029 ":100\r\n",
2030 "the source's and not b's"
2031 );
2032 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
2033 }
2034
2035 #[test]
2036 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
2037 let mut f = Fixture::new();
2038 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
2039 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
2042 }
2043
2044 #[test]
2045 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
2046 let mut f = Fixture::new();
2047 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
2048
2049 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
2050 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2051 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
2054 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
2055 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
2056 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
2057 }
2058
2059 #[test]
2060 fn renaming_a_set_does_not_touch_a_member() {
2061 let mut f = Fixture::new();
2062 for i in 0..300 {
2063 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
2064 }
2065 let before = f.server.memory_bytes();
2066
2067 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
2068 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
2069 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
2070 assert!(
2071 f.server.memory_bytes().abs_diff(before) < 256,
2072 "the members were copied: {} against {before}",
2073 f.server.memory_bytes()
2074 );
2075 }
2076
2077 #[test]
2078 fn a_copy_is_a_second_value_and_not_a_second_name() {
2079 let mut f = Fixture::new();
2080 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
2081
2082 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
2083 f.run(&[b"SADD", b"t", b"m3"]);
2084 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
2085 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
2086 }
2087
2088 #[test]
2098 fn every_type_can_be_copied() {
2099 let mut f = Fixture::new();
2100 f.run(&[b"SET", b"str", b"v1"]);
2101 f.run(&[b"SADD", b"set", b"m1"]);
2102 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2103 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
2104 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
2105
2106 for name in [
2107 &b"str"[..],
2108 &b"set"[..],
2109 &b"hash"[..],
2110 &b"list"[..],
2111 &b"zset"[..],
2112 ] {
2113 let dst = [name, b":copy"].concat();
2114 assert_eq!(
2115 f.run(&[b"COPY", name, &dst]),
2116 ":1\r\n",
2117 "copying {}",
2118 String::from_utf8_lossy(name)
2119 );
2120 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
2121 }
2122
2123 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
2124 let mut want = String::from("*2\r\n");
2125 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
2126 want
2127 });
2128 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
2129
2130 f.run(&[b"RPUSH", b"list:copy", b"c"]);
2132 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
2133 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
2134 }
2135
2136 #[test]
2137 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
2138 let mut f = Fixture::new();
2139 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2140 f.run(&[b"SET", b"b", b"v2"]);
2141
2142 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
2143 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2144 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
2145 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2146 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
2147 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
2148 }
2149
2150 #[test]
2151 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
2152 let mut f = Fixture::new();
2153 f.run(&[b"SET", b"a", b"v1"]);
2154
2155 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
2158 f.run(&[b"SELECT", b"1"]);
2159 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
2160 assert_eq!(
2161 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
2162 ":0\r\n",
2163 "taken"
2164 );
2165 assert_eq!(
2166 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
2167 ":1\r\n"
2168 );
2169 }
2170
2171 #[test]
2172 fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
2173 let mut f = Fixture::new();
2174 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2175 assert_eq!(
2176 f.run(&[b"SORT", b"l"]),
2177 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2178 );
2179 assert_eq!(
2181 f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
2182 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2183 );
2184 assert_eq!(
2185 f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
2186 "*1\r\n$1\r\n2\r\n"
2187 );
2188 }
2189
2190 #[test]
2191 fn sort_reads_a_key_per_element_for_by_and_for_get() {
2192 let mut f = Fixture::new();
2193 f.run(&[b"RPUSH", b"l", b"a", b"b"]);
2194 f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
2195 assert_eq!(
2198 f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
2199 "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
2200 );
2201 }
2202
2203 #[test]
2204 fn sort_store_writes_a_list_and_answers_its_length() {
2205 let mut f = Fixture::new();
2206 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2207 assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
2208 assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
2209 assert_eq!(
2210 f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
2211 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2212 );
2213 assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
2216 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
2217 }
2218
2219 #[test]
2220 fn sort_ro_does_not_know_the_word_store() {
2221 let mut f = Fixture::new();
2222 f.run(&[b"RPUSH", b"l", b"2", b"1"]);
2223 assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
2224 assert_eq!(
2225 f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
2226 "-ERR syntax error\r\n"
2227 );
2228 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2229 }
2230
2231 #[test]
2232 fn sort_refuses_what_it_cannot_sort() {
2233 let mut f = Fixture::new();
2234 assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
2235 f.run(&[b"SET", b"s", b"x"]);
2236 assert_eq!(
2237 f.run(&[b"SORT", b"s"]),
2238 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
2239 );
2240 f.run(&[b"RPUSH", b"words", b"one", b"two"]);
2241 assert_eq!(
2242 f.run(&[b"SORT", b"words"]),
2243 "-ERR One or more scores can't be converted into double\r\n"
2244 );
2245 assert_eq!(
2246 f.run(&[b"SORT", b"words", b"ALPHA"]),
2247 "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
2248 );
2249 assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
2250 }
2251
2252 #[test]
2253 fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
2254 let mut f = Fixture::new();
2255 assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
2256 assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
2257 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2258 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2259 assert_eq!(
2260 f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
2261 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2262 );
2263 assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
2266 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2267 }
2268
2269 #[test]
2270 fn move_answers_zero_when_either_end_says_no() {
2271 let mut f = Fixture::new();
2272 assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
2273 assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
2274 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2275 assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
2276 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2277 assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
2280 assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
2281 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2282 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
2283 }
2284
2285 #[test]
2286 fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
2287 let mut f = Fixture::new();
2288 assert_eq!(
2289 f.run(&[b"MOVE", b"a", b"0"]),
2290 "-ERR source and destination objects are the same\r\n"
2291 );
2292 assert_eq!(
2293 f.run(&[b"MOVE", b"a", b"99"]),
2294 "-ERR DB index is out of range\r\n"
2295 );
2296 assert_eq!(
2297 f.run(&[b"MOVE", b"a", b"-1"]),
2298 "-ERR DB index is out of range\r\n"
2299 );
2300 assert_eq!(
2301 f.run(&[b"MOVE", b"a", b"x"]),
2302 "-ERR value is not an integer or out of range\r\n"
2303 );
2304 }
2305
2306 #[test]
2307 fn swapdb_swaps_what_two_connections_would_see() {
2308 let mut f = Fixture::new();
2309 assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
2310 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2311 assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
2312 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2313
2314 assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
2315 assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
2317 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2318 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2319 assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
2321 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2322 }
2323
2324 #[test]
2325 fn swapdb_says_which_index_it_could_not_read() {
2326 let mut f = Fixture::new();
2327 assert_eq!(
2328 f.run(&[b"SWAPDB", b"x", b"1"]),
2329 "-ERR invalid first DB index\r\n"
2330 );
2331 assert_eq!(
2332 f.run(&[b"SWAPDB", b"0", b"y"]),
2333 "-ERR invalid second DB index\r\n"
2334 );
2335 assert_eq!(
2339 f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2340 "-ERR invalid first DB index\r\n"
2341 );
2342 assert_eq!(
2343 f.run(&[b"SWAPDB", b"0", b"99"]),
2344 "-ERR DB index is out of range\r\n"
2345 );
2346 assert_eq!(
2347 f.run(&[b"SWAPDB", b"-1", b"0"]),
2348 "-ERR DB index is out of range\r\n"
2349 );
2350 }
2351
2352 #[test]
2353 fn wait_answers_zero_replicas_without_waiting() {
2354 let mut f = Fixture::new();
2355 assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2356 assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2357 assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2360 assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2363 assert_eq!(
2364 f.run(&[b"WAIT", b"x", b"0"]),
2365 "-ERR value is not an integer or out of range\r\n"
2366 );
2367 assert_eq!(
2368 f.run(&[b"WAIT", b"0", b"-1"]),
2369 "-ERR timeout is negative\r\n"
2370 );
2371 assert_eq!(
2372 f.run(&[b"WAIT", b"0", b"1.5"]),
2373 "-ERR timeout is not an integer or out of range\r\n"
2374 );
2375 }
2376
2377 #[test]
2378 fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2379 let mut f = Fixture::new();
2380 assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2381 assert_eq!(
2382 f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2383 "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2384 );
2385 assert_eq!(
2386 f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2387 "-ERR value is out of range, value must between 0 and 1\r\n"
2388 );
2389 assert_eq!(
2390 f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2391 "-ERR value is out of range, must be positive\r\n"
2392 );
2393 assert_eq!(
2396 f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2397 "-ERR timeout is negative\r\n"
2398 );
2399 }
2400
2401 fn payload(reply: &[u8]) -> Vec<u8> {
2405 let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2406 reply[head + 2..reply.len() - 2].to_vec()
2407 }
2408
2409 #[test]
2410 fn a_value_survives_a_dump_and_a_restore() {
2411 let mut f = Fixture::new();
2412 f.run(&[b"SET", b"s", b"hello"]);
2413 f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2414 f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2415 f.run(&[b"SADD", b"u", b"x", b"y"]);
2416 f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2417 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2418
2419 for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2420 let mut copy = key.to_vec();
2421 copy.push(b'2');
2422 let bytes = payload(&f.raw(&[b"DUMP", key]));
2423 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
2424 assert_eq!(f.run(&[b"TYPE", ©]), f.run(&[b"TYPE", key]));
2425 }
2426
2427 assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2428 assert_eq!(
2429 f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2430 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2431 );
2432 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2433 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2434 assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2435 assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2436 assert_eq!(
2439 f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2440 f.run(&[b"OBJECT", b"ENCODING", b"t"])
2441 );
2442 }
2443
2444 #[test]
2445 fn a_dumped_hash_keeps_its_field_deadlines() {
2446 let mut f = Fixture::new();
2447 f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2448 assert_eq!(
2449 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2450 "*1\r\n:1\r\n"
2451 );
2452 let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2453 assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2454 assert_eq!(
2455 f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2456 "*2\r\n:-1\r\n:100\r\n"
2457 );
2458 }
2459
2460 #[test]
2461 fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2462 let mut f = Fixture::new();
2463 f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2464 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2465 assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2466 assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2467 assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2468 assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2469 assert_eq!(
2472 f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2473 "+OK\r\n"
2474 );
2475 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2476 }
2477
2478 #[test]
2479 fn dump_answers_nothing_for_a_key_that_is_not_there() {
2480 let mut f = Fixture::new();
2481 assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2482 f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2483 f.advance(50);
2484 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2485 }
2486
2487 #[test]
2488 fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2489 let mut f = Fixture::new();
2490 f.run(&[b"SET", b"a", b"first"]);
2491 f.run(&[b"SET", b"b", b"second"]);
2492 let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2493 assert_eq!(
2494 f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2495 "-BUSYKEY Target key name already exists.\r\n"
2496 );
2497 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2498 assert_eq!(
2499 f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2500 "+OK\r\n"
2501 );
2502 assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2503 }
2504
2505 #[test]
2509 fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2510 let mut f = Fixture::new();
2511 f.run(&[b"SET", b"a", b"v"]);
2512 assert_eq!(
2513 f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2514 "-BUSYKEY Target key name already exists.\r\n"
2515 );
2516 assert_eq!(
2519 f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2520 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2521 );
2522 }
2523
2524 #[test]
2525 fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2526 let mut f = Fixture::new();
2527 f.run(&[b"SET", b"a", b"hello"]);
2528 let good = payload(&f.raw(&[b"DUMP", b"a"]));
2529
2530 let mut flipped = good.clone();
2531 flipped[2] ^= 0x40;
2532 assert_eq!(
2533 f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2534 "-ERR DUMP payload version or checksum are wrong\r\n"
2535 );
2536 assert_eq!(
2537 f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2538 "-ERR DUMP payload version or checksum are wrong\r\n"
2539 );
2540 let mut truncated = good[..1].to_vec();
2544 truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2545 let crc = yo_common::crc::crc64(0, &truncated);
2546 truncated.extend_from_slice(&crc.to_le_bytes());
2547 assert_eq!(
2548 f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2549 "-ERR Bad data format\r\n"
2550 );
2551 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2552 }
2553
2554 #[test]
2555 fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2556 let mut f = Fixture::new();
2557 f.run(&[b"SET", b"a", b"v"]);
2558 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2559 assert_eq!(
2560 f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2561 "-ERR Invalid TTL value, must be >= 0\r\n"
2562 );
2563 assert_eq!(
2564 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2565 "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2566 );
2567 assert_eq!(
2568 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2569 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2570 );
2571 assert_eq!(
2573 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2574 "+OK\r\n"
2575 );
2576 assert_eq!(
2577 f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2578 "+OK\r\n"
2579 );
2580 }
2581
2582 #[test]
2586 fn restore_takes_idletime_or_freq_and_not_both() {
2587 let mut f = Fixture::new();
2588 f.run(&[b"SET", b"a", b"v"]);
2589 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2590 assert_eq!(
2591 f.run(&[
2592 b"RESTORE",
2593 b"b",
2594 b"0",
2595 &bytes,
2596 b"IDLETIME",
2597 b"1",
2598 b"FREQ",
2599 b"2"
2600 ]),
2601 "-ERR syntax error\r\n"
2602 );
2603 assert_eq!(
2604 f.run(&[
2605 b"RESTORE",
2606 b"b",
2607 b"0",
2608 &bytes,
2609 b"FREQ",
2610 b"2",
2611 b"IDLETIME",
2612 b"1"
2613 ]),
2614 "-ERR syntax error\r\n"
2615 );
2616 assert_eq!(
2617 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2618 "-ERR syntax error\r\n"
2619 );
2620 assert_eq!(
2621 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2622 "-ERR syntax error\r\n"
2623 );
2624 }
2625
2626 #[test]
2627 fn copy_checks_its_options_before_it_looks_for_anything() {
2628 let mut f = Fixture::new();
2629 assert_eq!(
2632 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2633 "-ERR DB index is out of range\r\n"
2634 );
2635 assert_eq!(
2636 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2637 "-ERR DB index is out of range\r\n"
2638 );
2639 assert_eq!(
2640 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2641 "-ERR value is not an integer or out of range\r\n"
2642 );
2643 assert_eq!(
2644 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2645 "-ERR syntax error\r\n"
2646 );
2647 assert_eq!(
2648 f.run(&[b"COPY", b"a", b"a"]),
2649 "-ERR source and destination objects are the same\r\n"
2650 );
2651 assert_eq!(
2653 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2654 ":0\r\n"
2655 );
2656 }
2657
2658 #[test]
2659 fn time_is_two_bulk_strings_and_moves() {
2660 let mut f = Fixture::new();
2661 let first = f.run(&[b"TIME"]);
2662 assert!(first.starts_with("*2\r\n$"), "got {first}");
2663 let parts: Vec<&str> = first.split("\r\n").collect();
2664 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2665 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2666 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2667 assert!((0..1_000_000).contains(µs), "got {micros}");
2668 assert_ne!(first, f.run(&[b"TIME"]));
2672 }
2673
2674 #[test]
2675 fn a_keyspace_scan_walks_every_key_once() {
2676 let mut f = Fixture::new();
2677 for i in 0..500 {
2678 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2679 }
2680
2681 let mut seen: Vec<String> = Vec::new();
2682 let mut cursor = "0".to_owned();
2683 let mut calls = 0;
2684 loop {
2685 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2686 seen.extend(keys);
2687 cursor = next;
2688 calls += 1;
2689 assert!(calls < 10_000, "the cursor is not advancing");
2690 if cursor == "0" {
2691 break;
2692 }
2693 }
2694
2695 seen.sort();
2696 seen.dedup();
2697 assert_eq!(seen.len(), 500, "every key once and only once");
2698 assert!(calls > 1, "500 keys came back in one batch");
2701 }
2702
2703 #[test]
2704 fn a_scan_narrows_by_pattern_and_by_type() {
2705 let mut f = Fixture::new();
2706 f.run(&[b"SET", b"str", b"v"]);
2707 f.run(&[b"SADD", b"members", b"a"]);
2708 f.run(&[b"HSET", b"fields", b"f", b"v"]);
2709
2710 let all = |f: &mut Fixture, args: &[&[u8]]| {
2711 let mut out: Vec<String> = Vec::new();
2712 let mut cursor = "0".to_owned();
2713 loop {
2714 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2715 line.extend_from_slice(args);
2716 let (next, keys) = scan_reply(&f.run(&line));
2717 out.extend(keys);
2718 cursor = next;
2719 if cursor == "0" {
2720 break;
2721 }
2722 }
2723 out.sort();
2724 out
2725 };
2726
2727 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2728 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2729 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2730 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2732 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2734 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2735 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2737 }
2738
2739 #[test]
2740 fn a_scan_says_what_is_wrong_with_it() {
2741 let mut f = Fixture::new();
2742 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2743 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2744 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2745 assert_eq!(
2746 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2747 "-ERR syntax error\r\n"
2748 );
2749 assert_eq!(
2750 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2751 "-ERR value is not an integer or out of range\r\n"
2752 );
2753 assert_eq!(
2754 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2755 "-ERR syntax error\r\n"
2756 );
2757 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2762 }
2763
2764 #[test]
2765 fn keys_and_randomkey_look_at_the_whole_database() {
2766 let mut f = Fixture::new();
2767 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2768 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2769
2770 for name in ["one", "two", "three"] {
2771 f.run(&[b"SET", name.as_bytes(), b"v"]);
2772 }
2773 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2774 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2775 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2776
2777 for _ in 0..50 {
2778 let got = f.run(&[b"RANDOMKEY"]);
2779 assert!(
2780 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2781 "got {got}"
2782 );
2783 }
2784 }
2785
2786 #[test]
2787 fn a_walk_does_not_answer_keys_that_have_expired() {
2788 let mut f = Fixture::new();
2789 f.run(&[b"SET", b"alive", b"v"]);
2790 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2791 f.server.advance_clock_ms(2);
2792 assert_eq!(
2793 f.run(&[b"DBSIZE"]),
2794 ":2\r\n",
2795 "nothing has collected it yet"
2796 );
2797
2798 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2799 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2800 assert_eq!(keys, ["alive"]);
2801 for _ in 0..20 {
2802 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2803 }
2804 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2807 }
2808
2809 #[test]
2810 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2811 let mut f = Fixture::new();
2812 f.run(&[b"SET", b"k", b"v"]);
2813 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2814 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2815
2816 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2817 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2818 let ms = int(&f.run(&[b"PTTL", b"k"]));
2819 assert!((99_000..=100_000).contains(&ms), "got {ms}");
2820
2821 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2823 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2824 assert_eq!(at, (at_ms + 500) / 1000);
2825 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2826
2827 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2828 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2829 assert_eq!(
2830 f.run(&[b"PERSIST", b"k"]),
2831 ":0\r\n",
2832 "nothing to take off the second time"
2833 );
2834 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2835 assert_eq!(
2836 f.run(&[b"GET", b"k"]),
2837 "$1\r\nv\r\n",
2838 "and the value went through all of that untouched"
2839 );
2840 }
2841
2842 #[test]
2843 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2844 let mut f = Fixture::new();
2845 f.run(&[b"SET", b"str", b"v"]);
2846 f.run(&[b"SADD", b"set", b"a", b"b"]);
2847 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2848
2849 for key in [b"str".as_slice(), b"set", b"hash"] {
2850 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2851 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2852 }
2853 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2856 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2857 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2858 }
2859
2860 #[test]
2861 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2862 let mut f = Fixture::new();
2863 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2864 f.run(&[b"SET", key, b"v"]);
2865 }
2866 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2870 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2871 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2872 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2873 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2874 assert_eq!(
2875 f.run(&[b"EXPIRE", b"a", b"100"]),
2876 ":0\r\n",
2877 "and the key really went, so there is nothing to put a deadline on"
2878 );
2879 }
2880
2881 #[test]
2882 fn the_four_conditions_decide_whether_the_deadline_moves() {
2883 let mut f = Fixture::new();
2884 f.run(&[b"SET", b"k", b"v"]);
2885
2886 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2887 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2888 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2889 assert_eq!(
2890 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2891 ":1\r\n",
2892 "no deadline reads as infinitely far away, so LT passes where GT fails"
2893 );
2894
2895 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2896 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2897 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2898 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2899 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2900 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2901
2902 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2905 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2906 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2907 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2908 }
2909
2910 #[test]
2911 fn the_conditions_are_a_set_and_not_a_keyword() {
2912 let mut f = Fixture::new();
2913 f.run(&[b"SET", b"k", b"v"]);
2914
2915 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2916 assert_eq!(
2917 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2918 ":0\r\n",
2919 "the same keyword twice means it once, and NX now has a deadline to fail on"
2920 );
2921
2922 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2925 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2926 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2927 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2928 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2929 f.run(&[b"PERSIST", b"k"]);
2930 assert_eq!(
2931 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2932 ":0\r\n",
2933 "where LT on its own would have taken it"
2934 );
2935 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2936 }
2937
2938 #[test]
2939 fn a_key_is_gone_once_its_moment_passes() {
2940 let mut f = Fixture::new();
2941 f.run(&[b"SET", b"k", b"v"]);
2942 f.run(&[b"EXPIRE", b"k", b"100"]);
2943
2944 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2945 f.server.set_clock_ms(at as u64 + 1);
2946 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2947 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2948 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2949 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2950 }
2951
2952 #[test]
2953 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2954 let mut f = Fixture::new();
2955 f.run(&[b"SET", b"k", b"v"]);
2956 for (bad, want) in [
2957 (
2958 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2959 "-ERR value is not an integer or out of range\r\n",
2960 ),
2961 (
2962 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2963 "-ERR Unsupported option MAYBE\r\n",
2964 ),
2965 (
2966 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2967 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2968 ),
2969 (
2970 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2971 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2972 ),
2973 (
2974 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2975 "-ERR GT and LT options at the same time are not compatible\r\n",
2976 ),
2977 (
2980 &[b"EXPIRE", b"k", b"9223372036854775807"],
2981 "-ERR invalid expire time in 'expire' command\r\n",
2982 ),
2983 (
2984 &[b"EXPIREAT", b"k", b"9223372036854775807"],
2985 "-ERR invalid expire time in 'expireat' command\r\n",
2986 ),
2987 (
2988 &[b"PEXPIRE", b"k", b"9223372036854775807"],
2989 "-ERR invalid expire time in 'pexpire' command\r\n",
2990 ),
2991 ] {
2992 assert_eq!(f.run(bad), want, "for {bad:?}");
2993 }
2994 assert_eq!(
2995 f.run(&[b"TTL", b"k"]),
2996 ":-1\r\n",
2997 "and none of those put a deadline on anything"
2998 );
2999
3000 assert_eq!(
3004 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
3005 ":1\r\n"
3006 );
3007 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
3008 }
3009
3010 #[test]
3011 fn flushing_empties_this_database_or_every_one_of_them() {
3012 let mut f = Fixture::new();
3013 f.run(&[b"SELECT", b"0"]);
3014 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3015 f.run(&[b"SELECT", b"1"]);
3016 f.run(&[b"SET", b"c", b"3"]);
3017 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3018 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
3021 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3022 f.run(&[b"SELECT", b"0"]);
3024 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
3025 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
3026 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3027 f.run(&[b"SELECT", b"1"]);
3028 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3029 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
3032 assert_eq!(
3033 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
3034 "-ERR syntax error\r\n"
3035 );
3036 }
3037
3038 #[test]
3039 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
3040 let mut f = Fixture::new();
3041 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
3042 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
3043 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
3044 assert_eq!(
3047 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
3048 "*2\r\n:0\r\n:0\r\n"
3049 );
3050 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
3051 assert_eq!(
3052 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
3053 "*0\r\n"
3054 );
3055 assert_eq!(
3056 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
3057 "-ERR Library not found\r\n"
3058 );
3059
3060 assert_eq!(
3063 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
3064 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
3065 );
3066 assert_eq!(
3067 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
3068 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
3069 );
3070 assert_eq!(
3073 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
3074 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
3075 );
3076 assert_eq!(
3077 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
3078 "-ERR Unknown argument bogus\r\n"
3079 );
3080 assert_eq!(
3081 f.run(&[b"SCRIPT", b"EXISTS"]),
3082 "-ERR wrong number of arguments for 'script|exists' command\r\n"
3083 );
3084
3085 assert_eq!(
3088 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
3089 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
3090 );
3091 assert_eq!(
3092 f.run(&[b"FUNCTION", b"STATS"]),
3093 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
3094 );
3095 }
3096
3097 #[test]
3098 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
3099 let mut f = Fixture::new();
3100 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
3101 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
3102 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
3103 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
3106 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
3107 f.run(&[b"SET", b"k", b"hello"]);
3110 assert_eq!(
3111 f.run(&[b"INCR", b"k"]),
3112 "-ERR value is not an integer or out of range\r\n"
3113 );
3114 assert_eq!(
3115 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
3116 "-ERR increment would produce NaN or Infinity\r\n"
3117 );
3118 }
3119
3120 #[test]
3125 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
3126 let mut f = Fixture::new();
3127 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
3128 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
3131 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
3132 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
3133 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
3134 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
3135 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
3136 assert_eq!(
3137 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
3138 "*2\r\n:1\r\n:0\r\n",
3139 "a refused increment reports the value it left alone and applied nothing"
3140 );
3141 assert_eq!(
3142 f.run(&[
3143 b"INCREX",
3144 b"n",
3145 b"BYINT",
3146 b"5",
3147 b"UBOUND",
3148 b"3",
3149 b"SATURATE"
3150 ]),
3151 "*2\r\n:3\r\n:2\r\n"
3152 );
3153 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
3154 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
3155 }
3156
3157 #[test]
3158 fn the_same_answers_come_out_in_resp3_spelling() {
3159 let mut f = Fixture::new();
3160 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
3161 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
3162 assert_eq!(
3165 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
3166 "*2\r\n,1.5\r\n,1.5\r\n"
3167 );
3168 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
3169 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
3172 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
3173 }
3174
3175 #[test]
3176 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
3177 let mut f = Fixture::new();
3178 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
3179 assert_eq!(flow, Flow::Continue);
3180 assert_eq!(
3181 reply,
3182 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
3183 );
3184 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
3187 assert_eq!(reply.matches("\r\n").count(), 1);
3188 }
3189
3190 #[test]
3191 fn arity_is_checked_before_the_command_is() {
3192 let mut f = Fixture::new();
3193 assert_eq!(
3194 f.run(&[b"GET"]),
3195 "-ERR wrong number of arguments for 'get' command\r\n"
3196 );
3197 assert_eq!(
3198 f.run(&[b"MSET", b"k"]),
3199 "-ERR wrong number of arguments for 'mset' command\r\n"
3200 );
3201 assert_eq!(
3205 f.run(&[b"PING", b"a", b"b"]),
3206 "-ERR wrong number of arguments for 'ping' command\r\n"
3207 );
3208 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
3209 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
3210 assert_eq!(
3212 f.run(&[b"DELEX", b"k", b"IFEQ"]),
3213 "-ERR wrong number of arguments for 'delex' command\r\n"
3214 );
3215 }
3216
3217 #[test]
3221 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
3222 let mut f = Fixture::new();
3223 let syntax = "-ERR syntax error\r\n";
3224 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
3225 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
3226 assert_eq!(
3227 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
3228 syntax
3229 );
3230 assert_eq!(
3231 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
3232 syntax
3233 );
3234 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
3235 assert_eq!(
3237 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
3238 "+OK\r\n"
3239 );
3240 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
3241 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
3242 assert_eq!(
3244 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
3245 syntax
3246 );
3247 assert_eq!(
3248 f.run(&[b"INCREX", b"n", b"ENX"]),
3249 "-ERR ENX flag requires an expiration\r\n"
3250 );
3251 assert_eq!(
3252 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
3253 "-ERR UBOUND is not an integer or out of range\r\n"
3254 );
3255 assert_eq!(
3256 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
3257 "-ERR LBOUND can't be greater than UBOUND\r\n"
3258 );
3259 assert_eq!(
3260 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
3261 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
3262 );
3263 }
3264
3265 #[test]
3269 fn the_expiry_rules_are_redis_own() {
3270 let mut f = Fixture::new();
3271 let bad = "-ERR invalid expire time in 'set' command\r\n";
3272 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
3273 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
3274 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
3275 assert_eq!(
3276 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
3277 bad
3278 );
3279 assert_eq!(
3280 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
3281 "-ERR value is not an integer or out of range\r\n"
3282 );
3283 assert_eq!(
3284 f.run(&[b"SETEX", b"k", b"0", b"v"]),
3285 "-ERR invalid expire time in 'setex' command\r\n"
3286 );
3287 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
3288 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
3289 assert_eq!(
3290 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
3291 "-ERR syntax error\r\n",
3292 "the option list is still checked before the key is looked up"
3293 );
3294 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3296 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
3297 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3298 }
3299
3300 #[test]
3301 fn mset_takes_its_pairs_from_the_read_buffer() {
3302 let mut f = Fixture::new();
3303 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
3304 assert_eq!(
3305 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
3306 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
3307 );
3308 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
3309 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
3310 assert_eq!(
3311 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
3312 "-ERR wrong number of key-value pairs\r\n"
3313 );
3314 assert_eq!(
3315 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
3316 "-ERR invalid numkeys value\r\n"
3317 );
3318 assert_eq!(
3319 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
3320 "-ERR invalid numkeys value\r\n"
3321 );
3322 }
3323
3324 #[test]
3325 fn lcs_answers_the_length_the_string_and_the_runs() {
3326 let mut f = Fixture::new();
3327 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
3328 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
3329 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
3330 assert_eq!(
3331 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
3332 "*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"
3333 );
3334 assert_eq!(
3337 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3338 "$6\r\nmytext\r\n"
3339 );
3340 }
3341
3342 #[test]
3343 fn select_moves_the_connection_and_the_databases_stay_apart() {
3344 let mut f = Fixture::new();
3345 f.run(&[b"SET", b"k", b"zero"]);
3346 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3347 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3348 f.run(&[b"SET", b"k", b"four"]);
3349 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3350 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3351 assert_eq!(
3352 f.run(&[b"SELECT", b"99"]),
3353 "-ERR DB index is out of range\r\n"
3354 );
3355 assert_eq!(
3356 f.run(&[b"SELECT", b"-1"]),
3357 "-ERR DB index is out of range\r\n"
3358 );
3359 assert_eq!(
3360 f.run(&[b"SELECT", b"abc"]),
3361 "-ERR value is not an integer or out of range\r\n"
3362 );
3363 f.run(&[b"SELECT", b"4"]);
3365 f.run(&[b"RESET"]);
3366 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3367 }
3368
3369 #[test]
3370 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3371 let mut f = Fixture::new();
3372 let reply = f.run(&[b"HELLO"]);
3373 assert!(reply.starts_with("*14\r\n"), "{reply}");
3374 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3375 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3376 assert!(
3377 reply.contains(":7\r\n"),
3378 "the connection id is in there: {reply}"
3379 );
3380 assert_eq!(
3381 f.run(&[b"HELLO", b"4"]),
3382 "-NOPROTO unsupported protocol version\r\n"
3383 );
3384 assert_eq!(
3385 f.run(&[b"HELLO", b"abc"]),
3386 "-ERR Protocol version is not an integer or out of range\r\n"
3387 );
3388 assert_eq!(
3389 f.run(&[b"HELLO", b"3", b"SETNAME"]),
3390 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3391 );
3392 assert!(
3393 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3394 .starts_with("%7\r\n")
3395 );
3396 assert_eq!(f.session.name(), b"bob");
3397 f.run(&[b"RESET"]);
3398 assert_eq!(f.session.name(), b"");
3399 }
3400
3401 #[test]
3402 fn command_describes_this_server_in_the_shape_a_driver_reads() {
3403 let mut f = Fixture::new();
3404 let count = format!(":{}\r\n", COMMANDS.len());
3405 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3406 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3407 assert_eq!(
3408 info,
3409 "*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\
3410 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3411 );
3412 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3414 assert_eq!(
3415 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3416 "*1\r\n$8\r\ngetrange\r\n"
3417 );
3418 assert_eq!(
3419 f.run(&[b"COMMAND", b"NOPE"]),
3420 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3421 );
3422 }
3423
3424 #[test]
3428 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3429 let mut f = Fixture::new();
3430 assert_eq!(
3431 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3432 "*1\r\n$1\r\nk\r\n"
3433 );
3434 assert_eq!(
3435 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3436 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3437 );
3438 assert_eq!(
3439 f.run(&[
3440 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3441 ]),
3442 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3443 );
3444 assert_eq!(
3445 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3446 "-ERR The command has no key arguments\r\n"
3447 );
3448 assert_eq!(
3449 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3450 "-ERR Invalid number of arguments specified for command\r\n"
3451 );
3452 }
3453
3454 #[test]
3455 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3456 let mut f = Fixture::new();
3457 assert_eq!(
3458 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3459 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3460 );
3461 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3464 assert!(both.starts_with("*6\r\n"), "{both}");
3465 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3466 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3467 assert_eq!(
3468 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3469 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3470 );
3471 assert_eq!(
3472 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3473 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3474 );
3475 assert_eq!(
3476 f.run(&[b"CONFIG", b"GET"]),
3477 "-ERR wrong number of arguments for 'config|get' command\r\n"
3478 );
3479 assert_eq!(
3483 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3484 "-ERR wrong number of arguments for 'config|set' command\r\n"
3485 );
3486 assert_eq!(
3487 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3488 "-ERR syntax error\r\n"
3489 );
3490 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3491 assert_eq!(
3492 f.run(&[b"CONFIG", b"REWRITE"]),
3493 "-ERR The server is running without a config file\r\n"
3494 );
3495 }
3496
3497 #[test]
3498 fn the_eviction_policy_reads_back_what_was_written_to_it() {
3499 let mut f = Fixture::new();
3500 assert_eq!(
3501 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3502 "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3503 );
3504 assert_eq!(
3505 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3506 "+OK\r\n",
3507 "the name is matched without regard to case, like every other one"
3508 );
3509 assert_eq!(
3510 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3511 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3512 );
3513 assert!(
3515 f.run(&[b"INFO", b"memory"])
3516 .contains("maxmemory_policy:allkeys-lfu"),
3517 "INFO and CONFIG disagree about the policy"
3518 );
3519 assert_eq!(
3523 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3524 "-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"
3525 );
3526 assert_eq!(
3529 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3530 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3531 );
3532 f.run(&[
3533 b"CONFIG",
3534 b"SET",
3535 b"hash-max-listpack-entries",
3536 b"7",
3537 b"maxmemory-policy",
3538 b"nonsense",
3539 ]);
3540 assert_eq!(
3541 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3542 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3543 );
3544 }
3545
3546 #[test]
3547 fn the_three_eviction_numbers_read_back_too() {
3548 let mut f = Fixture::new();
3549 for (name, default, set) in [
3550 ("maxmemory-samples", "5", "12"),
3551 ("lfu-log-factor", "10", "3"),
3552 ("lfu-decay-time", "1", "60"),
3553 ] {
3554 let get = || {
3555 format!(
3556 "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3557 name.len(),
3558 default.len()
3559 )
3560 };
3561 assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3562 assert_eq!(
3563 f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3564 "+OK\r\n"
3565 );
3566 assert_eq!(
3567 f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3568 format!(
3569 "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3570 name.len(),
3571 set.len()
3572 )
3573 );
3574 assert_eq!(
3577 f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3578 format!(
3579 "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3580 )
3581 );
3582 }
3583 }
3584
3585 #[test]
3586 fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3587 let mut f = Fixture::new();
3588 assert_eq!(
3589 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3590 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3591 "no limit is the default"
3592 );
3593 for (typed, bytes) in [
3596 (&b"1024"[..], "1024"),
3597 (b"1k", "1000"),
3598 (b"1kb", "1024"),
3599 (b"1M", "1000000"),
3600 (b"1Mb", "1048576"),
3601 (b"1gb", "1073741824"),
3602 (b"100mb", "104857600"),
3603 ] {
3604 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3605 assert_eq!(
3606 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3607 format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3608 "set {}",
3609 String::from_utf8_lossy(typed)
3610 );
3611 }
3612 assert!(
3613 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3614 "the report agrees with the setting"
3615 );
3616
3617 for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3620 assert_eq!(
3621 f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3622 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3623 "refused {}",
3624 String::from_utf8_lossy(bad)
3625 );
3626 }
3627 assert!(
3628 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3629 "and the refusal left the old one alone"
3630 );
3631 }
3632
3633 #[test]
3634 fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3635 let mut f = Fixture::new();
3636 f.run(&[b"SET", b"here", b"already"]);
3637 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3641 assert_eq!(
3642 f.run(&[b"SET", b"k", b"v"]),
3643 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3644 );
3645 assert_eq!(
3646 f.run(&[b"LPUSH", b"l", b"v"]),
3647 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3648 );
3649 assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3651 assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3652 assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3653
3654 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3656 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3657 }
3658
3659 #[test]
3660 fn an_allkeys_policy_makes_room_instead_of_refusing() {
3661 let mut f = Fixture::new();
3662 let val = vec![b'v'; 256];
3663 for i in 0..24000u32 {
3664 let k = format!("key:{i:08}");
3665 f.run(&[b"SET", k.as_bytes(), &val]);
3666 }
3667 let full = f.server.memory_bytes();
3668 assert!(
3669 full > 3 * 1024 * 1024,
3670 "the arena is several segments: {full}"
3671 );
3672
3673 let limit = full - 2 * 1024 * 1024;
3677 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3678 f.run(&[
3679 b"CONFIG",
3680 b"SET",
3681 b"maxmemory",
3682 limit.to_string().as_bytes(),
3683 ]);
3684
3685 for i in 0..2000u32 {
3689 let k = format!("new:{i:08}");
3690 assert_eq!(
3691 f.run(&[b"SET", k.as_bytes(), &val]),
3692 "+OK\r\n",
3693 "write {i} was refused"
3694 );
3695 f.server.refresh_memory();
3696 if f.server.memory_bytes() <= limit {
3697 break;
3698 }
3699 }
3700 assert!(
3701 f.server.memory_bytes() <= limit,
3702 "it never got under: {} against {limit}",
3703 f.server.memory_bytes()
3704 );
3705 let info = f.run(&[b"INFO", b"stats"]);
3706 assert!(!info.contains("evicted_keys:0"), "{info}");
3707 assert!(
3708 f.run(&[b"DBSIZE"]) != ":0\r\n",
3709 "and it did not empty the database to get there"
3710 );
3711 }
3712
3713 #[test]
3714 fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3715 let mut f = Fixture::new();
3722 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3723 let big = vec![b'v'; 200];
3724
3725 for i in 0..400u32 {
3726 let n = i.to_string();
3727 let n = n.as_bytes();
3728 f.run(&[b"SADD", b"s", n]);
3729 f.run(&[b"SADD", b"s2", &big]);
3730 f.run(&[b"HSET", b"h", n, &big]);
3731 f.run(&[b"RPUSH", b"l", &big]);
3732 f.run(&[b"ZADD", b"z", n, n]);
3733 f.run(&[b"ARSET", b"a", n, &big]);
3734 if i % 7 == 0 {
3735 f.run(&[b"SREM", b"s", n]);
3736 f.run(&[b"HDEL", b"h", n]);
3737 f.run(&[b"LPOP", b"l"]);
3738 f.run(&[b"ZREM", b"z", n]);
3739 f.run(&[b"ARDEL", b"a", n]);
3740 }
3741 if i % 53 == 0 {
3742 f.run(&[b"DEL", b"s2"]);
3745 }
3746 assert_eq!(
3747 f.server.settled_memory(),
3748 f.server.memory_bytes(),
3749 "after round {i}"
3750 );
3751 }
3752
3753 assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3756 assert!(
3757 f.server.memory_bytes() > 512 * 1024,
3758 "{}",
3759 f.server.memory_bytes()
3760 );
3761
3762 f.run(&[b"FLUSHALL"]);
3764 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3765 }
3766
3767 #[test]
3768 fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3769 let mut f = Fixture::new();
3774 for i in 0..200u32 {
3775 let n = i.to_string();
3776 f.run(&[b"SADD", b"s", n.as_bytes()]);
3777 f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3778 }
3779 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3780 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3781
3782 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3783 for i in 200..400u32 {
3784 let n = i.to_string();
3785 f.run(&[b"SADD", b"s", n.as_bytes()]);
3786 }
3787 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3788 assert_eq!(
3789 f.server.settled_memory(),
3790 f.server.memory_bytes(),
3791 "the writes it was not watching are in the number it started from"
3792 );
3793 }
3794
3795 #[test]
3796 fn evicted_keys_and_expired_keys_are_different_numbers() {
3797 let mut f = Fixture::new();
3798 f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3801 f.server.advance_clock_ms(20);
3802 f.run(&[b"GET", b"gone"]);
3803 let info = f.run(&[b"INFO", b"stats"]);
3804 assert!(info.contains("expired_keys:1"), "{info}");
3805 assert!(info.contains("evicted_keys:0"), "{info}");
3806 }
3807
3808 #[test]
3809 fn the_object_subcommands_follow_the_policy() {
3810 let mut f = Fixture::new();
3811 f.run(&[b"SET", b"s", b"v"]);
3812 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3816 assert!(
3817 f.run(&[b"OBJECT", b"FREQ", b"s"])
3818 .starts_with("-ERR An LFU maxmemory policy is not selected"),
3819 );
3820
3821 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3822 assert!(
3823 f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3824 .starts_with("-ERR An LFU maxmemory policy is selected"),
3825 );
3826 assert!(
3831 f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3832 "FREQ should answer under an LFU policy"
3833 );
3834 }
3835
3836 #[test]
3837 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3838 let mut f = Fixture::new();
3839 f.run(&[b"SET", b"s", b"hello"]);
3840 f.run(&[b"SET", b"n", b"123"]);
3841 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3842 f.run(&[b"SADD", b"ss", b"a", b"b"]);
3843 f.run(&[b"HSET", b"h", b"f", b"v"]);
3844 for (key, want) in [
3845 (b"s".as_slice(), "embstr"),
3846 (b"n", "int"),
3847 (b"si", "intset"),
3848 (b"ss", "listpack"),
3849 (b"h", "listpack"),
3850 ] {
3851 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3852 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3853 }
3854
3855 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3858 assert_eq!(
3859 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3860 "$10\r\nlistpackex\r\n"
3861 );
3862
3863 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3864 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3865 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3866 }
3867
3868 #[test]
3869 fn object_answers_nil_for_a_key_that_is_not_there() {
3870 let mut f = Fixture::new();
3871 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3872 assert_eq!(
3873 f.run(&[b"OBJECT", sub, b"nokey"]),
3874 "$-1\r\n",
3875 "a nil and not an error, which is what 8.10.1 does"
3876 );
3877 }
3878 f.run(&[b"SET", b"s", b"v"]);
3881 assert!(
3882 f.run(&[b"OBJECT", b"FREQ", b"s"])
3883 .starts_with("-ERR An LFU maxmemory policy is not"),
3884 );
3885 assert_eq!(
3886 f.run(&[b"OBJECT", b"NOPE", b"s"]),
3887 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3888 );
3889 assert_eq!(
3890 f.run(&[b"OBJECT", b"ENCODING"]),
3891 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3892 );
3893 assert_eq!(
3894 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3895 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3896 );
3897 assert_eq!(
3898 f.run(&[b"OBJECT"]),
3899 "-ERR wrong number of arguments for 'object' command\r\n"
3900 );
3901 }
3902
3903 #[test]
3904 fn config_moves_the_ladder_and_object_encoding_agrees() {
3905 let mut f = Fixture::new();
3906 assert_eq!(
3907 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3908 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3909 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3910 );
3911 assert_eq!(
3914 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3915 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3916 );
3917 assert!(
3918 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3919 .starts_with("*8\r\n")
3920 );
3921 assert!(
3922 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3923 .starts_with("*6\r\n")
3924 );
3925
3926 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3927 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3928
3929 assert_eq!(
3930 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3931 "+OK\r\n",
3932 "written under the old name and read back under the new one"
3933 );
3934 assert_eq!(
3935 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3936 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3937 );
3938 assert_eq!(
3939 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3940 "$8\r\nlistpack\r\n",
3941 "the hash that already exists is left exactly where it was"
3942 );
3943 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3944 assert_eq!(
3945 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3946 "$9\r\nhashtable\r\n",
3947 "and the next one built goes straight to a table"
3948 );
3949
3950 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3952 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3953 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3954 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3955 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3956 assert_eq!(
3957 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3958 "$9\r\nhashtable\r\n"
3959 );
3960 }
3961
3962 #[test]
3963 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3964 let mut f = Fixture::new();
3965 assert_eq!(
3966 f.run(&[
3967 b"CONFIG",
3968 b"SET",
3969 b"hash-max-listpack-entries",
3970 b"7",
3971 b"set-max-listpack-entries",
3972 b"abc"
3973 ]),
3974 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3975 );
3976 assert_eq!(
3977 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3978 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3979 "the pair in front of the bad one did not go in"
3980 );
3981 assert_eq!(
3984 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3985 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3986 );
3987 assert_eq!(
3988 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3989 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3990 );
3991 assert_eq!(
3994 f.run(&[
3995 b"CONFIG",
3996 b"SET",
3997 b"set-max-intset-entries",
3998 b"99999999999999999999"
3999 ]),
4000 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
4001 );
4002 assert_eq!(
4003 f.run(&[
4004 b"CONFIG",
4005 b"SET",
4006 b"set-max-intset-entries",
4007 b"9223372036854775807"
4008 ]),
4009 "+OK\r\n"
4010 );
4011 }
4012
4013 #[test]
4014 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
4015 let mut f = Fixture::new();
4016 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
4017 f.run(&[b"SELECT", b"3"]);
4018 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4019 assert_eq!(
4020 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
4021 "$9\r\nhashtable\r\n",
4022 "these are one server wide number in Redis, whatever a Keyspace carries"
4023 );
4024 }
4025
4026 #[test]
4027 fn info_reports_the_numbers_it_can_stand_behind() {
4028 let mut f = Fixture::new();
4029 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
4030 let all = f.run(&[b"INFO"]);
4031 assert!(all.contains("redis_version:8.8.0"), "{all}");
4032 assert!(
4033 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
4034 "{all}"
4035 );
4036 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
4037 assert!(all.contains("role:master"), "{all}");
4038 let clients = f.run(&[b"INFO", b"clients"]);
4040 assert!(clients.contains("connected_clients:0"), "{clients}");
4041 assert!(!clients.contains("redis_version"), "{clients}");
4042 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
4043 }
4044
4045 #[test]
4052 fn commandstats_is_asked_for_and_replication_is_not() {
4053 let mut f = Fixture::new();
4054 for arg in ["", "all", "default", "everything"] {
4055 let info = if arg.is_empty() {
4056 f.run(&[b"INFO"])
4057 } else {
4058 f.run(&[b"INFO", arg.as_bytes()])
4059 };
4060 assert!(info.contains("redis_version"), "{arg}: {info}");
4061 assert!(info.contains("used_cpu_user"), "{arg}: {info}");
4062 assert!(info.contains("used_memory"), "{arg}: {info}");
4063 assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
4064 let asked = arg == "all" || arg == "everything";
4065 assert_eq!(
4066 info.contains("rejected_calls"),
4067 asked,
4068 "{arg} should{} carry the command counters: {info}",
4069 if asked { "" } else { " not" }
4070 );
4071 }
4072
4073 let cpu = f.run(&[b"INFO", b"cpu"]);
4074 assert!(cpu.contains("used_cpu_user"), "{cpu}");
4075 assert!(!cpu.contains("used_memory"), "{cpu}");
4076
4077 let stats = f.run(&[b"INFO", b"commandSTATS"]);
4080 assert!(!stats.contains("used_memory"), "{stats}");
4081 assert!(stats.contains("rejected_calls"), "{stats}");
4082
4083 let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
4085 assert!(pair.contains("used_cpu_user"), "{pair}");
4086 assert!(!pair.contains("master_repl_offset"), "{pair}");
4087
4088 let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
4089 assert!(with_all.contains("used_memory"), "{with_all}");
4090 assert!(with_all.contains("master_repl_offset"), "{with_all}");
4091 assert!(with_all.contains("rejected_calls"), "{with_all}");
4092 assert_eq!(
4094 with_all.matches("used_cpu_user_children").count(),
4095 1,
4096 "{with_all}"
4097 );
4098
4099 let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
4100 assert!(with_default.contains("used_memory"), "{with_default}");
4101 assert!(
4102 with_default.contains("master_repl_offset"),
4103 "{with_default}"
4104 );
4105 assert!(!with_default.contains("rejected_calls"), "{with_default}");
4106 assert_eq!(
4107 with_default.matches("used_cpu_user_children").count(),
4108 1,
4109 "{with_default}"
4110 );
4111 }
4112
4113 #[test]
4122 fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
4123 let mut f = Fixture::new();
4124 let info = f.run(&[b"INFO", b"memory"]);
4125 for field in [
4126 "total_system_memory:",
4127 "mem_cgroup_limit:",
4128 "mem_limit:",
4129 "mem_budget:",
4130 ] {
4131 assert!(info.contains(field), "no {field} in {info}");
4132 }
4133
4134 let field = |name: &str| -> u64 {
4135 info.lines()
4136 .find_map(|l| l.strip_prefix(name))
4137 .unwrap_or_else(|| panic!("no {name} in {info}"))
4138 .trim()
4139 .parse()
4140 .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
4141 };
4142 let limit = field("mem_limit:");
4143 assert_eq!(field("mem_budget:"), limit / 4, "{info}");
4144 if limit != 0 {
4147 let host = field("total_system_memory:");
4148 let cgroup = field("mem_cgroup_limit:");
4149 assert!(
4150 limit == host || limit == cgroup,
4151 "the limit came from neither number: {info}"
4152 );
4153 }
4154 }
4155
4156 #[test]
4165 fn a_command_counts_what_it_did_separately_from_what_it_refused() {
4166 let mut f = Fixture::new();
4167 f.run(&[b"SET", b"k", b"v"]);
4168 f.run(&[b"SET", b"k", b"w"]);
4169 f.run(&[b"LPUSH", b"k", b"x"]);
4171 f.run(&[b"LPUSH", b"k"]);
4173
4174 let stats = f.run(&[b"INFO", b"commandstats"]);
4175 assert!(
4176 stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
4177 "{stats}"
4178 );
4179 assert!(
4180 stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
4181 "{stats}"
4182 );
4183 assert!(
4184 !stats.contains("cmdstat_zadd"),
4185 "a command nobody has sent has no row: {stats}"
4186 );
4187 }
4188
4189 #[test]
4193 fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
4194 let mut f = Fixture::new();
4195 for i in 0..3_000u32 {
4196 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4197 }
4198 for i in 0..1_000u32 {
4199 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4200 }
4201 assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
4202 f.advance(100);
4203 assert_eq!(
4204 f.run(&[b"DBSIZE"]),
4205 ":4000\r\n",
4206 "DBSIZE counts records and nothing has read past the dead ones yet"
4207 );
4208
4209 let mut spent = 0;
4211 for _ in 0..2_000 {
4212 spent += f.server.expire_step(4096);
4213 if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
4214 break;
4215 }
4216 }
4217 assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
4218 assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
4219 for i in 0..1_000u32 {
4220 assert_eq!(
4221 f.run(&[b"GET", format!("k{i}").as_bytes()]),
4222 "$1\r\nv\r\n",
4223 "it took a key that had no deadline"
4224 );
4225 }
4226 }
4227
4228 #[test]
4229 fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
4230 let mut f = Fixture::new();
4231 for i in 0..2_000u32 {
4232 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4233 }
4234 assert_eq!(f.server.expire_step(4096), 0);
4235 f.run(&[b"SELECT", b"3"]);
4237 f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
4238 f.advance(100);
4239 for _ in 0..64 {
4240 f.server.expire_step(4096);
4241 }
4242 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4243 f.run(&[b"SELECT", b"0"]);
4244 assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
4245 assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
4246 }
4247
4248 #[test]
4251 fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
4252 let mut f = Fixture::new();
4253 for i in 0..500u32 {
4254 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4255 }
4256 f.advance(100);
4257 let at = f.server.striped(0).now_ms();
4258 f.server.set_clock_ms(at);
4259 assert!(f.server.expire_slice(8) > 0, "the first one works");
4263 for _ in 0..1_000 {
4264 assert_eq!(
4265 f.server.expire_slice(8),
4266 0,
4267 "the millisecond has not moved and neither should this"
4268 );
4269 }
4270 assert!(
4271 f.server.striped(0).expires() > 400,
4272 "there is plenty left to take"
4273 );
4274 f.server.set_clock_ms(at + 1);
4275 assert!(f.server.expire_slice(8) > 0, "and then it goes again");
4276 }
4277
4278 #[test]
4281 fn info_keyspace_counts_the_keys_that_have_a_deadline() {
4282 let mut f = Fixture::new();
4283 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
4284 assert!(
4285 f.run(&[b"INFO", b"keyspace"])
4286 .contains("db0:keys=3,expires=0"),
4287 "none of them has one yet"
4288 );
4289 f.run(&[b"EXPIRE", b"a", b"1000"]);
4290 f.run(&[b"EXPIRE", b"b", b"1000"]);
4291 let two = f.run(&[b"INFO", b"keyspace"]);
4292 assert!(two.contains("db0:keys=3,expires=2"), "{two}");
4293 f.run(&[b"PERSIST", b"a"]);
4294 f.run(&[b"DEL", b"b"]);
4295 let none = f.run(&[b"INFO", b"keyspace"]);
4296 assert!(none.contains("db0:keys=2,expires=0"), "{none}");
4297
4298 f.run(&[b"SELECT", b"1"]);
4300 f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
4301 let both = f.run(&[b"INFO", b"keyspace"]);
4302 assert!(both.contains("db0:keys=2,expires=0"), "{both}");
4303 assert!(both.contains("db1:keys=1,expires=1"), "{both}");
4304 }
4305
4306 #[cfg(unix)]
4307 #[test]
4308 fn info_cpu_reports_processor_time_that_was_really_measured() {
4309 let mut f = Fixture::new();
4310 let cpu = f.run(&[b"INFO", b"cpu"]);
4311 assert!(cpu.contains("# CPU"), "{cpu}");
4312 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
4314 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
4315 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
4316 assert!(!cpu.contains("redis_version"), "{cpu}");
4317
4318 let before = used_cpu_user(&cpu);
4322 let mut n = 0u64;
4323 let mut rounds = 0;
4324 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
4325 for i in 0..1_000_000u64 {
4326 n = n.wrapping_add(i.wrapping_mul(i));
4327 }
4328 rounds += 1;
4329 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
4333 }
4334 }
4335
4336 #[cfg(unix)]
4338 fn used_cpu_user(info: &str) -> f64 {
4339 info.lines()
4340 .find_map(|l| l.strip_prefix("used_cpu_user:"))
4341 .expect("no used_cpu_user in the reply")
4342 .trim()
4343 .parse()
4344 .expect("used_cpu_user is not a number")
4345 }
4346
4347 #[test]
4353 fn a_command_that_fails_leaves_nothing_half_written() {
4354 let mut f = Fixture::new();
4355 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4356 assert_eq!(reply, "-ERR offset is out of range\r\n");
4357 assert!(!reply.contains(':'), "no integer went out in front of it");
4358 }
4359
4360 #[test]
4361 fn quit_answers_first_and_closes_after() {
4362 let mut f = Fixture::new();
4363 let (flow, reply) = f.flow(&[b"QUIT"]);
4364 assert_eq!(reply, "+OK\r\n");
4365 assert_eq!(flow, Flow::Close);
4366 }
4367
4368 #[test]
4375 fn shutdown_writes_nothing_and_sets_the_flag() {
4376 let mut f = Fixture::new();
4377 assert!(!f.server.stopping(), "nobody has asked yet");
4378
4379 let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4380 assert_eq!(reply, "");
4381 assert_eq!(flow, Flow::Close);
4382 assert!(f.server.stopping());
4383 }
4384
4385 #[test]
4393 fn shutdown_takes_the_flags_redis_takes() {
4394 for flags in [
4395 &[b"NOSAVE".as_slice()][..],
4396 &[b"SAVE"],
4397 &[b"NOW"],
4398 &[b"FORCE"],
4399 &[b"nosave"],
4400 &[b"NOW", b"NOW"],
4401 &[b"SAVE", b"SAVE"],
4402 &[b"NOSAVE", b"NOW", b"FORCE"],
4403 ] {
4404 let mut f = Fixture::new();
4405 let mut parts = vec![b"SHUTDOWN".as_slice()];
4406 parts.extend_from_slice(flags);
4407 let (flow, reply) = f.flow(&parts);
4408 assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4409 assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4410 assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4411 }
4412
4413 for flags in [
4414 &[b"BOGUS".as_slice()][..],
4415 &[b"SAVE", b"NOSAVE"],
4416 &[b"NOSAVE", b"SAVE"],
4417 &[b"ABORT", b"NOW"],
4418 &[b"NOSAVE", b"ABORT"],
4419 &[b"NOW", b"FORCE", b"ABORT"],
4420 ] {
4421 let mut f = Fixture::new();
4422 let mut parts = vec![b"SHUTDOWN".as_slice()];
4423 parts.extend_from_slice(flags);
4424 assert_eq!(
4425 f.run(&parts),
4426 "-ERR syntax error\r\n",
4427 "SHUTDOWN {flags:?} was accepted"
4428 );
4429 assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4430 }
4431 }
4432
4433 #[test]
4441 fn shutdown_abort_never_has_anything_to_abort() {
4442 let mut f = Fixture::new();
4443 for parts in [
4444 &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4445 &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4446 ] {
4447 assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4448 assert!(!f.server.stopping(), "an abort stopped the server");
4449 }
4450 }
4451
4452 struct Backups {
4460 f: Fixture,
4461 dir: PathBuf,
4462 }
4463
4464 impl Backups {
4465 fn new(name: &str) -> Backups {
4466 let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4467 let _ = std::fs::remove_dir_all(&dir);
4468 std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4469 let mut f = Fixture::new();
4470 f.server.set_dir(dir.clone());
4471 Backups { f, dir }
4472 }
4473
4474 fn run(&mut self, parts: &[&[u8]]) -> String {
4475 self.f.run(parts)
4476 }
4477
4478 fn files(&self) -> Vec<String> {
4480 let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4481 Ok(entries) => entries
4482 .filter_map(|e| e.ok())
4483 .map(|e| e.file_name().to_string_lossy().into_owned())
4484 .collect(),
4485 Err(_) => Vec::new(),
4486 };
4487 names.sort();
4488 names
4489 }
4490
4491 fn read(&self, name: &str) -> Vec<u8> {
4492 std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4493 }
4494 }
4495
4496 impl Drop for Backups {
4497 fn drop(&mut self) {
4498 let _ = std::fs::remove_dir_all(&self.dir);
4499 }
4500 }
4501
4502 #[test]
4505 fn backup_walks_the_states_the_reference_walks() {
4506 let mut b = Backups::new("states");
4507 let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4508
4509 assert!(status(&mut b).contains("idle"));
4510 assert!(b.files().is_empty(), "an idle server has written a backup");
4511
4512 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4513 assert!(status(&mut b).contains("incrementing"));
4514 assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4515
4516 assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4517 assert!(status(&mut b).contains("sealed"));
4518 assert_eq!(
4519 b.files(),
4520 [
4521 "appendonly.aof.1.base.rdb",
4522 "appendonly.aof.1.incr.aof",
4523 "appendonly.aof.manifest",
4524 ]
4525 );
4526
4527 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4528 assert!(status(&mut b).contains("idle"));
4529 assert!(b.files().is_empty(), "cleanup left something behind");
4530 }
4531
4532 #[test]
4534 fn backup_refuses_the_moves_the_reference_refuses() {
4535 let mut b = Backups::new("refusals");
4536
4537 assert_eq!(
4538 b.run(&[b"BACKUP", b"SEAL"]),
4539 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4540 );
4541 assert_eq!(
4542 b.run(&[b"BACKUP", b"ABORT"]),
4543 "-ERR No backup in progress\r\n"
4544 );
4545 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4548
4549 b.run(&[b"BACKUP", b"START"]);
4550 assert_eq!(
4551 b.run(&[b"BACKUP", b"START"]),
4552 "-ERR A backup is already in progress, ABORT it first\r\n"
4553 );
4554 assert_eq!(
4555 b.run(&[b"BACKUP", b"CLEANUP"]),
4556 "-ERR Backup is in progress\r\n"
4557 );
4558
4559 b.run(&[b"BACKUP", b"SEAL"]);
4560 assert_eq!(
4561 b.run(&[b"BACKUP", b"START"]),
4562 "-ERR A sealed backup exists, CLEANUP it first\r\n"
4563 );
4564 assert_eq!(
4565 b.run(&[b"BACKUP", b"SEAL"]),
4566 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4567 );
4568 assert_eq!(
4569 b.run(&[b"BACKUP", b"ABORT"]),
4570 "-ERR No backup in progress\r\n"
4571 );
4572 }
4573
4574 #[test]
4581 fn backup_abort_removes_the_file_and_says_who_did_it() {
4582 let mut b = Backups::new("abort");
4583 b.run(&[b"BACKUP", b"START"]);
4584 assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4585
4586 let status = b.run(&[b"BACKUP", b"STATUS"]);
4587 assert!(status.contains("failed"), "{status}");
4588 assert!(status.contains("aborted by user"), "{status}");
4589 assert!(b.files().is_empty(), "abort left the base file behind");
4590 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4591
4592 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4594 assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4595 let status = b.run(&[b"BACKUP", b"STATUS"]);
4596 assert!(status.contains("incrementing"), "{status}");
4597 assert!(!status.contains("aborted"), "the old error was kept");
4598 }
4599
4600 #[test]
4602 fn backup_list_names_the_files_that_are_pinned_so_far() {
4603 let mut b = Backups::new("list");
4604 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4605
4606 b.run(&[b"BACKUP", b"START"]);
4607 let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4608 let base = base.to_string_lossy().into_owned();
4609 assert_eq!(
4610 b.run(&[b"BACKUP", b"LIST"]),
4611 format!("*1\r\n${}\r\n{base}\r\n", base.len())
4612 );
4613
4614 b.run(&[b"BACKUP", b"SEAL"]);
4615 let listed = b.run(&[b"BACKUP", b"LIST"]);
4616 assert!(listed.starts_with("*3\r\n"), "{listed}");
4617 let names: Vec<&str> = listed
4620 .lines()
4621 .filter(|l| l.starts_with('/') || l.contains(":\\"))
4622 .collect();
4623 assert_eq!(names.len(), 3, "{listed}");
4624 assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4625 assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4626 assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4627 }
4628
4629 #[test]
4637 fn a_backup_holds_the_dataset_as_it_was_at_start() {
4638 let mut b = Backups::new("contents");
4639 b.run(&[b"SET", b"bk", b"v1"]);
4640 b.run(&[b"BACKUP", b"START"]);
4641 b.run(&[b"SET", b"bk", b"v2"]);
4642 b.run(&[b"BACKUP", b"SEAL"]);
4643
4644 let base = b.read("appendonly.aof.1.base.rdb");
4645 assert!(base.starts_with(b"REDIS"), "not an RDB file");
4646 assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4647 assert!(
4648 !base.windows(2).any(|w| w == b"v2"),
4649 "the base file moved on after START"
4650 );
4651 let at = base
4656 .windows(8)
4657 .position(|w| w == b"aof-base")
4658 .expect("no aof-base aux field");
4659 assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4660
4661 assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4662 assert_eq!(
4663 String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4664 "file appendonly.aof.1.base.rdb seq 1 type b\n\
4665 file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4666 );
4667 }
4668
4669 #[test]
4672 fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4673 let mut b = Backups::new("status");
4674 b.f.server.set_clock_ms(1_700_000_000_000);
4675
4676 assert_eq!(
4677 b.run(&[b"BACKUP", b"STATUS"]),
4678 "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4679 $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4680 );
4681
4682 b.f.out = Out::new(Proto::Resp3);
4683 b.run(&[b"BACKUP", b"START"]);
4684 assert_eq!(
4685 b.run(&[b"BACKUP", b"STATUS"]),
4686 "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4687 $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4688 );
4689
4690 b.run(&[b"BACKUP", b"SEAL"]);
4691 let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4692 assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4693 }
4694
4695 #[test]
4698 fn a_sealed_backup_is_swept_away_after_the_timeout() {
4699 let mut b = Backups::new("ttl");
4700 b.f.server.set_clock_ms(1_000_000);
4701 assert_eq!(
4702 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4703 "+OK\r\n"
4704 );
4705 b.run(&[b"BACKUP", b"START"]);
4706 b.run(&[b"BACKUP", b"SEAL"]);
4707
4708 b.f.server.set_clock_ms(1_000_000 + 59_000);
4710 b.f.server.backup_expire();
4711 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4712 assert_eq!(b.files().len(), 3);
4713
4714 b.f.server.set_clock_ms(1_000_000 + 60_000);
4715 b.f.server.backup_expire();
4716 let status = b.run(&[b"BACKUP", b"STATUS"]);
4717 assert!(status.contains("idle"), "{status}");
4718 assert!(b.files().is_empty(), "the timeout left the files behind");
4719
4720 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4722 b.run(&[b"BACKUP", b"START"]);
4723 b.run(&[b"BACKUP", b"SEAL"]);
4724 b.f.server.set_clock_ms(9_000_000_000);
4725 b.f.server.backup_expire();
4726 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4727 }
4728
4729 #[test]
4732 fn the_backup_settings_behave_the_way_the_reference_does() {
4733 let mut b = Backups::new("config");
4734 let dir = b.dir.to_string_lossy().into_owned();
4735
4736 assert_eq!(
4737 b.run(&[b"CONFIG", b"GET", b"dir"]),
4738 format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4739 );
4740 assert_eq!(
4741 b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4742 "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4743 );
4744 assert_eq!(
4745 b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4746 "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4747 );
4748
4749 assert_eq!(
4752 b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4753 "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4754 );
4755 assert_eq!(
4756 b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4757 "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4758 );
4759 assert!(
4760 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4761 .contains("argument couldn't be parsed into an integer")
4762 );
4763 assert!(
4764 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4765 .contains("argument must be between 0 and 9223372036854775807 inclusive")
4766 );
4767 }
4768
4769 #[test]
4771 fn backup_help_is_the_text_the_reference_sends() {
4772 let mut f = Fixture::new();
4773 let help = f.run(&[b"BACKUP", b"HELP"]);
4774 assert!(help.starts_with("*17\r\n"), "{help}");
4775 assert!(
4776 help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4777 );
4778 assert!(help.contains("+ Start a new backup into the configured 'backupdirname'.\r\n"));
4779 assert!(help.contains("+ Freeze the current backup (BASE + INCR + manifest).\r\n"));
4780 assert!(help.contains("+ Return this help.\r\n+HELP\r\n+ Print this help.\r\n"));
4781 }
4782
4783 #[test]
4791 fn backup_refuses_what_it_cannot_read() {
4792 let mut f = Fixture::new();
4793 assert_eq!(
4794 f.run(&[b"BACKUP"]),
4795 "-ERR wrong number of arguments for 'backup' command\r\n"
4796 );
4797 assert_eq!(
4798 f.run(&[b"BACKUP", b"START", b"x"]),
4799 "-ERR wrong number of arguments for 'backup' command\r\n"
4800 );
4801 assert_eq!(
4802 f.run(&[b"BACKUP", b"NOPE"]),
4803 "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4804 );
4805 }
4806
4807 #[test]
4808 fn the_command_counter_counts_every_command_including_the_bad_ones() {
4809 let mut f = Fixture::new();
4810 f.run(&[b"PING"]);
4811 f.run(&[b"NOPE"]);
4812 f.run(&[b"GET"]);
4813 assert_eq!(f.server.totals().commands, 3);
4814 }
4815
4816 #[test]
4817 fn what_a_thread_marked_is_taken_by_the_maintenance_turn() {
4818 let mut server = Server::new();
4819 server.set_threads(2);
4820 server.dirty = 0;
4823 server.locals[1].mark(1 << 9);
4824 server.collect_marks();
4825 assert_ne!(server.dirty & (1 << 9), 0);
4826 assert_eq!(server.locals[1].dirty.load(Relaxed), 0);
4828 }
4829
4830 #[test]
4831 fn what_two_threads_counted_is_added_up_when_info_asks() {
4832 let mut server = Server::new();
4833 server.set_threads(2);
4834 let ping = lookup(b"PING").expect("PING is a command");
4838 for (at, calls) in [(0, 2), (1, 3)] {
4839 let counters = &server.locals[at];
4840 for _ in 0..calls {
4841 counters.stats.commands.bump();
4842 counters.cmdstats.at(ping).calls.bump();
4843 }
4844 counters.stats.opened();
4845 }
4846 assert_eq!(server.totals().commands, 5);
4847 assert_eq!(server.totals().clients, 2);
4848 assert_eq!(server.totals().connections, 2);
4849 let rows: Vec<_> = server.command_stats().collect();
4850 assert_eq!(rows.len(), 1);
4851 assert_eq!(rows[0].0, "ping");
4852 assert_eq!(rows[0].1.calls, 5);
4853 server.reset_stats();
4856 assert_eq!(server.totals().commands, 0);
4857 assert_eq!(server.totals().connections, 0);
4858 assert_eq!(server.totals().clients, 2);
4859 }
4860
4861 #[test]
4862 fn the_parked_count_says_what_the_waiter_list_says() {
4863 let mut f = Fixture::new();
4864 assert_eq!(f.server.parked(), 0);
4865 for client in 1..=3u64 {
4866 f.session = Session::new(client);
4867 assert_eq!(f.flow(&[b"BLPOP", b"q", b"0"]).0, Flow::Block);
4868 }
4869 assert_eq!(f.server.parked(), 3);
4870 assert_eq!(f.server.waiters().len(), 3);
4871
4872 f.server.drop_waiter(1);
4876 assert_eq!(f.server.parked(), f.server.waiters().len());
4877 f.server.forget_waiters(1);
4878 assert_eq!(f.server.parked(), f.server.waiters().len());
4879 f.run(&[b"RPUSH", b"q", b"v"]);
4880 let mut out = Out::new(Proto::Resp2);
4881 assert!(f.server.serve_waiter(0, 0, &mut out));
4882 f.server.drop_waiter(0);
4883 assert_eq!(f.server.parked(), 0);
4884 assert!(f.server.waiters().is_empty());
4885 }
4886
4887 #[test]
4888 fn a_set_goes_from_bytes_to_bytes() {
4889 let mut f = Fixture::new();
4890 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4891 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4892 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4893 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4894 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4895 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4896 assert_eq!(
4897 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4898 "*3\r\n:1\r\n:0\r\n:1\r\n"
4899 );
4900 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4901 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4902 }
4903
4904 #[test]
4905 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4906 let mut f = Fixture::new();
4907 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4908 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4909 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4910 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4911 assert_eq!(
4912 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4913 "*2\r\n:0\r\n:0\r\n"
4914 );
4915 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4916 }
4917
4918 #[test]
4919 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
4920 let mut f = Fixture::new();
4924 f.run(&[b"SADD", b"s", b"one"]);
4925 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
4926
4927 f.run(&[b"HELLO", b"3"]);
4928 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
4929 }
4930
4931 #[test]
4932 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
4933 let mut f = Fixture::new();
4936 f.run(&[b"SADD", b"s", b"42"]);
4937 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
4938 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
4939 assert_eq!(
4940 f.run(&[b"SISMEMBER", b"s", b"042"]),
4941 ":0\r\n",
4942 "the member is the bytes and not the number they parse to"
4943 );
4944 }
4945
4946 #[test]
4947 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
4948 let mut f = Fixture::new();
4949 f.run(&[b"SET", b"str", b"v"]);
4950 f.run(&[b"SADD", b"set", b"a"]);
4951
4952 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4953 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
4954 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
4955 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
4956 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
4957 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
4958 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
4959 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
4960 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
4961
4962 assert_eq!(
4965 f.run(&[b"MGET", b"str", b"set", b"nope"]),
4966 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
4967 );
4968 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
4970 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
4971 }
4972
4973 #[test]
4974 fn a_wrongtype_leaves_nothing_half_written() {
4975 let mut f = Fixture::new();
4979 f.run(&[b"SET", b"k", b"v"]);
4980 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
4981 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
4982 assert!(!reply.contains('*'), "an array header went out in front");
4983 }
4984
4985 #[test]
4986 fn emptying_a_set_takes_the_key_with_it() {
4987 let mut f = Fixture::new();
4988 f.run(&[b"SADD", b"s", b"a", b"b"]);
4989 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4990 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
4991 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4992 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
4993 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4994 }
4995
4996 fn split_scan(reply: &str) -> (String, Vec<String>) {
5002 let mut lines = reply.split("\r\n");
5003 assert_eq!(lines.next(), Some("*2"), "got {reply}");
5004 lines.next().expect("the cursor header");
5005 let cursor = lines.next().expect("the cursor").to_owned();
5006 let header = lines.next().expect("the member header");
5007 let n: usize = header[1..].parse().expect("a member count");
5008 let mut members = Vec::with_capacity(n);
5009 for _ in 0..n {
5010 lines.next().expect("a member header");
5011 members.push(lines.next().expect("a member").to_owned());
5012 }
5013 (cursor, members)
5014 }
5015
5016 #[test]
5017 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
5018 let mut f = Fixture::new();
5019 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
5020
5021 let one = f.run(&[b"SPOP", b"s"]);
5022 assert!(
5023 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
5024 "got {one}"
5025 );
5026 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
5027
5028 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
5030 assert!(rest.starts_with("*3\r\n"), "got {rest}");
5031 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
5032 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
5034 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
5035 }
5036
5037 #[test]
5038 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
5039 let mut f = Fixture::new();
5044 f.run(&[b"HELLO", b"3"]);
5045 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
5046
5047 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
5048 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
5050
5051 f.run(&[b"SADD", b"one", b"z"]);
5055 assert_eq!(
5056 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
5057 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
5058 );
5059 }
5060
5061 #[test]
5062 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
5063 let mut f = Fixture::new();
5064 f.run(&[b"SADD", b"s", b"only"]);
5065 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5066 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5067 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
5068
5069 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
5070 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
5073 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
5074 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
5076 }
5077
5078 #[test]
5079 fn a_pop_count_that_is_not_a_positive_number_says_so() {
5080 let mut f = Fixture::new();
5081 f.run(&[b"SADD", b"s", b"a"]);
5082 let bad = "-ERR value is out of range, must be positive\r\n";
5083 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
5084 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
5085 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
5086 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
5088 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
5089 }
5090
5091 #[test]
5092 fn a_scan_walks_a_set_of_any_size_exactly_once() {
5093 let mut f = Fixture::new();
5094 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
5095 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
5096 .into_iter()
5097 .chain(members.iter().map(Vec::as_slice))
5098 .collect();
5099 f.run(&args);
5100
5101 let mut seen = Vec::new();
5102 let mut cursor = "0".to_owned();
5103 loop {
5104 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
5105 let (next, got) = split_scan(&reply);
5106 seen.extend(got);
5107 cursor = next;
5108 if cursor == "0" {
5109 break;
5110 }
5111 }
5112 seen.sort();
5113 seen.dedup();
5114 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
5115
5116 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
5119 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
5120 assert_eq!(cursor, "0");
5121 assert_eq!(got.len(), 3);
5122 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
5124 }
5125
5126 #[test]
5127 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
5128 let mut f = Fixture::new();
5129 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
5130
5131 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
5132 let mut got = got;
5133 got.sort();
5134 assert_eq!(got, ["aa", "ab"]);
5135
5136 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
5139 let mut got = got;
5140 got.sort();
5141 assert_eq!(got, ["12", "13"]);
5142
5143 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
5144 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
5145 assert_eq!(
5146 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
5147 "-ERR syntax error\r\n"
5148 );
5149 assert_eq!(
5152 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
5153 "-ERR syntax error\r\n"
5154 );
5155 }
5156
5157 #[test]
5158 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
5159 let mut f = Fixture::new();
5160 f.run(&[b"SADD", b"src", b"a", b"b"]);
5161 f.run(&[b"SADD", b"dst", b"c"]);
5162
5163 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
5164 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
5165 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
5166 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
5168 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
5169
5170 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
5173 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
5174 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
5175 }
5176
5177 #[test]
5178 fn moving_checks_the_types_in_the_order_redis_checks_them() {
5179 let mut f = Fixture::new();
5183 f.run(&[b"SET", b"str", b"v"]);
5184 f.run(&[b"SADD", b"set", b"a"]);
5185
5186 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5187 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
5188 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
5189 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
5190 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
5191 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
5192 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
5193 assert_eq!(
5194 f.run(&[b"SISMEMBER", b"set", b"a"]),
5195 ":1\r\n",
5196 "and none of that moved anything"
5197 );
5198 }
5199
5200 #[test]
5201 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5202 let mut f = Fixture::new();
5205 f.run(&[b"SADD", b"s", b"a"]);
5206 for bad in [
5207 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
5208 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
5209 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
5210 ] {
5211 let reply = f.run(bad);
5212 assert!(reply.starts_with("-ERR"), "got {reply}");
5213 assert!(!reply.contains('*'), "an array header went out in front");
5214 }
5215 }
5216
5217 #[test]
5218 fn a_hash_writes_reads_and_deletes_its_fields() {
5219 let mut f = Fixture::new();
5220 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
5221 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
5222 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5223 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
5224 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
5225 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
5226 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
5227 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
5228 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
5229 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
5230
5231 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
5234
5235 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
5236 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
5237 assert_eq!(
5238 f.run(&[b"EXISTS", b"h"]),
5239 ":0\r\n",
5240 "and losing the last field lost the key"
5241 );
5242 }
5243
5244 #[test]
5245 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
5246 let mut f = Fixture::new();
5247 f.run(&[b"HSET", b"h", b"a", b"1"]);
5248 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
5249 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
5250 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
5251 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
5252 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
5253
5254 f.run(&[b"HELLO", b"3"]);
5255 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
5256 assert_eq!(
5257 f.run(&[b"HGETALL", b"nokey"]),
5258 "%0\r\n",
5259 "a missing key is the empty hash and never a nil"
5260 );
5261 assert_eq!(
5262 f.run(&[b"HKEYS", b"h"]),
5263 "*1\r\n$1\r\na\r\n",
5264 "and the two that answer one side stay arrays"
5265 );
5266 }
5267
5268 #[test]
5269 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
5270 let mut f = Fixture::new();
5271 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
5272 assert_eq!(
5273 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
5274 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
5275 "the reply is positional, so b is a nil and not a gap"
5276 );
5277 assert_eq!(
5278 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
5279 "*2\r\n$-1\r\n$-1\r\n",
5280 "and a missing key is all nils rather than an empty array"
5281 );
5282
5283 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
5284 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
5285 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5286 }
5287
5288 #[test]
5289 fn a_hash_counts_up_and_says_so_when_it_cannot() {
5290 let mut f = Fixture::new();
5291 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
5292 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
5293 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
5294 assert_eq!(
5295 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
5296 "$4\r\n10.5\r\n",
5297 "a bulk string and not a double, on both protocols"
5298 );
5299
5300 f.run(&[b"HSET", b"h", b"s", b"words"]);
5301 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
5302 assert!(
5303 bad.starts_with("-ERR hash value is not an integer"),
5304 "{bad}"
5305 );
5306 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
5307 assert!(
5308 bad.starts_with("-ERR value is not an integer"),
5309 "a bad argument is not yet a hash value, {bad}"
5310 );
5311 assert_eq!(
5312 f.run(&[b"HGET", b"h", b"s"]),
5313 "$5\r\nwords\r\n",
5314 "and neither of them wrote anything"
5315 );
5316 }
5317
5318 #[test]
5319 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
5320 let mut f = Fixture::new();
5321 for i in 0..500 {
5322 let field = format!("field-{i}");
5323 let value = format!("value-{i}");
5324 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
5325 }
5326
5327 let mut seen: Vec<String> = Vec::new();
5328 let mut cursor = "0".to_owned();
5329 loop {
5330 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
5331 let (next, items) = scan_reply(&reply);
5332 assert_eq!(items.len() % 2, 0, "a pair went out half written");
5333 for pair in items.chunks(2) {
5334 assert_eq!(
5335 pair[0].strip_prefix("field-"),
5336 pair[1].strip_prefix("value-"),
5337 "a field came back with someone else's value"
5338 );
5339 seen.push(pair[0].clone());
5340 }
5341 cursor = next;
5342 if cursor == "0" {
5343 break;
5344 }
5345 }
5346 seen.sort();
5347 seen.dedup();
5348 assert_eq!(seen.len(), 500, "every field once and only once");
5349
5350 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
5351 assert!(
5352 items.iter().all(|s| s.starts_with("field-")),
5353 "NOVALUES still sent the values"
5354 );
5355
5356 let (_, one) = scan_reply(&f.run(&[
5357 b"HSCAN",
5358 b"h",
5359 b"0",
5360 b"MATCH",
5361 b"field-499",
5362 b"COUNT",
5363 b"1000",
5364 ]));
5365 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
5366 }
5367
5368 #[test]
5369 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
5370 let mut f = Fixture::new();
5371 f.run(&[b"HSET", b"h", b"a", b"1"]);
5372 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
5373 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
5374 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
5375 assert_eq!(
5376 f.run(&[b"HRANDFIELD", b"h", b"3"]),
5377 "*1\r\n$1\r\na\r\n",
5378 "a positive count is capped at the size of the hash"
5379 );
5380 assert_eq!(
5381 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
5382 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
5383 "and a negative one repeats itself"
5384 );
5385 assert_eq!(
5386 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5387 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5388 "flat on RESP2"
5389 );
5390
5391 f.run(&[b"HELLO", b"3"]);
5392 assert_eq!(
5393 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5394 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5395 "and nested on RESP3, but still an array and never a map"
5396 );
5397 }
5398
5399 #[test]
5400 fn every_hash_command_says_wrongtype_and_writes_nothing() {
5401 let mut f = Fixture::new();
5402 f.run(&[b"SET", b"str", b"v"]);
5403 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5404
5405 for cmd in [
5406 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
5407 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
5408 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5409 &[b"HGET".as_slice(), b"str", b"f"][..],
5410 &[b"HMGET".as_slice(), b"str", b"f"][..],
5411 &[b"HDEL".as_slice(), b"str", b"f"][..],
5412 &[b"HLEN".as_slice(), b"str"][..],
5413 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5414 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5415 &[b"HGETALL".as_slice(), b"str"][..],
5416 &[b"HKEYS".as_slice(), b"str"][..],
5417 &[b"HVALS".as_slice(), b"str"][..],
5418 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5419 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5420 &[b"HRANDFIELD".as_slice(), b"str"][..],
5421 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5422 &[b"HSCAN".as_slice(), b"str", b"0"][..],
5423 ] {
5424 let reply = f.run(cmd);
5425 assert_eq!(reply, wrong, "{:?}", cmd[0]);
5426 }
5427 assert_eq!(
5428 f.run(&[b"GET", b"str"]),
5429 "$1\r\nv\r\n",
5430 "and none of them touched the value"
5431 );
5432 }
5433
5434 #[test]
5435 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5436 let mut f = Fixture::new();
5437 f.run(&[b"HSET", b"h", b"f", b"v"]);
5438 for bad in [
5439 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5440 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5441 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5442 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5443 ] {
5444 let reply = f.run(bad);
5445 assert!(reply.starts_with("-ERR"), "got {reply}");
5446 assert!(!reply.contains('*'), "an array header went out in front");
5447 }
5448 }
5449
5450 #[test]
5451 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5452 let mut f = Fixture::new();
5453 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5454 assert_eq!(
5455 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5456 "*1\r\n:1\r\n"
5457 );
5458 assert_eq!(
5459 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5460 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5461 "one answer per field, and the two sentinels are TTL's own"
5462 );
5463
5464 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5467 assert!((99_000..=100_000).contains(&ms), "got {ms}");
5468 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5469 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5470 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5471 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5472
5473 assert_eq!(
5474 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5475 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5476 "one for the deadline taken off, and it does not say what it was"
5477 );
5478 assert_eq!(
5479 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5480 "*1\r\n:-1\r\n"
5481 );
5482 assert_eq!(
5483 f.run(&[b"HGET", b"h", b"a"]),
5484 "$1\r\n1\r\n",
5485 "and the field is still there with the value it had"
5486 );
5487 }
5488
5489 #[test]
5490 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5491 let mut f = Fixture::new();
5492 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5493 assert_eq!(
5494 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5495 "*1\r\n:2\r\n",
5496 "two, and not one, because nothing was stored"
5497 );
5498 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5499 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5500
5501 assert_eq!(
5502 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5503 "*1\r\n:2\r\n"
5504 );
5505 assert_eq!(
5506 f.run(&[b"EXISTS", b"h"]),
5507 ":0\r\n",
5508 "and the last field going took the key with it"
5509 );
5510
5511 f.run(&[b"HSET", b"h", b"a", b"1"]);
5514 assert_eq!(
5515 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5516 "*1\r\n:2\r\n"
5517 );
5518 }
5519
5520 #[test]
5521 fn a_field_is_gone_once_its_moment_passes() {
5522 let mut f = Fixture::new();
5523 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5524 assert_eq!(
5525 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5526 "*1\r\n:1\r\n"
5527 );
5528 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5529
5530 f.server.advance_clock_ms(60);
5534 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5535 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5536 assert_eq!(
5537 f.run(&[b"HGETALL", b"h"]),
5538 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5539 "and the walks do not hand back a field that has expired"
5540 );
5541 }
5542
5543 #[test]
5544 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5545 let mut f = Fixture::new();
5546 for cmd in [
5547 &[
5548 b"HEXPIRE".as_slice(),
5549 b"nokey",
5550 b"100",
5551 b"FIELDS",
5552 b"2",
5553 b"a",
5554 b"b",
5555 ][..],
5556 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5557 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5558 &[
5559 b"HEXPIRETIME".as_slice(),
5560 b"nokey",
5561 b"FIELDS",
5562 b"2",
5563 b"a",
5564 b"b",
5565 ][..],
5566 &[
5567 b"HPERSIST".as_slice(),
5568 b"nokey",
5569 b"FIELDS",
5570 b"2",
5571 b"a",
5572 b"b",
5573 ][..],
5574 ] {
5575 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5576 }
5577 }
5578
5579 #[test]
5580 fn writing_a_field_clears_the_deadline_that_was_on_it() {
5581 let mut f = Fixture::new();
5582 f.run(&[b"HSET", b"h", b"a", b"1"]);
5583 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5584 f.run(&[b"HSET", b"h", b"a", b"2"]);
5585 assert_eq!(
5586 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5587 "*1\r\n:-1\r\n",
5588 "Redis has done this since 7.4, and it is why HGETEX exists"
5589 );
5590 }
5591
5592 #[test]
5593 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5594 let mut f = Fixture::new();
5595 f.run(&[b"HSET", b"h", b"a", b"1"]);
5596 assert_eq!(
5597 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5598 "*1\r\n:0\r\n",
5599 "XX on a field with no deadline changes nothing"
5600 );
5601 assert_eq!(
5602 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5603 "*1\r\n:1\r\n"
5604 );
5605 assert_eq!(
5606 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5607 "*1\r\n:0\r\n",
5608 "and NX will not move one that is already there"
5609 );
5610 assert_eq!(
5611 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5612 "*1\r\n:0\r\n"
5613 );
5614 assert_eq!(
5615 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5616 "*1\r\n:1\r\n"
5617 );
5618 assert_eq!(
5619 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5620 "*1\r\n:1\r\n"
5621 );
5622 assert_eq!(
5623 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5624 "*1\r\n:50\r\n"
5625 );
5626 }
5627
5628 #[test]
5629 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5630 let mut f = Fixture::new();
5631 f.run(&[b"HSET", b"h", b"a", b"1"]);
5632 for (bad, want) in [
5633 (
5634 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5635 "-ERR invalid expire time, must be >= 0",
5636 ),
5637 (
5638 &[
5639 b"HEXPIRE".as_slice(),
5640 b"h",
5641 b"9999999999999999",
5642 b"FIELDS",
5643 b"1",
5644 b"a",
5645 ][..],
5646 "-ERR invalid expire time in 'hexpire' command",
5647 ),
5648 (
5649 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5650 "-ERR wrong number of arguments for 'hexpire' command",
5651 ),
5652 (
5653 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5654 "-ERR Parameter `numFields` should be greater than 0",
5655 ),
5656 (
5657 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5658 "-ERR wrong number of arguments",
5659 ),
5660 (
5661 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5662 "-ERR wrong number of arguments",
5663 ),
5664 ] {
5665 let reply = f.run(bad);
5666 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5667 assert!(!reply.contains('*'), "an array header went out in front");
5668 }
5669 assert_eq!(
5670 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5671 "*1\r\n:-1\r\n",
5672 "and not one of them put a deadline on anything"
5673 );
5674 }
5675
5676 #[test]
5677 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5678 let mut f = Fixture::new();
5679 f.run(&[b"SET", b"str", b"v"]);
5680 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5681
5682 for cmd in [
5683 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5684 &[
5685 b"HPEXPIRE".as_slice(),
5686 b"str",
5687 b"100",
5688 b"FIELDS",
5689 b"1",
5690 b"f",
5691 ][..],
5692 &[
5693 b"HEXPIREAT".as_slice(),
5694 b"str",
5695 b"9999999999",
5696 b"FIELDS",
5697 b"1",
5698 b"f",
5699 ][..],
5700 &[
5701 b"HPEXPIREAT".as_slice(),
5702 b"str",
5703 b"9999999999999",
5704 b"FIELDS",
5705 b"1",
5706 b"f",
5707 ][..],
5708 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5709 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5710 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5711 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5712 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5713 ] {
5714 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5715 }
5716 assert_eq!(
5717 f.run(&[b"GET", b"str"]),
5718 "$1\r\nv\r\n",
5719 "and none of them touched the value"
5720 );
5721 }
5722
5723 #[test]
5724 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5725 let mut f = Fixture::new();
5726 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5727 assert_eq!(
5728 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5729 "*2\r\n$1\r\n1\r\n$-1\r\n",
5730 "positional, so the field that was not there is a nil in its place"
5731 );
5732 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5733 assert_eq!(
5734 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5735 "*1\r\n$-1\r\n"
5736 );
5737 assert_eq!(
5738 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5739 "*1\r\n$1\r\n2\r\n"
5740 );
5741 assert_eq!(
5742 f.run(&[b"EXISTS", b"h"]),
5743 ":0\r\n",
5744 "and the last field took the key"
5745 );
5746 }
5747
5748 #[test]
5749 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5750 let mut f = Fixture::new();
5751 f.run(&[b"HSET", b"h", b"a", b"1"]);
5752 assert_eq!(
5753 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5754 "*1\r\n$1\r\n1\r\n"
5755 );
5756 assert_eq!(
5757 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5758 "*1\r\n:-1\r\n",
5759 "no option means leave it alone, which is the one place this is not GETEX"
5760 );
5761
5762 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5763 assert_eq!(
5764 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5765 "*1\r\n:100\r\n"
5766 );
5767 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5768 assert_eq!(
5769 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5770 "*1\r\n:100\r\n",
5771 "and a plain read really does leave it alone"
5772 );
5773 assert_eq!(
5774 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5775 "*1\r\n$1\r\n1\r\n"
5776 );
5777 assert_eq!(
5778 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5779 "*1\r\n:-1\r\n"
5780 );
5781
5782 assert_eq!(
5783 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5784 "*1\r\n$1\r\n1\r\n",
5785 "the value goes out before the deadline that has already gone is applied"
5786 );
5787 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5788 assert_eq!(
5789 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5790 "*1\r\n$-1\r\n"
5791 );
5792 }
5793
5794 #[test]
5795 fn hsetex_writes_all_of_it_or_none_of_it() {
5796 let mut f = Fixture::new();
5797 assert_eq!(
5798 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5799 ":1\r\n"
5800 );
5801 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5802 assert_eq!(
5803 f.run(&[
5804 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5805 ]),
5806 ":0\r\n",
5807 "FNX wants every field named to be missing"
5808 );
5809 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5810 assert_eq!(
5811 f.run(&[b"HEXISTS", b"h", b"new"]),
5812 ":0\r\n",
5813 "and none of the list was written"
5814 );
5815 assert_eq!(
5816 f.run(&[
5817 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5818 ]),
5819 ":0\r\n",
5820 "and FXX wants every one of them to be there"
5821 );
5822 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5823 assert_eq!(
5824 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5825 ":1\r\n"
5826 );
5827 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5828
5829 assert_eq!(
5830 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5831 ":0\r\n"
5832 );
5833 assert_eq!(
5834 f.run(&[b"EXISTS", b"gone"]),
5835 ":0\r\n",
5836 "a key with no fields cannot meet FXX and is not created trying"
5837 );
5838 }
5839
5840 #[test]
5841 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5842 let mut f = Fixture::new();
5843 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5844 assert_eq!(
5845 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5846 "*1\r\n:100\r\n"
5847 );
5848
5849 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5850 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5851 assert_eq!(
5852 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5853 "*1\r\n:100\r\n",
5854 "KEEPTTL put back what the write cleared"
5855 );
5856
5857 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5858 assert_eq!(
5859 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5860 "*1\r\n:-1\r\n",
5861 "and without it a write clears the deadline the way HSET does"
5862 );
5863
5864 assert_eq!(
5867 f.run(&[
5868 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5869 ]),
5870 ":1\r\n"
5871 );
5872 assert_eq!(
5873 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5874 "*1\r\n:100\r\n"
5875 );
5876
5877 assert_eq!(
5878 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5879 ":1\r\n",
5880 "written, and not the separate code the HEXPIRE family has for this"
5881 );
5882 assert_eq!(
5883 f.run(&[b"EXISTS", b"h"]),
5884 ":0\r\n",
5885 "and storing it and then removing it emptied the hash"
5886 );
5887 }
5888
5889 #[test]
5890 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5891 let mut f = Fixture::new();
5892 f.run(&[b"HSET", b"h", b"a", b"1"]);
5893 for (bad, want) in [
5894 (
5896 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5897 "-ERR Number of fields must be a positive integer",
5898 ),
5899 (
5900 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5901 "-ERR The `numfields` parameter must match the number of arguments",
5902 ),
5903 (
5904 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5905 "-ERR Mandatory argument FIELDS is missing or not at the right position",
5906 ),
5907 (
5909 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5910 "-ERR invalid number of fields",
5911 ),
5912 (
5913 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5914 "-ERR wrong number of arguments",
5915 ),
5916 (
5917 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5918 "-ERR unknown argument: FIELD",
5919 ),
5920 (
5921 &[
5922 b"HGETEX".as_slice(),
5923 b"h",
5924 b"KEEPTTL",
5925 b"FIELDS",
5926 b"1",
5927 b"a",
5928 ][..],
5929 "-ERR unknown argument: KEEPTTL",
5930 ),
5931 (
5932 &[
5933 b"HGETEX".as_slice(),
5934 b"h",
5935 b"EX",
5936 b"100",
5937 b"PERSIST",
5938 b"FIELDS",
5939 b"1",
5940 b"a",
5941 ][..],
5942 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
5943 ),
5944 (
5945 &[
5946 b"HSETEX".as_slice(),
5947 b"h",
5948 b"EX",
5949 b"1",
5950 b"KEEPTTL",
5951 b"FIELDS",
5952 b"1",
5953 b"a",
5954 b"1",
5955 ][..],
5956 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
5957 ),
5958 (
5959 &[
5960 b"HSETEX".as_slice(),
5961 b"h",
5962 b"FNX",
5963 b"FXX",
5964 b"FIELDS",
5965 b"1",
5966 b"a",
5967 b"1",
5968 ][..],
5969 "-ERR Only one of FXX or FNX arguments can be specified",
5970 ),
5971 (
5972 &[
5973 b"HSETEX".as_slice(),
5974 b"h",
5975 b"FIELDS",
5976 b"2",
5977 b"a",
5978 b"1",
5979 b"b",
5980 ][..],
5981 "-ERR wrong number of arguments",
5982 ),
5983 (
5984 &[
5985 b"HGETEX".as_slice(),
5986 b"h",
5987 b"EX",
5988 b"-1",
5989 b"FIELDS",
5990 b"1",
5991 b"a",
5992 ][..],
5993 "-ERR invalid expire time, must be >= 0",
5994 ),
5995 (
5996 &[
5997 b"HGETEX".as_slice(),
5998 b"h",
5999 b"PXAT",
6000 b"99999999999999",
6001 b"FIELDS",
6002 b"1",
6003 b"a",
6004 ][..],
6005 "-ERR invalid expire time in 'hgetex' command",
6006 ),
6007 (
6008 &[
6009 b"HSETEX".as_slice(),
6010 b"h",
6011 b"EX",
6012 b"abc",
6013 b"FIELDS",
6014 b"1",
6015 b"a",
6016 b"1",
6017 ][..],
6018 "-ERR value is not an integer or out of range",
6019 ),
6020 ] {
6021 let reply = f.run(bad);
6022 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
6023 assert!(!reply.contains('*'), "an array header went out in front");
6024 }
6025 assert_eq!(
6026 f.run(&[b"HGET", b"h", b"a"]),
6027 "$1\r\n1\r\n",
6028 "and not one of them wrote anything"
6029 );
6030 assert_eq!(
6031 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
6032 "*1\r\n:-1\r\n"
6033 );
6034 }
6035
6036 #[test]
6037 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
6038 let mut f = Fixture::new();
6039 f.run(&[b"SET", b"str", b"v"]);
6040 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6041 for cmd in [
6042 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6043 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6044 &[
6045 b"HGETEX".as_slice(),
6046 b"str",
6047 b"EX",
6048 b"100",
6049 b"FIELDS",
6050 b"1",
6051 b"f",
6052 ][..],
6053 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
6054 ] {
6055 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
6056 }
6057 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
6058 }
6059
6060 #[test]
6068 fn himport_writes_declared_values_into_sorted_fields() {
6069 let mut f = Fixture::new();
6070 assert_eq!(
6071 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
6072 "+OK\r\n"
6073 );
6074 assert_eq!(
6075 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
6076 "+OK\r\n"
6077 );
6078 assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
6079 assert_eq!(
6080 f.run(&[b"HGETALL", b"k"]),
6081 bulks(&["a", "3", "b", "1", "aa", "2"])
6082 );
6083 }
6084
6085 #[test]
6088 fn himport_set_replaces_the_whole_key() {
6089 let mut f = Fixture::new();
6090 f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
6091 f.run(&[b"EXPIRE", b"k", b"100"]);
6092 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6093 assert_eq!(
6094 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6095 "+OK\r\n"
6096 );
6097 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6098 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
6099 }
6100
6101 #[test]
6104 fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
6105 let mut f = Fixture::new();
6106 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
6107 f.run(&[b"SELECT", b"1"]);
6108 assert_eq!(
6109 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6110 "+OK\r\n"
6111 );
6112 f.run(&[b"SELECT", b"0"]);
6113 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
6114 assert_eq!(
6115 f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
6116 "-ERR no such fieldset\r\n"
6117 );
6118 }
6119
6120 #[test]
6127 fn himport_complains_in_the_order_a_real_server_does() {
6128 let mut f = Fixture::new();
6129 f.run(&[b"SET", b"str", b"v"]);
6130 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6131 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6132 assert_eq!(
6133 f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
6134 wrong,
6135 "the type beats a missing fieldset"
6136 );
6137 assert_eq!(
6138 f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
6139 wrong,
6140 "and it beats a value count that does not fit"
6141 );
6142 assert_eq!(
6143 f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
6144 "-ERR no such fieldset\r\n"
6145 );
6146 for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
6148 let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
6149 line.extend_from_slice(values);
6150 assert_eq!(
6151 f.run(&line),
6152 "-ERR value count does not match fieldset field count\r\n",
6153 "{} values into two fields",
6154 values.len()
6155 );
6156 }
6157 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6158 }
6159
6160 #[test]
6162 fn himport_checks_each_subcommand_count_under_its_own_name() {
6163 let mut f = Fixture::new();
6164 assert_eq!(
6165 f.run(&[b"HIMPORT"]),
6166 "-ERR wrong number of arguments for 'himport' command\r\n"
6167 );
6168 for (rest, name) in [
6169 (&["PREPARE"][..], "prepare"),
6170 (&["PREPARE", "fs"][..], "prepare"),
6171 (&["SET"][..], "set"),
6172 (&["SET", "k"][..], "set"),
6173 (&["SET", "k", "fs"][..], "set"),
6174 (&["DISCARD"][..], "discard"),
6175 (&["DISCARD", "a", "b"][..], "discard"),
6176 (&["DISCARDALL", "x"][..], "discardall"),
6177 ] {
6178 let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
6179 line.extend(rest.iter().map(|a| a.as_bytes()));
6180 assert_eq!(
6181 f.run(&line),
6182 format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
6183 "HIMPORT {}",
6184 rest.join(" ")
6185 );
6186 }
6187 assert_eq!(
6188 f.run(&[b"HIMPORT", b"NOPE", b"x"]),
6189 "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
6190 );
6191 }
6192
6193 #[test]
6196 fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
6197 let mut f = Fixture::new();
6198 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6199 assert_eq!(
6200 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
6201 "-ERR duplicate field name in fieldset\r\n"
6202 );
6203 assert_eq!(
6204 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6205 "+OK\r\n"
6206 );
6207 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6208 }
6209
6210 #[test]
6213 fn himport_prepare_replaces_and_the_discards_count() {
6214 let mut f = Fixture::new();
6215 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6216 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
6217 assert_eq!(
6218 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6219 "+OK\r\n"
6220 );
6221 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
6222
6223 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
6224 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
6225 f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
6226 f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
6227 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
6228 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
6229 }
6230
6231 fn int(reply: &str) -> i64 {
6237 let body = reply
6238 .strip_prefix(':')
6239 .and_then(|s| s.strip_suffix("\r\n"))
6240 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
6241 body.parse().expect("an integer")
6242 }
6243
6244 fn int_reply(reply: &str) -> i64 {
6245 let body = reply
6246 .strip_prefix("*1\r\n:")
6247 .and_then(|s| s.strip_suffix("\r\n"))
6248 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
6249 body.parse().expect("an integer")
6250 }
6251
6252 fn scan_reply(reply: &str) -> (String, Vec<String>) {
6254 let mut lines = reply.split("\r\n");
6255 assert_eq!(lines.next(), Some("*2"), "got {reply}");
6256 lines.next().expect("the cursor header");
6257 let cursor = lines.next().expect("a cursor").to_owned();
6258 let header = lines.next().expect("an item count");
6259 let n: usize = header[1..].parse().expect("a count");
6260 let mut items = Vec::with_capacity(n);
6261 for _ in 0..n {
6262 lines.next().expect("an item header");
6263 items.push(lines.next().expect("an item").to_owned());
6264 }
6265 (cursor, items)
6266 }
6267
6268 fn sorted(reply: &str) -> Vec<String> {
6271 let mut lines = reply.split("\r\n");
6272 let header = lines.next().expect("a header");
6273 assert!(
6274 header.starts_with('*') || header.starts_with('~'),
6275 "got {reply}"
6276 );
6277 let n: usize = header[1..].parse().expect("a member count");
6278 let mut got = Vec::with_capacity(n);
6279 for _ in 0..n {
6280 lines.next().expect("a member header");
6281 got.push(lines.next().expect("a member").to_owned());
6282 }
6283 got.sort();
6284 got
6285 }
6286
6287 #[test]
6288 fn the_algebra_answers_what_the_sets_share_and_do_not() {
6289 let mut f = Fixture::new();
6290 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6291 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6292 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
6293
6294 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
6295 assert_eq!(
6296 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
6297 ["1", "2", "3", "4", "5"]
6298 );
6299 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
6300 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
6301
6302 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
6305 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
6306 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
6307 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
6308 }
6309
6310 #[test]
6311 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
6312 let mut f = Fixture::new();
6313 f.run(&[b"SADD", b"a", b"x"]);
6314 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
6315 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
6316 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
6317
6318 f.run(&[b"HELLO", b"3"]);
6319 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
6320 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
6321 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
6322 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
6323 }
6324
6325 #[test]
6326 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
6327 let mut f = Fixture::new();
6328 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6329 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6330
6331 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
6332 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
6333 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
6334 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
6335 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
6336 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
6337
6338 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
6341 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6342 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
6343 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
6344
6345 f.run(&[b"SET", b"str", b"v"]);
6348 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
6349 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
6350 }
6351
6352 #[test]
6353 fn sintercard_counts_without_building_and_stops_at_a_limit() {
6354 let mut f = Fixture::new();
6355 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6356 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
6357
6358 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
6359 assert_eq!(
6360 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6361 ":2\r\n"
6362 );
6363 assert_eq!(
6364 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6365 ":3\r\n",
6366 "a limit of zero is no limit"
6367 );
6368 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
6369 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
6370
6371 assert_eq!(
6373 f.run(&[b"SINTERCARD", b"0", b"a"]),
6374 "-ERR numkeys should be greater than 0\r\n"
6375 );
6376 assert_eq!(
6377 f.run(&[b"SINTERCARD", b"abc", b"a"]),
6378 "-ERR numkeys should be greater than 0\r\n"
6379 );
6380 assert_eq!(
6381 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
6382 "-ERR Number of keys can't be greater than number of args\r\n"
6383 );
6384 assert_eq!(
6385 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
6386 "-ERR LIMIT can't be negative\r\n"
6387 );
6388 assert_eq!(
6389 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
6390 "-ERR syntax error\r\n"
6391 );
6392 f.run(&[b"SADD", b"LIMIT", b"2"]);
6394 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
6395 }
6396
6397 #[test]
6400 fn sunioncard_and_sdiffcard_count_without_building() {
6401 let mut f = Fixture::new();
6402 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6403 f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
6404
6405 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
6406 assert_eq!(
6407 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6408 ":2\r\n"
6409 );
6410 assert_eq!(
6411 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6412 ":6\r\n",
6413 "a limit of zero is no limit"
6414 );
6415 assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6416 assert_eq!(
6417 f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6418 ":4\r\n",
6419 "a missing key adds nothing to a union"
6420 );
6421
6422 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6423 assert_eq!(
6424 f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6425 ":1\r\n"
6426 );
6427 assert_eq!(
6428 f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6429 ":2\r\n",
6430 "a difference is not symmetric"
6431 );
6432 assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6433 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6434 assert_eq!(
6435 f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6436 ":0\r\n",
6437 "nothing taken away from nothing"
6438 );
6439
6440 for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6443 assert_eq!(
6444 f.run(&[name, b"0", b"a"]),
6445 "-ERR numkeys should be greater than 0\r\n"
6446 );
6447 assert_eq!(
6448 f.run(&[name, b"abc", b"a"]),
6449 "-ERR numkeys should be greater than 0\r\n"
6450 );
6451 assert_eq!(
6452 f.run(&[name, b"-1", b"a"]),
6453 "-ERR numkeys should be greater than 0\r\n"
6454 );
6455 assert_eq!(
6456 f.run(&[name, b"3", b"a", b"b"]),
6457 "-ERR Number of keys can't be greater than number of args\r\n"
6458 );
6459 assert_eq!(
6460 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6461 "-ERR LIMIT can't be negative\r\n"
6462 );
6463 assert_eq!(
6464 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6465 "-ERR LIMIT can't be negative\r\n",
6466 "a LIMIT that is not a number gets the negative message too"
6467 );
6468 assert_eq!(
6469 f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6470 "-ERR syntax error\r\n"
6471 );
6472 assert_eq!(
6473 f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6474 "-ERR syntax error\r\n"
6475 );
6476 assert_eq!(
6477 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6478 "-ERR syntax error\r\n"
6479 );
6480 }
6481
6482 f.run(&[b"SADD", b"LIMIT", b"2"]);
6484 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6485 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6486 }
6487
6488 #[test]
6489 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6490 let mut f = Fixture::new();
6491 f.run(&[b"SADD", b"a", b"1"]);
6492 f.run(&[b"SADD", b"d", b"old"]);
6493 f.run(&[b"SET", b"str", b"v"]);
6494
6495 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6496 for bad in [
6497 &[b"SINTER".as_slice(), b"a", b"str"][..],
6498 &[b"SUNION".as_slice(), b"str"][..],
6499 &[b"SDIFF".as_slice(), b"a", b"str"][..],
6500 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6501 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6502 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6503 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6504 ] {
6505 let reply = f.run(bad);
6506 assert_eq!(reply, wrong, "for {:?}", bad[0]);
6507 }
6508 assert_eq!(
6509 f.run(&[b"SMEMBERS", b"d"]),
6510 "*1\r\n$3\r\nold\r\n",
6511 "and the destination was left alone every time"
6512 );
6513 }
6514
6515 #[test]
6518 fn churning_sets_does_not_grow_the_server() {
6519 let mut f = Fixture::new();
6520 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6521 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6522 .chain(std::iter::once(&b"s"[..]))
6523 .chain(members.iter().map(Vec::as_slice))
6524 .collect();
6525
6526 f.run(&args);
6527 f.run(&[b"DEL", b"s"]);
6528 f.server.compact_step();
6529 let after_first = f.server.memory_bytes();
6530
6531 for _ in 0..200 {
6532 f.run(&args);
6533 f.run(&[b"DEL", b"s"]);
6534 f.server.compact_step();
6535 }
6536 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6537 assert!(
6538 f.server.memory_bytes() <= after_first * 2,
6539 "held {} after two hundred passes against {after_first} after one",
6540 f.server.memory_bytes()
6541 );
6542 }
6543
6544 #[test]
6552 fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6553 let mut f = Fixture::new();
6554 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6555 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6556 assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6557 assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6558 assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6559 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6560
6561 assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6563 assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6564 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6565
6566 f.run(&[b"SET", b"num", b"12345"]);
6567 assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6568 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6569 assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6570 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6571 assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6572 }
6573
6574 #[test]
6579 fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6580 let mut f = Fixture::new();
6581 f.run(&[b"SET", b"mykey", b"foobar"]);
6582 assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6583 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6584 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6585 assert_eq!(
6586 f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6587 ":6\r\n"
6588 );
6589 assert_eq!(
6590 f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6591 ":25\r\n"
6592 );
6593 assert_eq!(
6594 f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6595 ":17\r\n"
6596 );
6597 assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6598
6599 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6602
6603 assert_eq!(
6605 f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6606 "-ERR syntax error\r\n"
6607 );
6608 assert_eq!(
6609 f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6610 "-ERR syntax error\r\n"
6611 );
6612 }
6613
6614 #[test]
6620 fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6621 let mut f = Fixture::new();
6622 f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6623 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6624 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6625 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6626 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6627 assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6628
6629 f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6630 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6631 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6632 assert_eq!(
6633 f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6634 ":8\r\n"
6635 );
6636
6637 assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6640 assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6641 }
6642
6643 #[test]
6645 fn the_eight_combinations_write_what_a_real_server_writes() {
6646 let mut f = Fixture::new();
6647 f.run(&[b"SET", b"a", b"abc"]);
6648 f.run(&[b"SET", b"b", b"abd"]);
6649 let cases: &[(&[u8], &str)] = &[
6650 (b"AND", "ab`"),
6651 (b"OR", "abg"),
6652 (b"XOR", "\u{0}\u{0}\u{7}"),
6653 (b"DIFF", "\u{0}\u{0}\u{3}"),
6654 (b"DIFF1", "\u{0}\u{0}\u{4}"),
6655 (b"ANDOR", "ab`"),
6656 (b"ONE", "\u{0}\u{0}\u{7}"),
6657 ];
6658 for (op, want) in cases {
6659 assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6660 assert_eq!(
6661 f.run(&[b"GET", b"d"]),
6662 format!("$3\r\n{want}\r\n"),
6663 "{op:?}"
6664 );
6665 }
6666 assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6668 assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6669
6670 assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6673 assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6674
6675 f.run(&[b"SET", b"dest", b"x"]);
6678 assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6679 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6680 }
6681
6682 #[test]
6684 fn bitop_names_the_operation_in_its_own_complaints() {
6685 let mut f = Fixture::new();
6686 f.run(&[b"SET", b"a", b"abc"]);
6687 assert_eq!(
6688 f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6689 "-ERR syntax error\r\n"
6690 );
6691 assert_eq!(
6692 f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6693 "-ERR BITOP NOT must be called with a single source key.\r\n"
6694 );
6695 for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6696 assert_eq!(
6697 f.run(&[b"BITOP", op, b"d", b"a"]),
6698 format!(
6699 "-ERR BITOP {} must be called with at least two source keys.\r\n",
6700 String::from_utf8_lossy(op)
6701 )
6702 );
6703 }
6704 f.run(&[b"LPUSH", b"l", b"x"]);
6705 assert_eq!(
6706 f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6707 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6708 );
6709 }
6710
6711 #[test]
6713 fn bitfield_reads_and_writes_packed_fields() {
6714 let mut f = Fixture::new();
6715 assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6716 assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6717
6718 assert_eq!(
6719 f.run(&[
6720 b"BITFIELD",
6721 b"bf",
6722 b"INCRBY",
6723 b"u2",
6724 b"100",
6725 b"1",
6726 b"GET",
6727 b"u4",
6728 b"0"
6729 ]),
6730 "*2\r\n:1\r\n:0\r\n"
6731 );
6732 assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6735
6736 assert_eq!(
6738 f.run(&[
6739 b"BITFIELD",
6740 b"bf",
6741 b"SET",
6742 b"u8",
6743 b"#0",
6744 b"255",
6745 b"GET",
6746 b"u8",
6747 b"#0"
6748 ]),
6749 "*2\r\n:0\r\n:255\r\n"
6750 );
6751
6752 assert_eq!(
6753 f.run(&[
6754 b"BITFIELD",
6755 b"bf",
6756 b"OVERFLOW",
6757 b"SAT",
6758 b"INCRBY",
6759 b"i8",
6760 b"0",
6761 b"120",
6762 b"INCRBY",
6763 b"i8",
6764 b"0",
6765 b"120"
6766 ]),
6767 "*2\r\n:119\r\n:127\r\n"
6768 );
6769 assert_eq!(
6770 f.run(&[
6771 b"BITFIELD",
6772 b"bf2",
6773 b"OVERFLOW",
6774 b"FAIL",
6775 b"INCRBY",
6776 b"u2",
6777 b"0",
6778 b"5"
6779 ]),
6780 "*1\r\n$-1\r\n"
6781 );
6782 assert_eq!(
6783 f.run(&[
6784 b"BITFIELD",
6785 b"bf3",
6786 b"OVERFLOW",
6787 b"WRAP",
6788 b"INCRBY",
6789 b"u2",
6790 b"0",
6791 b"5"
6792 ]),
6793 "*1\r\n:1\r\n"
6794 );
6795 assert_eq!(
6796 f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6797 "*1\r\n:4611686018427387904\r\n"
6798 );
6799 }
6800
6801 #[test]
6807 fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6808 let mut f = Fixture::new();
6809 let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6810 assert_eq!(
6811 f.run(&[
6812 b"BITFIELD",
6813 b"bad",
6814 b"SET",
6815 b"u8",
6816 b"0",
6817 b"1",
6818 b"GET",
6819 b"u99",
6820 b"0"
6821 ]),
6822 bad_type
6823 );
6824 assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6825 assert_eq!(
6826 f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6827 bad_type
6828 );
6829 assert_eq!(
6830 f.run(&[b"BITFIELD", b"bad", b"GET"]),
6831 "-ERR syntax error\r\n"
6832 );
6833 assert_eq!(
6834 f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6835 "-ERR syntax error\r\n"
6836 );
6837 assert_eq!(
6838 f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6839 "-ERR syntax error\r\n"
6840 );
6841 assert_eq!(
6842 f.run(&[
6843 b"BITFIELD",
6844 b"bad",
6845 b"OVERFLOW",
6846 b"NOPE",
6847 b"GET",
6848 b"u8",
6849 b"0"
6850 ]),
6851 "-ERR Invalid OVERFLOW type specified\r\n"
6852 );
6853 assert_eq!(
6854 f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6855 "-ERR value is not an integer or out of range\r\n"
6856 );
6857 for at in [&b"#-1"[..], b"abc"] {
6858 assert_eq!(
6859 f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6860 "-ERR bit offset is not an integer or out of range\r\n"
6861 );
6862 }
6863 }
6864
6865 #[test]
6867 fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6868 let mut f = Fixture::new();
6869 f.run(&[b"SET", b"n", b"123"]);
6870 assert_eq!(
6871 f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6872 "*1\r\n:49\r\n"
6873 );
6874 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6876
6877 assert_eq!(
6879 f.run(&[
6880 b"BITFIELD_RO",
6881 b"n",
6882 b"OVERFLOW",
6883 b"SAT",
6884 b"GET",
6885 b"u8",
6886 b"0"
6887 ]),
6888 "*1\r\n:49\r\n"
6889 );
6890 for sub in [&b"SET"[..], b"INCRBY"] {
6891 assert_eq!(
6892 f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6893 "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6894 );
6895 }
6896
6897 assert_eq!(
6898 f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6899 "*1\r\n:0\r\n"
6900 );
6901 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6902 }
6903
6904 #[test]
6906 fn an_offset_off_the_end_of_the_world_is_refused() {
6907 let mut f = Fixture::new();
6908 let bad = "-ERR bit offset is not an integer or out of range\r\n";
6909 for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6910 assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6911 assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6912 }
6913 for arg in [&b"2"[..], b"-1"] {
6914 assert_eq!(
6915 f.run(&[b"BITPOS", b"k", arg]),
6916 "-ERR The bit argument must be 1 or 0.\r\n"
6917 );
6918 }
6919 assert_eq!(
6920 f.run(&[b"BITPOS", b"k", b"abc"]),
6921 "-ERR value is not an integer or out of range\r\n"
6922 );
6923 assert_eq!(
6924 f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
6925 "-ERR value is not an integer or out of range\r\n"
6926 );
6927 let bad_bit = "-ERR bit is not an integer or out of range\r\n";
6928 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
6929 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
6930 }
6931
6932 #[test]
6934 fn every_bitmap_command_says_wrongtype() {
6935 let mut f = Fixture::new();
6936 f.run(&[b"LPUSH", b"l", b"x"]);
6937 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6938 let cases: &[&[&[u8]]] = &[
6939 &[b"SETBIT", b"l", b"0", b"1"],
6940 &[b"GETBIT", b"l", b"0"],
6941 &[b"BITCOUNT", b"l"],
6942 &[b"BITPOS", b"l", b"1"],
6943 &[b"BITOP", b"AND", b"d", b"l"],
6944 &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
6945 &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
6946 ];
6947 for case in cases {
6948 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
6949 }
6950 }
6951
6952 #[test]
6955 fn a_sketch_is_added_to_and_counted() {
6956 let mut f = Fixture::new();
6957 assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
6959 assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
6960 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
6961 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
6962 assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
6965 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
6966
6967 assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
6968 assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
6969 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6970 }
6971
6972 #[test]
6973 fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
6974 let mut f = Fixture::new();
6975 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6976 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";
6978 let mut reply = b"$27\r\n".to_vec();
6979 reply.extend_from_slice(want);
6980 reply.extend_from_slice(b"\r\n");
6981 assert_eq!(f.raw(&[b"GET", b"h"]), reply);
6982 }
6983
6984 #[test]
6985 fn counting_several_keys_counts_their_union() {
6986 let mut f = Fixture::new();
6987 f.run(&[b"PFADD", b"a", b"x", b"y"]);
6988 f.run(&[b"PFADD", b"b", b"y", b"z"]);
6989 assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
6990 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
6991 assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
6994 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
6995 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6996 }
6997
6998 #[test]
6999 fn a_merge_keeps_what_the_destination_had() {
7000 let mut f = Fixture::new();
7001 f.run(&[b"PFADD", b"a", b"x", b"y"]);
7002 f.run(&[b"PFADD", b"b", b"z"]);
7003 assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
7004 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
7005 f.run(&[b"PFADD", b"c", b"w"]);
7007 assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
7008 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
7009 assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
7012 assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
7013 }
7014
7015 #[test]
7016 fn the_debug_forms_answer_four_different_shapes() {
7017 let mut f = Fixture::new();
7018 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7019 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
7020 assert_eq!(
7021 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7022 "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
7023 );
7024 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
7025 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
7026 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
7027 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
7028 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
7029 assert_eq!(
7031 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7032 "-ERR HLL encoding is not sparse\r\n"
7033 );
7034
7035 let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
7037 assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
7038 assert_eq!(reply.matches(":0\r\n").count(), 16381);
7039 assert_eq!(reply.matches(":1\r\n").count(), 2);
7040 assert_eq!(reply.matches(":2\r\n").count(), 1);
7041
7042 assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
7043 }
7044
7045 #[test]
7046 fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
7047 let mut f = Fixture::new();
7048 f.run(&[b"SET", b"plain", b"not a sketch"]);
7049 let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
7050 assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
7051 assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
7052 assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
7053 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
7054
7055 f.run(&[b"RPUSH", b"l", b"x"]);
7058 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7059 assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
7060 assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
7061 assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
7062 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
7063 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
7064 }
7065
7066 #[test]
7067 fn pfdebug_has_its_own_complaints() {
7068 let mut f = Fixture::new();
7069 f.run(&[b"PFADD", b"h", b"a"]);
7070 assert_eq!(
7073 f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
7074 "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
7075 );
7076 let gone = "-ERR The specified key does not exist\r\n";
7078 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
7079 assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
7080 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
7081 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
7082 assert_eq!(
7083 f.run(&[b"PFDEBUG"]),
7084 "-ERR wrong number of arguments for 'pfdebug' command\r\n"
7085 );
7086 assert_eq!(
7087 f.run(&[b"PFSELFTEST", b"x"]),
7088 "-ERR wrong number of arguments for 'pfselftest' command\r\n"
7089 );
7090 }
7091
7092 #[test]
7093 fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
7094 let mut f = Fixture::new();
7095 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7096 let reply = f.raw(&[b"GET", b"h"]);
7099 let short = reply[5..reply.len() - 3].to_vec();
7100 f.run(&[b"SET", b"h", &short]);
7101 assert_eq!(
7102 f.run(&[b"PFCOUNT", b"h"]),
7103 "-INVALIDOBJ Corrupted HLL object detected\r\n"
7104 );
7105 }
7106
7107 #[test]
7108 fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
7109 let mut f = Fixture::new();
7110 f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
7113 for i in 0..10_000u32 {
7114 let ele = format!("e{i}");
7115 f.run(&[b"PFADD", b"big", ele.as_bytes()]);
7116 }
7117 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
7118 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
7119
7120 for key in [&b"small"[..], b"big"] {
7121 let mut copy = key.to_vec();
7122 copy.push(b'2');
7123 let bytes = payload(&f.raw(&[b"DUMP", key]));
7124 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
7125 assert_eq!(f.raw(&[b"GET", ©]), f.raw(&[b"GET", key]));
7128 assert_eq!(
7129 f.run(&[b"PFDEBUG", b"ENCODING", ©]),
7130 f.run(&[b"PFDEBUG", b"ENCODING", key])
7131 );
7132 assert_eq!(f.run(&[b"PFCOUNT", ©]), f.run(&[b"PFCOUNT", key]));
7133 }
7134 assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
7135 assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
7136 }
7137
7138 fn bulk(s: &str) -> String {
7142 format!("${}\r\n{s}\r\n", s.len())
7143 }
7144
7145 fn bulks(parts: &[&str]) -> String {
7148 let mut s = format!("*{}\r\n", parts.len());
7149 for p in parts {
7150 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
7151 }
7152 s
7153 }
7154
7155 #[test]
7156 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
7157 let mut f = Fixture::new();
7158 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
7162 assert_eq!(
7163 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7164 bulks(&["c", "b", "a"])
7165 );
7166 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
7167 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
7168 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
7169 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
7170 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
7171 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
7172 }
7173
7174 #[test]
7175 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
7176 let mut f = Fixture::new();
7177 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
7178 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
7179 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7180 f.run(&[b"RPUSH", b"k", b"a"]);
7181 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
7182 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
7183 assert_eq!(
7184 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7185 bulks(&["z", "a", "y"])
7186 );
7187 }
7188
7189 #[test]
7192 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
7193 let mut f = Fixture::new();
7194 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
7195 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
7196 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
7197 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
7198 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7199 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
7202 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
7203 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
7205 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7206 }
7207
7208 #[test]
7209 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
7210 let mut f = Fixture::new();
7211 f.run(&[b"RPUSH", b"k", b"a"]);
7212 let range = "-ERR value is out of range, must be positive\r\n";
7213 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
7214 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
7215 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
7216 assert_eq!(
7219 f.run(&[b"LPOP", b"k", b"1", b"2"]),
7220 "-ERR wrong number of arguments for 'lpop' command\r\n"
7221 );
7222 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7223 }
7224
7225 #[test]
7226 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
7227 let mut f = Fixture::new();
7228 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7229 assert_eq!(
7230 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7231 bulks(&["a", "b", "c"])
7232 );
7233 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
7234 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
7235 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
7236 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
7237 assert_eq!(
7238 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
7239 bulks(&["a", "b", "c"])
7240 );
7241 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
7244 assert_eq!(
7245 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
7246 "-ERR value is not an integer or out of range\r\n"
7247 );
7248 }
7249
7250 #[test]
7251 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
7252 let mut f = Fixture::new();
7253 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7254 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
7255 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
7256 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
7257 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
7258 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
7259 assert_eq!(
7260 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7261 bulks(&["a", "b", "z"])
7262 );
7263 assert_eq!(
7266 f.run(&[b"LSET", b"k", b"99", b"z"]),
7267 "-ERR index out of range\r\n"
7268 );
7269 assert_eq!(
7270 f.run(&[b"LSET", b"nope", b"0", b"z"]),
7271 "-ERR no such key\r\n"
7272 );
7273 }
7274
7275 #[test]
7276 fn linsert_says_three_things_with_one_signed_number() {
7277 let mut f = Fixture::new();
7278 assert_eq!(
7281 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
7282 ":0\r\n"
7283 );
7284 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7285 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
7286 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
7287 assert_eq!(
7288 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7289 bulks(&["X", "a", "b", "Y"])
7290 );
7291 assert_eq!(
7292 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
7293 ":-1\r\n"
7294 );
7295 assert_eq!(
7296 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
7297 "-ERR syntax error\r\n"
7298 );
7299 }
7300
7301 #[test]
7302 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
7303 let mut f = Fixture::new();
7304 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
7305 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
7306 assert_eq!(
7307 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7308 bulks(&["b", "c", "a"])
7309 );
7310 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
7311 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7312 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
7313 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
7314 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7315 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
7316 }
7317
7318 #[test]
7319 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
7320 let mut f = Fixture::new();
7321 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
7322 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
7323 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7324 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
7327 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7328 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
7329 }
7330
7331 #[test]
7332 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
7333 let mut f = Fixture::new();
7334 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
7335 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
7336 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
7337 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
7338 assert_eq!(
7339 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
7340 "*2\r\n:0\r\n:3\r\n"
7341 );
7342 assert_eq!(
7343 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
7344 "*3\r\n:6\r\n:3\r\n:0\r\n"
7345 );
7346 assert_eq!(
7349 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
7350 "*1\r\n:0\r\n"
7351 );
7352 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
7355 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
7356 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
7357 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
7358 }
7359
7360 #[test]
7361 fn lpos_words_its_three_mistakes_the_way_redis_does() {
7362 let mut f = Fixture::new();
7363 f.run(&[b"RPUSH", b"p", b"a"]);
7364 assert_eq!(
7367 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
7368 "-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"
7369 );
7370 assert_eq!(
7371 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
7372 "-ERR COUNT can't be negative\r\n"
7373 );
7374 assert_eq!(
7375 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
7376 "-ERR MAXLEN can't be negative\r\n"
7377 );
7378 assert_eq!(
7379 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
7380 "-ERR syntax error\r\n"
7381 );
7382 assert_eq!(
7383 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
7384 "-ERR syntax error\r\n"
7385 );
7386 }
7387
7388 #[test]
7389 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
7390 let mut f = Fixture::new();
7391 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7392 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
7393 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7394 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
7395 assert_eq!(
7396 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
7397 "$1\r\na\r\n"
7398 );
7399 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
7400 f.run(&[b"DEL", b"r"]);
7403 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
7404 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
7405 assert_eq!(
7406 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
7407 bulks(&["3", "1", "2"])
7408 );
7409 assert_eq!(
7410 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7411 "$-1\r\n"
7412 );
7413 assert_eq!(
7414 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7415 "-ERR syntax error\r\n"
7416 );
7417 }
7418
7419 #[test]
7420 fn a_move_checks_the_destination_before_it_takes_anything() {
7421 let mut f = Fixture::new();
7422 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7423 f.run(&[b"SET", b"str", b"v"]);
7424 assert_eq!(
7425 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7426 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7427 );
7428 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7430 }
7431
7432 #[test]
7433 fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7434 for (from, to, order, want) in [
7438 ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7439 ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7440 ("LEFT", "LEFT", "OBO", ["b", "a"]),
7441 ("LEFT", "LEFT", "BULK", ["a", "b"]),
7442 ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7443 ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7444 ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7445 ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7446 ] {
7447 let mut f = Fixture::new();
7448 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7449 let how = format!("{from} {to} {order}");
7450 let reply = f.run(&[
7451 b"LMOVEM",
7452 b"s",
7453 b"d",
7454 from.as_bytes(),
7455 to.as_bytes(),
7456 b"COUNT",
7457 b"2",
7458 order.as_bytes(),
7459 ]);
7460 assert_eq!(reply, bulks(&want), "the reply for {how}");
7461 assert_eq!(
7462 f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7463 bulks(&want),
7464 "the destination for {how}"
7465 );
7466 }
7467 }
7468
7469 #[test]
7470 fn a_block_move_of_one_needs_no_count_at_all() {
7471 let mut f = Fixture::new();
7472 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7473 assert_eq!(
7474 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7475 bulks(&["a"])
7476 );
7477 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7478 assert_eq!(
7481 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7482 "-ERR syntax error\r\n"
7483 );
7484 assert_eq!(
7485 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7486 "-ERR syntax error\r\n"
7487 );
7488 }
7489
7490 #[test]
7491 fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7492 let mut f = Fixture::new();
7493 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7494 assert_eq!(
7498 f.run(&[
7499 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7500 ]),
7501 "*-1\r\n"
7502 );
7503 assert_eq!(
7504 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7505 bulks(&["a", "b", "c"])
7506 );
7507 assert_eq!(
7509 f.run(&[
7510 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7511 ]),
7512 bulks(&["a", "b", "c"])
7513 );
7514 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7515 assert_eq!(
7516 f.run(&[
7517 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7518 ]),
7519 "*-1\r\n"
7520 );
7521 }
7522
7523 #[test]
7524 fn a_block_move_onto_itself_rotates_by_the_count() {
7525 let mut f = Fixture::new();
7526 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7527 assert_eq!(
7528 f.run(&[
7529 b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7530 ]),
7531 bulks(&["a", "b"])
7532 );
7533 assert_eq!(
7534 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7535 bulks(&["c", "a", "b"])
7536 );
7537 }
7538
7539 #[test]
7540 fn a_block_move_reads_the_count_before_the_ordering_word() {
7541 let mut f = Fixture::new();
7542 f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7543 f.run(&[b"SET", b"str", b"v"]);
7544 let count = "-ERR count should be greater than 0\r\n";
7545 assert_eq!(
7546 f.run(&[
7547 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7548 ]),
7549 count
7550 );
7551 assert_eq!(
7552 f.run(&[
7553 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7554 ]),
7555 count
7556 );
7557 assert_eq!(
7558 f.run(&[
7559 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7560 ]),
7561 "-ERR syntax error\r\n"
7562 );
7563 assert_eq!(
7564 f.run(&[
7565 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7566 ]),
7567 "-ERR syntax error\r\n"
7568 );
7569 assert_eq!(
7572 f.run(&[
7573 b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7574 ]),
7575 count
7576 );
7577 assert_eq!(
7578 f.run(&[
7579 b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7580 ]),
7581 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7582 );
7583 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7584 }
7585
7586 #[test]
7587 fn lmpop_answers_from_the_first_key_that_has_anything() {
7588 let mut f = Fixture::new();
7589 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7590 assert_eq!(
7593 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7594 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7595 );
7596 assert_eq!(
7597 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7598 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7599 );
7600 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7601 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7604 }
7605
7606 #[test]
7607 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7608 let mut f = Fixture::new();
7609 f.run(&[b"RPUSH", b"k", b"a"]);
7610 assert_eq!(
7611 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7612 "-ERR numkeys should be greater than 0\r\n"
7613 );
7614 assert_eq!(
7615 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7616 "-ERR numkeys should be greater than 0\r\n"
7617 );
7618 assert_eq!(
7619 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7620 "-ERR count should be greater than 0\r\n"
7621 );
7622 assert_eq!(
7625 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7626 "-ERR syntax error\r\n"
7627 );
7628 assert_eq!(
7629 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7630 "-ERR syntax error\r\n"
7631 );
7632 assert_eq!(
7633 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7634 "-ERR syntax error\r\n"
7635 );
7636 assert_eq!(
7637 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7638 "-ERR syntax error\r\n"
7639 );
7640 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7641 }
7642
7643 #[test]
7644 fn every_list_command_says_wrongtype_and_writes_nothing() {
7645 let mut f = Fixture::new();
7646 f.run(&[b"SET", b"str", b"v"]);
7647 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7648 for cmd in [
7649 &[b"LPUSH".as_slice(), b"str", b"a"][..],
7650 &[b"RPUSH", b"str", b"a"],
7651 &[b"LPUSHX", b"str", b"a"],
7652 &[b"RPUSHX", b"str", b"a"],
7653 &[b"LPOP", b"str"],
7654 &[b"LPOP", b"str", b"2"],
7655 &[b"RPOP", b"str"],
7656 &[b"LLEN", b"str"],
7657 &[b"LRANGE", b"str", b"0", b"-1"],
7658 &[b"LINDEX", b"str", b"0"],
7659 &[b"LSET", b"str", b"0", b"a"],
7660 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7661 &[b"LREM", b"str", b"0", b"a"],
7662 &[b"LTRIM", b"str", b"0", b"-1"],
7663 &[b"LPOS", b"str", b"a"],
7664 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7665 &[b"RPOPLPUSH", b"str", b"d"],
7666 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7667 &[b"LMPOP", b"1", b"str", b"LEFT"],
7668 ] {
7669 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7670 }
7671 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7672 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7673 }
7674
7675 #[test]
7679 fn a_timeout_has_three_ways_of_being_wrong() {
7680 let mut f = Fixture::new();
7681 let not_float = "-ERR timeout is not a float or out of range\r\n";
7682 let range = "-ERR timeout is out of range\r\n";
7683 for (bad, want) in [
7684 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7685 (&[b"BLPOP", b"k", b"nan"], not_float),
7686 (&[b"BLPOP", b"k", b""], not_float),
7687 (&[b"BLPOP", b"k", b" 1"], not_float),
7690 (&[b"BLPOP", b"k", b"1 "], not_float),
7691 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7692 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7693 (&[b"BLPOP", b"k", b"1e400"], range),
7696 (&[b"BLPOP", b"k", b"inf"], range),
7697 (&[b"BLPOP", b"k", b"9999999999999999"], range),
7698 (&[b"BRPOP", b"k", b"abc"], not_float),
7699 (
7700 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7701 not_float,
7702 ),
7703 (
7704 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7705 "-ERR timeout is negative\r\n",
7706 ),
7707 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7708 ] {
7709 assert_eq!(f.run(bad), want, "for {bad:?}");
7710 }
7711 }
7712
7713 #[test]
7716 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7717 let mut f = Fixture::new();
7718 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7719 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7720 assert_eq!(flow, Flow::Block, "for {timeout:?}");
7721 assert!(out.is_empty(), "for {timeout:?}");
7722 }
7723 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7727 assert_eq!(flow, Flow::Block);
7728 assert!(out.is_empty());
7729 }
7730
7731 #[test]
7732 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7733 let mut f = Fixture::new();
7734 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7735
7736 assert_eq!(
7739 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7740 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7741 );
7742 assert_eq!(
7743 f.run(&[b"BRPOP", b"L", b"0"]),
7744 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7745 );
7746 assert_eq!(
7747 f.run(&[
7748 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7749 ]),
7750 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7751 );
7752 assert_eq!(
7753 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7754 "$1\r\nd\r\n"
7755 );
7756 assert_eq!(
7757 f.run(&[b"EXISTS", b"L"]),
7758 ":0\r\n",
7759 "and the key went with it"
7760 );
7761 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7762 f.run(&[b"RPUSH", b"D", b"x"]);
7765 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7766 assert_eq!(
7767 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7768 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7769 );
7770 }
7771
7772 #[test]
7773 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7774 let mut f = Fixture::new();
7775 f.run(&[b"RPUSH", b"k", b"a"]);
7776 for (bad, want) in [
7777 (
7778 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7779 "-ERR numkeys should be greater than 0\r\n",
7780 ),
7781 (
7782 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7783 "-ERR numkeys should be greater than 0\r\n",
7784 ),
7785 (
7788 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7789 "-ERR syntax error\r\n",
7790 ),
7791 (
7792 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7793 "-ERR syntax error\r\n",
7794 ),
7795 (
7796 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7797 "-ERR syntax error\r\n",
7798 ),
7799 (
7800 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7801 "-ERR syntax error\r\n",
7802 ),
7803 (
7806 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7807 "-ERR count should be greater than 0\r\n",
7808 ),
7809 (
7810 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7811 "-ERR count should be greater than 0\r\n",
7812 ),
7813 ] {
7814 assert_eq!(f.run(bad), want, "for {bad:?}");
7815 }
7816 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7817 }
7818
7819 #[test]
7820 fn a_blocking_move_reads_its_directions_before_its_timeout() {
7821 let mut f = Fixture::new();
7822 assert_eq!(
7825 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7826 "-ERR syntax error\r\n"
7827 );
7828 assert_eq!(
7829 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7830 "-ERR syntax error\r\n"
7831 );
7832 }
7833
7834 #[test]
7838 fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7839 let mut f = Fixture::new();
7840 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7841 assert_eq!(
7842 f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7843 (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7844 );
7845 assert_eq!(
7846 f.run(&[
7847 b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7848 ]),
7849 bulks(&["e", "d"])
7850 );
7851 assert_eq!(
7852 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7853 bulks(&["a", "e", "d"])
7854 );
7855 assert_eq!(
7857 f.run(&[
7858 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7859 ]),
7860 bulks(&["b", "c"])
7861 );
7862 assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7863 }
7864
7865 #[test]
7869 fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7870 let mut f = Fixture::new();
7871 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7872 assert_eq!(
7874 f.flow(&[
7875 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7876 ]),
7877 (Flow::Continue, bulks(&["a", "b"]))
7878 );
7879
7880 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7881 assert_eq!(
7884 f.flow(&[
7885 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7886 ])
7887 .0,
7888 Flow::Block
7889 );
7890 assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7891 }
7892
7893 #[test]
7894 fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7895 let mut f = Fixture::new();
7896 let syntax = "-ERR syntax error\r\n";
7897 assert_eq!(
7899 f.run(&[
7900 b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7901 ]),
7902 syntax
7903 );
7904 assert_eq!(
7906 f.run(&[
7907 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7908 ]),
7909 "-ERR timeout is not a float or out of range\r\n"
7910 );
7911 assert_eq!(
7912 f.run(&[
7913 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7914 ]),
7915 "-ERR timeout is negative\r\n"
7916 );
7917 assert_eq!(
7919 f.run(&[
7920 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
7921 ]),
7922 "-ERR count should be greater than 0\r\n"
7923 );
7924 assert_eq!(
7925 f.run(&[
7926 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
7927 ]),
7928 syntax
7929 );
7930 assert_eq!(
7933 f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
7934 syntax
7935 );
7936 assert_eq!(
7937 f.run(&[
7938 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
7939 ]),
7940 syntax
7941 );
7942 }
7943
7944 #[test]
7947 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
7948 let mut f = Fixture::new();
7949 f.run(&[b"SET", b"S", b"v"]);
7950 f.run(&[b"RPUSH", b"D", b"x"]);
7951 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7952
7953 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
7954 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
7957 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
7958 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
7959 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
7960 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
7963 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
7964 assert_eq!(
7965 f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
7966 wrong
7967 );
7968 assert_eq!(
7969 f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
7970 wrong
7971 );
7972
7973 assert_eq!(
7977 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7978 .0,
7979 Flow::Block
7980 );
7981 assert_eq!(
7985 f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7986 .0,
7987 Flow::Block
7988 );
7989 f.run(&[b"RPUSH", b"E", b"1", b"2"]);
7990 assert_eq!(
7991 f.flow(&[
7992 b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
7993 ])
7994 .0,
7995 Flow::Block
7996 );
7997 }
7998
7999 #[test]
8003 fn churning_lists_does_not_grow_the_server() {
8004 let mut f = Fixture::new();
8005 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
8006 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
8007 .into_iter()
8008 .chain(vals.iter().map(Vec::as_slice))
8009 .collect();
8010
8011 f.run(&args);
8012 f.run(&[b"DEL", b"k"]);
8013 f.server.compact_step();
8014 let after_first = f.server.memory_bytes();
8015
8016 for _ in 0..200 {
8017 f.run(&args);
8018 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
8019 f.server.compact_step();
8020 }
8021 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8022 assert!(
8023 f.server.memory_bytes() <= after_first * 2,
8024 "held {} after two hundred passes against {after_first} after one",
8025 f.server.memory_bytes()
8026 );
8027 }
8028
8029 #[test]
8032 fn a_sorted_set_takes_scores_and_gives_them_back() {
8033 let mut f = Fixture::new();
8034 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
8035 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
8036 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
8037 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
8038 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
8039 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
8040 assert_eq!(
8041 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
8042 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
8043 );
8044 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
8045 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
8046 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
8048 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8049 }
8050
8051 #[test]
8052 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
8053 let mut f = Fixture::new();
8054 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
8055 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
8056 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
8057 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
8058
8059 f.out = Out::new(Proto::Resp3);
8060 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
8061 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
8062 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
8063 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
8064 }
8065
8066 #[test]
8067 fn the_zadd_options_gate_what_gets_written() {
8068 let mut f = Fixture::new();
8069 f.run(&[b"ZADD", b"z", b"5", b"a"]);
8070 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
8072 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
8073 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
8074 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
8075 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
8077 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
8078 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
8079 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
8081 assert_eq!(
8082 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
8083 ":2\r\n"
8084 );
8085 }
8086
8087 #[test]
8088 fn zadd_incr_answers_a_score_or_nothing_at_all() {
8089 let mut f = Fixture::new();
8090 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
8091 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
8092 assert_eq!(
8095 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
8096 "$-1\r\n"
8097 );
8098 assert_eq!(
8099 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
8100 "$-1\r\n"
8101 );
8102 assert_eq!(
8103 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
8104 "$-1\r\n"
8105 );
8106 assert_eq!(
8107 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
8108 "$1\r\n8\r\n"
8109 );
8110 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
8111 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
8112 }
8113
8114 #[test]
8115 fn the_two_infinities_will_not_be_added_together() {
8116 let mut f = Fixture::new();
8117 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
8118 let nan = "-ERR resulting score is not a number (NaN)\r\n";
8119 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
8120 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
8121 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
8122 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
8124 }
8125
8126 #[test]
8127 fn zadd_says_its_mistakes_the_way_redis_says_them() {
8128 let mut f = Fixture::new();
8129 assert_eq!(
8132 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
8133 "-ERR syntax error\r\n"
8134 );
8135 assert_eq!(
8136 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
8137 "-ERR XX and NX options at the same time are not compatible\r\n"
8138 );
8139 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
8140 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
8141 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
8142 assert_eq!(
8143 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
8144 "-ERR INCR option supports a single increment-element pair\r\n"
8145 );
8146 assert_eq!(
8148 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
8149 "-ERR syntax error\r\n"
8150 );
8151 assert_eq!(
8153 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
8154 "-ERR value is not a valid float\r\n"
8155 );
8156 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8157 }
8158
8159 #[test]
8160 fn a_rank_says_where_a_member_sits_from_either_end() {
8161 let mut f = Fixture::new();
8162 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8163 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
8164 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
8165 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
8166 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
8167 assert_eq!(
8169 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
8170 "*2\r\n:1\r\n$1\r\n2\r\n"
8171 );
8172 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
8173 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
8174 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
8175 assert_eq!(
8178 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
8179 "-ERR syntax error\r\n"
8180 );
8181 assert_eq!(
8182 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
8183 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
8184 );
8185 }
8186
8187 #[test]
8188 fn the_two_counts_read_their_two_kinds_of_bound() {
8189 let mut f = Fixture::new();
8190 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8191 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
8192 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
8193 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
8194 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
8195 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
8196 assert_eq!(
8197 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
8198 "-ERR min or max is not a float\r\n"
8199 );
8200
8201 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
8202 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
8203 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
8204 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
8205 assert_eq!(
8208 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
8209 "-ERR min or max not valid string range item\r\n"
8210 );
8211 }
8212
8213 #[test]
8219 fn one_range_command_selects_by_rank_or_score_or_name() {
8220 let mut f = Fixture::new();
8221 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8222 assert_eq!(
8223 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8224 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8225 );
8226 assert_eq!(
8227 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
8228 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8229 );
8230 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
8231 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
8232 assert_eq!(
8235 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
8236 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8237 );
8238 assert_eq!(
8239 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
8240 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8241 );
8242 assert_eq!(
8245 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
8246 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8247 );
8248 assert_eq!(
8249 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
8250 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8251 );
8252 assert_eq!(
8253 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
8254 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8255 );
8256 }
8257
8258 #[test]
8261 fn the_older_range_spellings_name_their_high_end_first() {
8262 let mut f = Fixture::new();
8263 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8264 assert_eq!(
8265 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
8266 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8267 );
8268 assert_eq!(
8269 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
8270 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8271 );
8272 assert_eq!(
8273 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
8274 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8275 );
8276 assert_eq!(
8277 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
8278 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8279 );
8280 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
8284 assert_eq!(
8285 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
8286 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
8287 );
8288 assert_eq!(
8289 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
8290 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
8291 );
8292 for cmd in [
8295 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
8296 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
8297 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
8298 ] {
8299 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
8300 }
8301 }
8302
8303 #[test]
8306 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
8307 let mut f = Fixture::new();
8308 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8309 assert_eq!(
8310 f.run(&[
8311 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
8312 ]),
8313 "*1\r\n$1\r\nb\r\n"
8314 );
8315 assert_eq!(
8317 f.run(&[
8318 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
8319 ]),
8320 "*0\r\n"
8321 );
8322 assert_eq!(
8323 f.run(&[
8324 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
8325 ]),
8326 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8327 );
8328 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";
8330 assert_eq!(
8331 f.run(&[
8332 b"ZRANGEBYSCORE",
8333 b"z",
8334 b"1",
8335 b"3",
8336 b"WITHSCORES",
8337 b"LIMIT",
8338 b"0",
8339 b"2"
8340 ]),
8341 both
8342 );
8343 assert_eq!(
8344 f.run(&[
8345 b"ZRANGEBYSCORE",
8346 b"z",
8347 b"1",
8348 b"3",
8349 b"LIMIT",
8350 b"0",
8351 b"2",
8352 b"WITHSCORES"
8353 ]),
8354 both
8355 );
8356 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
8359 assert_eq!(
8360 f.run(&[
8361 b"ZREVRANGE",
8362 b"z",
8363 b"0",
8364 b"-1",
8365 b"WITHSCORES",
8366 b"LIMIT",
8367 b"0",
8368 b"1"
8369 ]),
8370 needs_by
8371 );
8372 assert_eq!(
8373 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
8374 needs_by
8375 );
8376 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
8377 assert_eq!(
8378 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
8379 not_bylex
8380 );
8381 assert_eq!(
8382 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
8383 not_bylex
8384 );
8385 for cmd in [
8388 &[
8389 b"ZRANGE".as_slice(),
8390 b"z",
8391 b"0",
8392 b"-1",
8393 b"BYSCORE",
8394 b"BYLEX",
8395 ][..],
8396 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
8397 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
8398 ] {
8399 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8400 }
8401 assert_eq!(
8402 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
8403 "-ERR min or max is not a float\r\n"
8404 );
8405 assert_eq!(
8406 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
8407 "-ERR min or max not valid string range item\r\n"
8408 );
8409 assert_eq!(
8410 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8411 "-ERR value is not an integer or out of range\r\n"
8412 );
8413 }
8414
8415 #[test]
8418 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8419 let mut f = Fixture::new();
8420 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8421 assert_eq!(
8422 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8423 "*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"
8424 );
8425 f.out = Out::new(Proto::Resp3);
8426 assert_eq!(
8427 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8428 "*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"
8429 );
8430 assert_eq!(
8431 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8432 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8433 );
8434 }
8435
8436 #[test]
8438 fn a_range_store_writes_the_window_into_another_key() {
8439 let mut f = Fixture::new();
8440 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8441 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8442 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8445 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8446 assert_eq!(
8447 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8448 ":2\r\n"
8449 );
8450 assert_eq!(
8451 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8452 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8453 );
8454 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8457 assert_eq!(
8458 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8459 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8460 );
8461 assert_eq!(
8464 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8465 "-ERR syntax error\r\n"
8466 );
8467 }
8468
8469 #[test]
8472 fn the_three_removals_share_their_window_with_the_reads() {
8473 let mut f = Fixture::new();
8474 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8475 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8476 assert_eq!(
8477 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8478 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8479 );
8480 assert_eq!(
8481 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8482 ":1\r\n"
8483 );
8484 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8485 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8487 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8488 assert_eq!(
8489 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8490 ":0\r\n"
8491 );
8492 assert_eq!(
8493 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8494 "-ERR value is not an integer or out of range\r\n"
8495 );
8496 }
8497
8498 #[test]
8500 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8501 let mut f = Fixture::new();
8502 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8503 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8504 assert_eq!(
8505 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8506 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8507 );
8508 assert_eq!(
8511 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8512 "*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"
8513 );
8514 assert_eq!(
8515 f.run(&[
8516 b"ZUNION",
8517 b"2",
8518 b"z",
8519 b"y",
8520 b"WEIGHTS",
8521 b"2",
8522 b"3",
8523 b"WITHSCORES"
8524 ]),
8525 "*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"
8526 );
8527 assert_eq!(
8528 f.run(&[
8529 b"ZUNION",
8530 b"2",
8531 b"z",
8532 b"y",
8533 b"AGGREGATE",
8534 b"MIN",
8535 b"WITHSCORES"
8536 ]),
8537 "*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"
8538 );
8539 assert_eq!(
8540 f.run(&[
8541 b"ZUNION",
8542 b"2",
8543 b"z",
8544 b"y",
8545 b"AGGREGATE",
8546 b"MAX",
8547 b"WITHSCORES"
8548 ]),
8549 "*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"
8550 );
8551 assert_eq!(
8552 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8553 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8554 );
8555 assert_eq!(
8556 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8557 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8558 );
8559 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8560 f.run(&[b"SADD", b"p", b"a", b"d"]);
8563 assert_eq!(
8564 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8565 "*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"
8566 );
8567 for cmd in [
8570 &[
8571 b"ZDIFF".as_slice(),
8572 b"2",
8573 b"z",
8574 b"y",
8575 b"WEIGHTS",
8576 b"1",
8577 b"1",
8578 ][..],
8579 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8580 ] {
8581 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8582 }
8583 }
8584
8585 #[test]
8587 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8588 let mut f = Fixture::new();
8589 f.run(&[b"ZADD", b"z", b"1", b"a"]);
8590 f.run(&[b"ZADD", b"y", b"2", b"b"]);
8591 assert_eq!(
8593 f.run(&[b"ZUNION", b"0", b"z"]),
8594 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8595 );
8596 assert_eq!(
8597 f.run(&[b"ZUNION", b"-1", b"z"]),
8598 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8599 );
8600 assert_eq!(
8601 f.run(&[b"ZINTERCARD", b"0", b"z"]),
8602 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8603 );
8604 assert_eq!(
8607 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8608 "-ERR syntax error\r\n"
8609 );
8610 assert_eq!(
8611 f.run(&[b"ZUNION", b"x", b"z"]),
8612 "-ERR value is not an integer or out of range\r\n"
8613 );
8614 assert_eq!(
8617 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8618 "-ERR syntax error\r\n"
8619 );
8620 assert_eq!(
8621 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8622 "-ERR weight value is not a float\r\n"
8623 );
8624 assert_eq!(
8625 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8626 "-ERR syntax error\r\n"
8627 );
8628 }
8629
8630 #[test]
8632 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8633 let mut f = Fixture::new();
8634 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8635 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8636 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8637 assert_eq!(
8638 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8639 "*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"
8640 );
8641 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8642 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8643 assert_eq!(
8646 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8647 ":0\r\n"
8648 );
8649 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8650 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8652 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8653 for cmd in [
8654 &[
8655 b"ZUNIONSTORE".as_slice(),
8656 b"d",
8657 b"2",
8658 b"z",
8659 b"y",
8660 b"WITHSCORES",
8661 ][..],
8662 &[
8663 b"ZDIFFSTORE",
8664 b"d",
8665 b"2",
8666 b"z",
8667 b"y",
8668 b"WEIGHTS",
8669 b"1",
8670 b"1",
8671 ],
8672 ] {
8673 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8674 }
8675 }
8676
8677 #[test]
8679 fn intercard_counts_and_stops_at_its_limit() {
8680 let mut f = Fixture::new();
8681 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8682 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8683 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8684 assert_eq!(
8686 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8687 ":2\r\n"
8688 );
8689 assert_eq!(
8690 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8691 ":1\r\n"
8692 );
8693 let bad = "-ERR LIMIT can't be negative\r\n";
8696 assert_eq!(
8697 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8698 bad
8699 );
8700 assert_eq!(
8701 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8702 bad
8703 );
8704 for cmd in [
8705 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8706 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8707 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8708 ] {
8709 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8710 }
8711 }
8712
8713 #[test]
8715 fn a_draw_answers_one_member_or_an_array_of_them() {
8716 let mut f = Fixture::new();
8717 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8718 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8721 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8722 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8723 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8724 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8727 assert!(all.starts_with("*3\r\n"), "{all}");
8728 for m in ["a", "b", "c"] {
8729 assert!(all.contains(m), "{all}");
8730 }
8731 assert!(
8734 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8735 "five draws with replacement"
8736 );
8737 assert!(
8738 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8739 .starts_with("*4\r\n"),
8740 "two pairs, flat on RESP2"
8741 );
8742 f.out = Out::new(Proto::Resp3);
8743 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8744 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8745 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8746 f.out = Out::new(Proto::Resp2);
8747 assert_eq!(
8748 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8749 "-ERR syntax error\r\n"
8750 );
8751 assert_eq!(
8752 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8753 "-ERR value is not an integer or out of range\r\n"
8754 );
8755 }
8756
8757 #[test]
8759 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8760 let mut f = Fixture::new();
8761 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8762 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";
8763 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8764 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8765 assert_eq!(
8766 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8767 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8768 );
8769 assert_eq!(
8770 f.run(&[b"ZSCAN", b"nokey", b"0"]),
8771 "*2\r\n$1\r\n0\r\n*0\r\n"
8772 );
8773 f.out = Out::new(Proto::Resp3);
8776 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8777 f.out = Out::new(Proto::Resp2);
8778 assert_eq!(
8779 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8780 "-ERR NOVALUES option can only be used in HSCAN\r\n"
8781 );
8782 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8783 assert_eq!(
8784 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8785 "-ERR syntax error\r\n"
8786 );
8787 }
8788
8789 #[test]
8791 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8792 let mut f = Fixture::new();
8793 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8794 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8796 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8797 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8798 assert_eq!(
8800 f.run(&[b"ZPOPMIN", b"z", b"2"]),
8801 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8802 );
8803 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8806 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8807 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8808 assert_eq!(
8810 f.run(&[b"ZPOPMIN", b"z", b"9"]),
8811 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8812 );
8813 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8814
8815 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8816 f.out = Out::new(Proto::Resp3);
8817 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8818 assert_eq!(
8819 f.run(&[b"ZPOPMIN", b"z", b"1"]),
8820 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8821 );
8822 f.out = Out::new(Proto::Resp2);
8823 let bad = "-ERR value is out of range, must be positive\r\n";
8826 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8827 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8828 assert_eq!(
8829 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8830 "-ERR syntax error\r\n"
8831 );
8832 }
8833
8834 #[test]
8836 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8837 let mut f = Fixture::new();
8838 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8839 assert_eq!(
8840 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8841 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8842 );
8843 assert_eq!(
8846 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8847 "*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"
8848 );
8849 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8851 f.out = Out::new(Proto::Resp3);
8852 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8853 f.out = Out::new(Proto::Resp2);
8854 let numkeys = "-ERR numkeys should be greater than 0\r\n";
8855 for bad in [
8856 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8857 &[b"ZMPOP", b"-1", b"z", b"MIN"],
8858 &[b"ZMPOP", b"x", b"z", b"MIN"],
8859 ] {
8860 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8861 }
8862 let count = "-ERR count should be greater than 0\r\n";
8863 for bad in [
8864 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8865 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8866 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8867 ] {
8868 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8869 }
8870 let syntax = "-ERR syntax error\r\n";
8871 for bad in [
8872 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8875 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8876 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8877 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8878 ] {
8879 assert_eq!(f.run(bad), syntax, "{bad:?}");
8880 }
8881 }
8882
8883 #[test]
8886 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8887 let mut f = Fixture::new();
8888 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8889 assert_eq!(
8890 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8891 (
8892 Flow::Continue,
8893 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8894 )
8895 );
8896 assert_eq!(
8897 f.run(&[b"BZPOPMAX", b"z", b"0"]),
8898 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8899 );
8900 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8901 assert_eq!(
8902 f.run(&[
8903 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8904 ]),
8905 "*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"
8906 );
8907 f.out = Out::new(Proto::Resp3);
8908 assert_eq!(
8909 f.run(&[b"BZPOPMIN", b"z", b"0"]),
8910 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8911 );
8912 f.out = Out::new(Proto::Resp2);
8913 assert_eq!(
8915 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8916 (Flow::Block, String::new())
8917 );
8918 assert_eq!(
8919 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
8920 (Flow::Block, String::new())
8921 );
8922 assert_eq!(
8925 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
8926 "-ERR timeout is not a float or out of range\r\n"
8927 );
8928 assert_eq!(
8929 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
8930 "-ERR numkeys should be greater than 0\r\n"
8931 );
8932 assert_eq!(
8933 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
8934 "-ERR timeout is negative\r\n"
8935 );
8936 }
8937
8938 #[test]
8942 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
8943 let mut f = Fixture::new();
8944 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
8945 assert_eq!(f.server.parked(), 1);
8946 f.run(&[b"SET", b"z", b"v"]);
8949 let mut out = Out::new(Proto::Resp2);
8950 assert!(!f.server.serve_waiter(0, 0, &mut out));
8951 assert!(out.as_slice().is_empty());
8952 f.run(&[b"DEL", b"z"]);
8953 f.run(&[b"ZADD", b"z", b"5", b"m"]);
8954 assert!(f.server.serve_waiter(0, 0, &mut out));
8955 assert_eq!(
8956 core::str::from_utf8(out.as_slice()).expect("ascii"),
8957 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
8958 );
8959 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8962 }
8963
8964 #[test]
8965 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
8966 let mut f = Fixture::new();
8967 f.run(&[b"SET", b"s", b"v"]);
8968 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8969 for cmd in [
8970 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
8971 &[b"ZINCRBY", b"s", b"1", b"a"],
8972 &[b"ZCARD", b"s"],
8973 &[b"ZSCORE", b"s", b"a"],
8974 &[b"ZMSCORE", b"s", b"a"],
8975 &[b"ZREM", b"s", b"a"],
8976 &[b"ZRANK", b"s", b"a"],
8977 &[b"ZREVRANK", b"s", b"a"],
8978 &[b"ZCOUNT", b"s", b"1", b"2"],
8979 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
8980 &[b"ZRANGE", b"s", b"0", b"-1"],
8981 &[b"ZREVRANGE", b"s", b"0", b"-1"],
8982 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
8983 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
8984 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
8985 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
8986 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
8987 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
8988 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
8989 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
8990 &[b"ZUNION", b"1", b"s"],
8991 &[b"ZINTER", b"1", b"s"],
8992 &[b"ZDIFF", b"1", b"s"],
8993 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
8994 &[b"ZINTERSTORE", b"d", b"1", b"s"],
8995 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
8996 &[b"ZINTERCARD", b"1", b"s"],
8997 &[b"ZRANDMEMBER", b"s"],
8998 &[b"ZSCAN", b"s", b"0"],
8999 &[b"ZPOPMIN", b"s"],
9000 &[b"ZPOPMAX", b"s", b"2"],
9001 &[b"ZMPOP", b"1", b"s", b"MIN"],
9002 &[b"BZPOPMIN", b"s", b"0"],
9003 &[b"BZPOPMAX", b"s", b"0"],
9004 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
9005 ] {
9006 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
9007 }
9008 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
9009 }
9010
9011 #[test]
9015 fn churning_sorted_sets_does_not_grow_the_server() {
9016 let mut f = Fixture::new();
9017 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
9018 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
9019 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
9020 for i in 0..200 {
9021 args.push(&scores[i]);
9022 args.push(&members[i]);
9023 }
9024
9025 f.run(&args);
9026 f.run(&[b"DEL", b"z"]);
9027 f.server.compact_step();
9028 let after_first = f.server.memory_bytes();
9029
9030 for _ in 0..200 {
9031 f.run(&args);
9032 f.run(&[b"DEL", b"z"]);
9033 f.server.compact_step();
9034 }
9035 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9036 assert!(
9037 f.server.memory_bytes() <= after_first * 2,
9038 "held {} after two hundred passes against {after_first} after one",
9039 f.server.memory_bytes()
9040 );
9041 }
9042
9043 fn sicily(f: &mut Fixture) {
9051 f.run(&[
9052 b"GEOADD",
9053 b"Sicily",
9054 b"13.361389",
9055 b"38.115556",
9056 b"Palermo",
9057 b"15.087269",
9058 b"37.502669",
9059 b"Catania",
9060 ]);
9061 f.run(&[
9062 b"GEOADD",
9063 b"Sicily",
9064 b"13.583333",
9065 b"37.316667",
9066 b"Agrigento",
9067 ]);
9068 }
9069
9070 #[test]
9071 fn places_go_in_as_scores_and_come_back_as_positions() {
9072 let mut f = Fixture::new();
9073 assert_eq!(
9074 f.run(&[
9075 b"GEOADD",
9076 b"Sicily",
9077 b"13.361389",
9078 b"38.115556",
9079 b"Palermo",
9080 b"15.087269",
9081 b"37.502669",
9082 b"Catania"
9083 ]),
9084 ":2\r\n"
9085 );
9086 assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
9090 assert_eq!(
9091 f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
9092 "$16\r\n3479099956230698\r\n"
9093 );
9094 assert_eq!(
9095 f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
9096 "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
9097 );
9098 assert_eq!(
9099 f.run(&[
9100 b"GEOHASH",
9101 b"Sicily",
9102 b"Palermo",
9103 b"Catania",
9104 b"NonExisting"
9105 ]),
9106 "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
9107 );
9108 assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
9112 assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
9113 }
9114
9115 #[test]
9116 fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
9117 let mut f = Fixture::new();
9118 sicily(&mut f);
9119 assert_eq!(
9120 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
9121 "$11\r\n166274.1516\r\n"
9122 );
9123 assert_eq!(
9124 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
9125 "$8\r\n166.2742\r\n"
9126 );
9127 assert_eq!(
9128 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
9129 "$8\r\n103.3182\r\n"
9130 );
9131 assert_eq!(
9135 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
9136 "$-1\r\n"
9137 );
9138 assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
9139 assert_eq!(
9140 f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
9141 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
9142 );
9143 assert_eq!(
9144 f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
9145 "-ERR syntax error\r\n"
9146 );
9147 }
9148
9149 #[test]
9150 fn a_search_finds_what_is_inside_it_nearest_first() {
9151 let mut f = Fixture::new();
9152 sicily(&mut f);
9153 let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
9154 assert_eq!(
9155 f.run(&[
9156 b"GEOSEARCH",
9157 b"Sicily",
9158 b"FROMLONLAT",
9159 b"15",
9160 b"37",
9161 b"BYRADIUS",
9162 b"200",
9163 b"km",
9164 b"ASC"
9165 ]),
9166 all
9167 );
9168 assert_eq!(
9171 f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
9172 all
9173 );
9174 assert_eq!(
9175 f.run(&[
9176 b"GEORADIUS_RO",
9177 b"Sicily",
9178 b"15",
9179 b"37",
9180 b"200",
9181 b"km",
9182 b"ASC"
9183 ]),
9184 all
9185 );
9186 assert_eq!(
9189 f.run(&[
9190 b"GEORADIUS",
9191 b"Sicily",
9192 b"15",
9193 b"37",
9194 b"200",
9195 b"km",
9196 b"DESC",
9197 b"COUNT",
9198 b"1"
9199 ]),
9200 "*1\r\n$7\r\nPalermo\r\n"
9201 );
9202 assert_eq!(
9203 f.run(&[
9204 b"GEORADIUS",
9205 b"Sicily",
9206 b"15",
9207 b"37",
9208 b"200",
9209 b"km",
9210 b"COUNT",
9211 b"1"
9212 ]),
9213 "*1\r\n$7\r\nCatania\r\n"
9214 );
9215 let empty = "*0\r\n";
9218 assert_eq!(
9219 f.run(&[
9220 b"GEOSEARCH",
9221 b"Sicily",
9222 b"FROMLONLAT",
9223 b"15",
9224 b"37",
9225 b"BYRADIUS",
9226 b"1",
9227 b"km"
9228 ]),
9229 empty
9230 );
9231 assert_eq!(
9232 f.run(&[
9233 b"GEOSEARCH",
9234 b"nokey",
9235 b"FROMLONLAT",
9236 b"15",
9237 b"37",
9238 b"BYRADIUS",
9239 b"1",
9240 b"km"
9241 ]),
9242 empty
9243 );
9244 assert_eq!(
9245 f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
9246 empty
9247 );
9248 }
9249
9250 #[test]
9251 fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
9252 let mut f = Fixture::new();
9253 sicily(&mut f);
9254 assert_eq!(
9255 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
9256 "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9257 );
9258 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";
9261 assert_eq!(
9262 f.run(&[
9263 b"GEORADIUSBYMEMBER_RO",
9264 b"Sicily",
9265 b"Agrigento",
9266 b"100",
9267 b"km",
9268 b"WITHDIST"
9269 ]),
9270 with_dist
9271 );
9272 assert_eq!(
9273 f.run(&[
9274 b"GEOSEARCH",
9275 b"Sicily",
9276 b"FROMMEMBER",
9277 b"Agrigento",
9278 b"BYRADIUS",
9279 b"100",
9280 b"km",
9281 b"ASC",
9282 b"WITHDIST"
9283 ]),
9284 with_dist
9285 );
9286 assert_eq!(
9287 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
9288 "-ERR could not decode requested zset member\r\n"
9289 );
9290 }
9291
9292 #[test]
9293 fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
9294 let mut f = Fixture::new();
9295 sicily(&mut f);
9296 assert_eq!(
9300 f.run(&[
9301 b"GEOSEARCH",
9302 b"Sicily",
9303 b"FROMLONLAT",
9304 b"15",
9305 b"37",
9306 b"BYBOX",
9307 b"400",
9308 b"400",
9309 b"km",
9310 b"ASC",
9311 b"WITHCOORD",
9312 b"WITHDIST",
9313 b"WITHHASH"
9314 ]),
9315 "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
9316 $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
9317 *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
9318 $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
9319 *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
9320 $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
9321 );
9322 }
9323
9324 #[test]
9325 fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
9326 let mut f = Fixture::new();
9327 sicily(&mut f);
9328 let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
9329 $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
9330 $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
9331 assert_eq!(
9332 f.run(&[
9333 b"GEOSEARCHSTORE",
9334 b"dst",
9335 b"Sicily",
9336 b"FROMLONLAT",
9337 b"15",
9338 b"37",
9339 b"BYRADIUS",
9340 b"200",
9341 b"km",
9342 b"ASC"
9343 ]),
9344 ":3\r\n"
9345 );
9346 assert_eq!(
9347 f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
9348 hashes
9349 );
9350 assert_eq!(
9353 f.run(&[
9354 b"GEORADIUS",
9355 b"Sicily",
9356 b"15",
9357 b"37",
9358 b"200",
9359 b"km",
9360 b"STORE",
9361 b"dst3"
9362 ]),
9363 ":3\r\n"
9364 );
9365 assert_eq!(
9366 f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
9367 hashes
9368 );
9369 assert_eq!(
9378 f.run(&[
9379 b"GEOSEARCHSTORE",
9380 b"dst2",
9381 b"Sicily",
9382 b"FROMLONLAT",
9383 b"15",
9384 b"37",
9385 b"BYRADIUS",
9386 b"200",
9387 b"km",
9388 b"ASC",
9389 b"STOREDIST"
9390 ]),
9391 ":3\r\n"
9392 );
9393 for (member, want) in [
9394 ("Catania", 56.441_257_870_158_19),
9395 ("Agrigento", 130.423_487_067_147_14),
9396 ("Palermo", 190.442_429_847_757_92),
9397 ] {
9398 let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
9399 let got: f64 = reply
9400 .trim_start_matches(|c: char| c != '\n')
9401 .trim()
9402 .parse()
9403 .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
9404 assert!(
9405 (got - want).abs() < 1e-9,
9406 "{member} scored {got} not {want}"
9407 );
9408 }
9409 assert_eq!(
9412 f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9413 "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9414 );
9415 assert_eq!(
9419 f.run(&[
9420 b"GEOSEARCHSTORE",
9421 b"dst",
9422 b"nokey",
9423 b"FROMLONLAT",
9424 b"15",
9425 b"37",
9426 b"BYRADIUS",
9427 b"200",
9428 b"km"
9429 ]),
9430 ":0\r\n"
9431 );
9432 assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9433 }
9434
9435 #[test]
9436 fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9437 let mut f = Fixture::new();
9438 sicily(&mut f);
9439 assert_eq!(
9442 f.run(&[
9443 b"GEOADD",
9444 b"Sicily",
9445 b"XX",
9446 b"CH",
9447 b"13.361389",
9448 b"38.115556",
9449 b"Palermo"
9450 ]),
9451 ":0\r\n"
9452 );
9453 assert_eq!(
9454 f.run(&[
9455 b"GEOADD",
9456 b"Sicily",
9457 b"NX",
9458 b"13.361389",
9459 b"38.9",
9460 b"Palermo"
9461 ]),
9462 ":0\r\n"
9463 );
9464 assert_eq!(
9465 f.run(&[
9466 b"GEOADD",
9467 b"Sicily",
9468 b"CH",
9469 b"13.361389",
9470 b"38.9",
9471 b"Palermo"
9472 ]),
9473 ":1\r\n"
9474 );
9475 assert_eq!(
9478 f.run(&[
9479 b"GEOADD",
9480 b"new",
9481 b"13.361389",
9482 b"38.115556",
9483 b"here",
9484 b"181",
9485 b"38",
9486 b"there"
9487 ]),
9488 "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9489 );
9490 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9491 assert_eq!(
9492 f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9493 "-ERR value is not a valid float\r\n"
9494 );
9495 assert_eq!(
9498 f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9499 "-ERR syntax error\r\n"
9500 );
9501 assert_eq!(
9502 f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9503 "-ERR syntax error\r\n"
9504 );
9505 assert_eq!(
9506 f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9507 "-ERR syntax error\r\n"
9508 );
9509 assert_eq!(
9510 f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9511 "-ERR wrong number of arguments for 'geoadd' command\r\n"
9512 );
9513 }
9514
9515 #[test]
9518 fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9519 let mut f = Fixture::new();
9520 sicily(&mut f);
9521 let cases: &[(&[&[u8]], &str)] = &[
9522 (
9523 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9524 "-ERR need numeric radius\r\n",
9525 ),
9526 (
9527 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9528 "-ERR radius cannot be negative\r\n",
9529 ),
9530 (
9531 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9532 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9533 ),
9534 (
9535 &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9536 "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9537 ),
9538 (
9539 &[
9540 b"GEOSEARCH",
9541 b"Sicily",
9542 b"FROMLONLAT",
9543 b"15",
9544 b"37",
9545 b"BYBOX",
9546 b"x",
9547 b"1",
9548 b"km",
9549 ],
9550 "-ERR need numeric width\r\n",
9551 ),
9552 (
9553 &[
9554 b"GEOSEARCH",
9555 b"Sicily",
9556 b"FROMLONLAT",
9557 b"15",
9558 b"37",
9559 b"BYBOX",
9560 b"1",
9561 b"y",
9562 b"km",
9563 ],
9564 "-ERR need numeric height\r\n",
9565 ),
9566 (
9567 &[
9568 b"GEOSEARCH",
9569 b"Sicily",
9570 b"FROMLONLAT",
9571 b"15",
9572 b"37",
9573 b"BYBOX",
9574 b"-1",
9575 b"1",
9576 b"km",
9577 ],
9578 "-ERR height or width cannot be negative\r\n",
9579 ),
9580 (
9581 &[
9582 b"GEOSEARCH",
9583 b"Sicily",
9584 b"FROMLONLAT",
9585 b"15",
9586 b"37",
9587 b"BYRADIUS",
9588 b"1",
9589 b"km",
9590 b"ANY",
9591 ],
9592 "-ERR the ANY argument requires COUNT argument\r\n",
9593 ),
9594 (
9595 &[
9596 b"GEOSEARCH",
9597 b"Sicily",
9598 b"FROMLONLAT",
9599 b"15",
9600 b"37",
9601 b"BYRADIUS",
9602 b"1",
9603 b"km",
9604 b"COUNT",
9605 b"0",
9606 ],
9607 "-ERR COUNT must be > 0\r\n",
9608 ),
9609 (
9610 &[
9611 b"GEOSEARCH",
9612 b"Sicily",
9613 b"BYRADIUS",
9614 b"1",
9615 b"km",
9616 b"BYBOX",
9617 b"1",
9618 b"1",
9619 b"km",
9620 ],
9621 "-ERR syntax error\r\n",
9622 ),
9623 (
9624 &[
9625 b"GEOSEARCH",
9626 b"Sicily",
9627 b"FROMMEMBER",
9628 b"Palermo",
9629 b"FROMLONLAT",
9630 b"1",
9631 b"2",
9632 b"BYRADIUS",
9633 b"1",
9634 b"km",
9635 ],
9636 "-ERR syntax error\r\n",
9637 ),
9638 (
9641 &[
9642 b"geosearch",
9643 b"Sicily",
9644 b"BYRADIUS",
9645 b"1",
9646 b"km",
9647 b"ASC",
9648 b"WITHDIST",
9649 ],
9650 "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9651 ),
9652 (
9653 &[
9654 b"GEOSEARCH",
9655 b"Sicily",
9656 b"FROMLONLAT",
9657 b"15",
9658 b"37",
9659 b"ASC",
9660 b"WITHDIST",
9661 ],
9662 "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9663 ),
9664 (
9667 &[
9668 b"GEOSEARCHSTORE",
9669 b"d",
9670 b"Sicily",
9671 b"FROMLONLAT",
9672 b"15",
9673 b"37",
9674 b"BYRADIUS",
9675 b"1",
9676 b"km",
9677 b"WITHCOORD",
9678 ],
9679 "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9680 ),
9681 (
9682 &[
9683 b"GEORADIUS",
9684 b"Sicily",
9685 b"15",
9686 b"37",
9687 b"1",
9688 b"km",
9689 b"WITHDIST",
9690 b"STORE",
9691 b"d",
9692 ],
9693 "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9694 ),
9695 (
9698 &[
9699 b"GEORADIUS_RO",
9700 b"Sicily",
9701 b"15",
9702 b"37",
9703 b"1",
9704 b"km",
9705 b"STORE",
9706 b"d",
9707 ],
9708 "-ERR syntax error\r\n",
9709 ),
9710 (
9711 &[
9712 b"GEOSEARCH",
9713 b"Sicily",
9714 b"FROMLONLAT",
9715 b"15",
9716 b"37",
9717 b"BYRADIUS",
9718 b"1",
9719 b"km",
9720 b"STOREDIST",
9721 ],
9722 "-ERR syntax error\r\n",
9723 ),
9724 ];
9725 for (parts, want) in cases {
9726 assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9727 }
9728 }
9729
9730 #[test]
9733 fn every_geo_command_says_wrongtype() {
9734 let mut f = Fixture::new();
9735 f.run(&[b"SET", b"s", b"v"]);
9736 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9737 let cases: &[&[&[u8]]] = &[
9738 &[b"GEOADD", b"s", b"13", b"38", b"m"],
9739 &[b"GEOPOS", b"s", b"m"],
9740 &[b"GEOHASH", b"s", b"m"],
9741 &[b"GEODIST", b"s", b"a", b"b"],
9742 &[
9743 b"GEOSEARCH",
9744 b"s",
9745 b"FROMLONLAT",
9746 b"15",
9747 b"37",
9748 b"BYRADIUS",
9749 b"1",
9750 b"km",
9751 ],
9752 &[
9753 b"GEOSEARCHSTORE",
9754 b"d",
9755 b"s",
9756 b"FROMLONLAT",
9757 b"15",
9758 b"37",
9759 b"BYRADIUS",
9760 b"1",
9761 b"km",
9762 ],
9763 &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9764 &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9765 &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9766 &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9767 ];
9768 for case in cases {
9769 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9770 }
9771 assert_eq!(
9774 f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9775 wrong
9776 );
9777 }
9778
9779 #[test]
9782 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9783 let mut f = Fixture::new();
9784 assert_eq!(
9787 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9788 ":3\r\n"
9789 );
9790 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9791 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9792 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9793 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9795 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9796 assert_eq!(
9797 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9798 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9799 );
9800 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9802 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9803 }
9804
9805 #[test]
9808 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9809 let mut f = Fixture::new();
9810 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9811 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9812 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9813 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9814 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9815 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9817 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9818 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9819
9820 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9824 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9825 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9826 assert_eq!(
9829 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9830 "-ERR array index overflow\r\n"
9831 );
9832 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9833 }
9834
9835 #[test]
9838 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9839 let mut f = Fixture::new();
9840 f.run(&[b"ARSET", b"a", b"1", b"x"]);
9841 assert_eq!(
9842 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9843 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9844 );
9845 assert_eq!(
9848 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9849 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9850 );
9851 assert_eq!(
9853 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9854 "*2\r\n$-1\r\n$-1\r\n"
9855 );
9856 assert_eq!(
9860 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9861 "-ERR range exceeds maximum of 1000000 items\r\n"
9862 );
9863 }
9864
9865 #[test]
9868 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9869 let mut f = Fixture::new();
9870 assert_eq!(
9871 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9872 "-ERR invalid array index\r\n"
9873 );
9874 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9875 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9876 assert_eq!(
9877 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9878 "-ERR invalid array index\r\n"
9879 );
9880 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9881 assert_eq!(
9884 f.run(&[b"ARGET", b"a", b"-1"]),
9885 "-ERR invalid array index\r\n"
9886 );
9887 assert_eq!(
9890 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9891 "-ERR wrong number of arguments for 'armset' command\r\n"
9892 );
9893 assert_eq!(
9894 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9895 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9896 );
9897 }
9898
9899 #[test]
9900 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9901 let mut f = Fixture::new();
9902 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9903 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9904 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9905 assert_eq!(
9908 f.run(&[
9909 b"ARDELRANGE",
9910 b"a",
9911 b"100",
9912 b"200",
9913 b"0",
9914 b"18446744073709551614"
9915 ]),
9916 ":2\r\n"
9917 );
9918 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9919 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
9920 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
9921 }
9922
9923 #[test]
9926 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
9927 let mut f = Fixture::new();
9928 let long = vec![b'v'; 200];
9929 f.run(&[
9930 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
9931 b"short", b"5", &long, b"6", b"-0",
9932 ]);
9933 assert_eq!(
9937 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
9938 format!(
9939 "*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",
9940 String::from_utf8_lossy(&long)
9941 )
9942 );
9943 }
9944
9945 #[test]
9946 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
9947 let mut f = Fixture::new();
9948 f.run(&[b"ARSET", b"a", b"0", b"x"]);
9949 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
9950 assert_eq!(
9951 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
9952 "$12\r\nsliced-array\r\n"
9953 );
9954 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
9956 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
9957 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
9958 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
9959 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
9960 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
9961 }
9962
9963 #[test]
9964 fn every_array_command_refuses_a_key_holding_something_else() {
9965 let mut f = Fixture::new();
9966 f.run(&[b"SET", b"s", b"v"]);
9967 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9968 for cmd in [
9969 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
9970 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
9971 &[b"ARGET".as_ref(), b"s", b"0"][..],
9972 &[b"ARMGET".as_ref(), b"s", b"0"][..],
9973 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
9974 &[b"ARLEN".as_ref(), b"s"][..],
9975 &[b"ARCOUNT".as_ref(), b"s"][..],
9976 &[b"ARDEL".as_ref(), b"s", b"0"][..],
9977 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
9978 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
9979 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
9980 &[b"ARNEXT".as_ref(), b"s"][..],
9981 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
9982 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
9983 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
9984 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
9985 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
9986 &[b"ARINFO".as_ref(), b"s"][..],
9987 ] {
9988 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
9989 }
9990 }
9991
9992 #[test]
9996 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
9997 let mut f = Fixture::new();
9998 f.run(&[b"SET", b"s", b"v"]);
9999 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10000 let bad = "-ERR invalid array index\r\n";
10001 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
10002 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
10003 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
10004 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
10005 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
10006 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
10007 f.run(&[b"ARSET", b"a", b"0", b"x"]);
10009 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
10010 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
10011 }
10012
10013 #[test]
10014 fn an_append_follows_a_cursor_the_client_can_move() {
10015 let mut f = Fixture::new();
10016 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
10017 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
10018 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
10019 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
10020 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
10021
10022 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
10025 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
10026 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
10027 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
10028 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
10029 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
10030 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
10031
10032 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
10035 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
10036 assert_eq!(
10037 f.run(&[b"ARINSERT", b"a", b"x"]),
10038 "-ERR insert index overflow\r\n"
10039 );
10040 assert_eq!(
10041 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
10042 "-ERR invalid array index\r\n"
10043 );
10044 }
10045
10046 #[test]
10047 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
10048 let mut f = Fixture::new();
10049 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
10050 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
10051 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
10052 assert_eq!(
10053 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
10054 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
10055 );
10056 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
10059 assert_eq!(
10060 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
10061 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
10062 );
10063 assert_eq!(
10066 f.run(&[b"ARRING", b"r", b"0", b"x"]),
10067 "-ERR size must be positive\r\n"
10068 );
10069 assert_eq!(
10070 f.run(&[b"ARRING", b"r", b"big", b"x"]),
10071 "-ERR invalid size\r\n"
10072 );
10073 }
10074
10075 #[test]
10076 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
10077 let mut f = Fixture::new();
10078 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
10079 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
10080 assert_eq!(
10081 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
10082 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
10083 );
10084 assert_eq!(
10085 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
10086 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
10087 );
10088 assert_eq!(
10089 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
10090 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
10091 "more than there is gets what there is"
10092 );
10093 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
10096 assert_eq!(
10097 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
10098 "-ERR syntax error\r\n"
10099 );
10100 assert_eq!(
10101 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
10102 "-ERR invalid COUNT\r\n"
10103 );
10104
10105 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
10108 assert_eq!(
10109 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
10110 "*2\r\n$-1\r\n$1\r\nz\r\n"
10111 );
10112 }
10113
10114 #[test]
10115 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
10116 let mut f = Fixture::new();
10117 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
10118 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
10119 assert_eq!(
10122 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
10123 "*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"
10124 );
10125 assert_eq!(
10126 f.run(&[
10127 b"ARSCAN",
10128 b"a",
10129 b"18446744073709551614",
10130 b"0",
10131 b"LIMIT",
10132 b"1"
10133 ]),
10134 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
10135 );
10136 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
10137 assert_eq!(
10138 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
10139 "-ERR LIMIT must be positive\r\n"
10140 );
10141 assert_eq!(
10142 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
10143 "-ERR syntax error\r\n"
10144 );
10145 assert_eq!(
10146 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
10147 "-ERR wrong number of arguments for 'arscan' command\r\n"
10148 );
10149 }
10150
10151 #[test]
10152 fn a_grep_answers_the_indexes_whose_elements_match() {
10153 let mut f = Fixture::new();
10154 assert_eq!(
10155 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
10156 "*0\r\n"
10157 );
10158 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
10159
10160 assert_eq!(
10163 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
10164 "*3\r\n:0\r\n:1\r\n:2\r\n"
10165 );
10166 assert_eq!(
10167 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
10168 "*3\r\n:2\r\n:1\r\n:0\r\n"
10169 );
10170 assert_eq!(
10171 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
10172 "*2\r\n:1\r\n:2\r\n"
10173 );
10174
10175 assert_eq!(
10178 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
10179 "*1\r\n:0\r\n"
10180 );
10181 assert_eq!(
10182 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
10183 "*2\r\n:0\r\n:3\r\n"
10184 );
10185 assert_eq!(
10186 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
10187 "*1\r\n:2\r\n"
10188 );
10189 assert_eq!(
10190 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
10191 "*2\r\n:1\r\n:2\r\n"
10192 );
10193
10194 let both: &[&[u8]] = &[
10197 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
10198 ];
10199 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
10200 assert_eq!(
10201 f.run(&[
10202 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
10203 ]),
10204 "*0\r\n"
10205 );
10206 assert_eq!(
10207 f.run(&[
10208 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
10209 ]),
10210 "*2\r\n:0\r\n:1\r\n"
10211 );
10212
10213 assert_eq!(
10216 f.run(&[
10217 b"ARGREP",
10218 b"a",
10219 b"-",
10220 b"+",
10221 b"MATCH",
10222 b"a",
10223 b"WITHVALUES",
10224 b"LIMIT",
10225 b"2"
10226 ]),
10227 "*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"
10228 );
10229 assert_eq!(
10230 f.run(&[
10231 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
10232 ]),
10233 "*1\r\n:3\r\n"
10234 );
10235 }
10236
10237 #[test]
10239 fn a_grep_reports_a_broken_command_the_way_redis_does() {
10240 let mut f = Fixture::new();
10241 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
10242 let syntax = "-ERR syntax error\r\n";
10243
10244 assert_eq!(
10247 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
10248 "-ERR invalid array index\r\n"
10249 );
10250 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
10251 assert_eq!(
10253 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
10254 syntax
10255 );
10256 assert_eq!(
10257 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
10258 syntax
10259 );
10260 assert_eq!(
10261 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
10262 syntax,
10263 "a command with no predicate in it at all"
10264 );
10265 assert_eq!(
10266 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
10267 "-ERR LIMIT must be positive\r\n"
10268 );
10269 assert_eq!(
10270 f.run(&[
10271 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
10272 ]),
10273 "-ERR value is not an integer or out of range\r\n"
10274 );
10275 assert_eq!(
10276 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
10277 "-ERR regular expression is empty\r\n"
10278 );
10279 assert_eq!(
10280 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
10281 "-ERR invalid regular expression: Missing ')'\r\n"
10282 );
10283 assert_eq!(
10284 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
10285 "-ERR regular expression backreferences are not supported\r\n"
10286 );
10287 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
10290 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
10291 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
10292 }
10293
10294 #[test]
10295 fn an_op_reduces_a_range_to_one_number() {
10296 let mut f = Fixture::new();
10297 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
10298 assert_eq!(
10299 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
10300 "$4\r\n-0.5\r\n"
10301 );
10302 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
10303 assert_eq!(
10304 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
10305 "$3\r\n2.5\r\n"
10306 );
10307 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
10308 assert_eq!(
10309 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
10310 ":1\r\n"
10311 );
10312 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
10315 assert_eq!(
10316 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
10317 "$19\r\n0.30000000000000004\r\n"
10318 );
10319 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
10320 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
10321
10322 f.run(&[b"ARSET", b"w", b"0", b"word"]);
10325 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
10326 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
10327 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
10328
10329 assert_eq!(
10330 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
10331 "-ERR unknown operation\r\n"
10332 );
10333 assert_eq!(
10334 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
10335 "-ERR MATCH requires a value argument\r\n"
10336 );
10337 assert_eq!(
10338 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
10339 "-ERR wrong number of arguments for 'arop' command\r\n"
10340 );
10341 }
10342
10343 #[test]
10344 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
10345 let mut f = Fixture::new();
10346 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
10347 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
10348 let short = f.run(&[b"ARINFO", b"a"]);
10349 assert!(
10350 short.starts_with("*14\r\n"),
10351 "seven pairs on RESP2: {short}"
10352 );
10353 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
10354 assert!(
10355 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
10356 "{short}"
10357 );
10358 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
10359 let full = f.run(&[b"ARINFO", b"a", b"full"]);
10360 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
10361 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
10364 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
10365 assert!(
10366 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
10367 "{full}"
10368 );
10369 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
10370
10371 let mut g = Fixture::new();
10373 g.run(&[b"HELLO", b"3"]);
10374 g.run(&[b"ARINSERT", b"a", b"x"]);
10375 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
10376 assert!(map.starts_with("%12\r\n"), "{map}");
10377 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
10378 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
10379 }
10380
10381 #[test]
10382 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
10383 let mut f = Fixture::new();
10384 for (score, want) in [
10387 ("3", "3"),
10388 ("3.5", "3.5"),
10389 ("0.3", "0.3"),
10390 ("1e30", "1e+30"),
10391 ("1e19", "1e+19"),
10392 ("1e-7", "1e-7"),
10393 ("0.000001", "0.000001"),
10394 ("4611686018427387904", "4611686018427387904"),
10395 ("-0", "-0"),
10396 ] {
10397 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
10398 assert_eq!(
10399 f.run(&[b"ZSCORE", b"z", b"m"]),
10400 format!("${}\r\n{want}\r\n", want.len()),
10401 "score {score}"
10402 );
10403 }
10404
10405 let mut g = Fixture::new();
10408 g.run(&[b"HELLO", b"3"]);
10409 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10410 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10411 assert_eq!(
10416 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10417 "$31\r\n1000000000000000000000000000000\r\n"
10418 );
10419 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10420 assert_eq!(
10421 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10422 "$20\r\n10000000000000000000\r\n"
10423 );
10424 }
10425
10426 #[test]
10429 fn a_node_comes_back_with_the_fields_it_went_in_with() {
10430 let mut f = Fixture::new();
10431 assert_eq!(
10432 f.run(&[
10433 b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10434 ]),
10435 ":1\r\n"
10436 );
10437 assert_eq!(
10443 f.run(&[b"G.NGET", b"social", b"ada"]),
10444 "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10445 );
10446 assert_eq!(
10449 f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10450 ":0\r\n"
10451 );
10452 assert_eq!(
10453 f.run(&[b"G.NGET", b"social", b"ada"]),
10454 "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10455 );
10456 assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10459 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10460 assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10461 assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10462
10463 assert_eq!(
10466 f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10467 "-ERR syntax error\r\n"
10468 );
10469 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10470
10471 let mut g = Fixture::new();
10473 g.run(&[b"HELLO", b"3"]);
10474 g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10475 assert_eq!(
10476 g.run(&[b"G.NGET", b"social", b"ada"]),
10477 "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10478 );
10479 }
10480
10481 #[test]
10482 fn an_edge_creates_the_ends_it_needs() {
10483 let mut f = Fixture::new();
10484 assert_eq!(
10485 f.run(&[
10486 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10487 ]),
10488 ":1\r\n"
10489 );
10490 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10492 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10493 assert_eq!(
10494 f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10495 "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10496 );
10497 assert_eq!(
10498 f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10499 "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10500 );
10501 assert_eq!(
10504 f.run(&[
10505 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10506 ]),
10507 ":0\r\n"
10508 );
10509 assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10510 assert_eq!(
10512 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10513 ":1\r\n"
10514 );
10515 assert_eq!(
10516 f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10517 ":1\r\n"
10518 );
10519
10520 assert_eq!(
10521 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10522 ":1\r\n"
10523 );
10524 assert_eq!(
10525 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10526 ":0\r\n"
10527 );
10528 assert_eq!(
10531 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10532 ":0\r\n"
10533 );
10534 assert_eq!(
10535 f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10536 ":0\r\n"
10537 );
10538 assert_eq!(
10539 f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10540 ":0\r\n"
10541 );
10542 }
10543
10544 #[test]
10547 fn a_hop_answers_a_cursor_and_a_page() {
10548 let mut f = Fixture::new();
10549 for i in 0..25u32 {
10550 let dst = format!("n{i}");
10551 f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10552 }
10553 let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10555 assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10556
10557 let mut seen = 0;
10558 let mut cursor = String::from("0");
10559 loop {
10560 let page = f.run(&[
10561 b"G.OUT",
10562 b"social",
10563 b"hub",
10564 b"FOLLOWS",
10565 b"COUNT",
10566 b"7",
10567 b"CURSOR",
10568 cursor.as_bytes(),
10569 ]);
10570 let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10571 cursor = head
10572 .rsplit("\r\n")
10573 .next()
10574 .expect("the cursor line")
10575 .to_string();
10576 seen += rest
10577 .split_once("\r\n")
10578 .expect("the page length")
10579 .0
10580 .parse::<usize>()
10581 .expect("a length");
10582 if cursor == "0" {
10583 break;
10584 }
10585 }
10586 assert_eq!(seen, 25, "every neighbour once across the pages");
10587
10588 assert_eq!(
10591 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10592 "*2\r\n$1\r\n0\r\n*0\r\n"
10593 );
10594 assert_eq!(
10595 f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10596 "*2\r\n$1\r\n0\r\n*0\r\n"
10597 );
10598 assert_eq!(
10599 f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10600 "*2\r\n$1\r\n0\r\n*0\r\n"
10601 );
10602 assert_eq!(
10603 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10604 "-ERR COUNT must be a positive integer\r\n"
10605 );
10606 assert_eq!(
10607 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10608 "-ERR syntax error\r\n"
10609 );
10610 }
10611
10612 #[test]
10613 fn a_degree_counts_one_way_or_both() {
10614 let mut f = Fixture::new();
10615 f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10616 f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10617 f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10618 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10619 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10620 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10621 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10622 assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10623 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10624 assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10625 assert_eq!(
10626 f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10627 "-ERR syntax error\r\n"
10628 );
10629 }
10630
10631 #[test]
10634 fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10635 let mut f = Fixture::new();
10636 for (src, dst) in [
10637 ("ada", "grace"),
10638 ("ada", "alan"),
10639 ("grace", "edsger"),
10640 ("alan", "edsger"),
10641 ("edsger", "barbara"),
10642 ] {
10643 f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10644 }
10645 assert_eq!(
10648 f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10649 "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10650 );
10651 assert_eq!(
10652 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10653 "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10654 );
10655 let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10656 assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10657 assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10658 assert_eq!(
10660 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10661 "*1\r\n$5\r\ngrace\r\n"
10662 );
10663 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10665 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10666 assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10667 assert_eq!(
10668 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10669 "-ERR DEPTH must be a positive integer\r\n"
10670 );
10671 assert_eq!(
10672 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10673 "-ERR syntax error\r\n"
10674 );
10675 }
10676
10677 #[test]
10680 fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10681 let mut f = Fixture::new();
10682 for i in 0..6u32 {
10685 let src = format!("n{i}");
10686 let dst = format!("n{}", i + 1);
10687 f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10688 }
10689 assert_eq!(
10690 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10691 "*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"
10692 );
10693 f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10694 assert_eq!(
10695 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10696 "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10697 );
10698 assert_eq!(
10701 f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10702 "*1\r\n$2\r\nn2\r\n"
10703 );
10704 assert_eq!(
10705 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10706 "*0\r\n"
10707 );
10708 assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10710 f.run(&[b"G.NADD", b"road", b"island"]);
10713 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10714 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10715 assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10716 assert_eq!(
10717 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10718 "-ERR syntax error\r\n"
10719 );
10720 }
10721
10722 #[test]
10726 fn the_keyspace_sees_a_graph_key_like_any_other() {
10727 let mut f = Fixture::new();
10728 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10729 assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10730 assert_eq!(
10731 f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10732 "$9\r\nadjacency\r\n"
10733 );
10734 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10735 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10736 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10737 let held = f.server.memory_bytes();
10741 for i in 0..200u32 {
10742 let dst = format!("n{i}");
10743 f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10744 }
10745 assert!(
10746 f.server.memory_bytes() > held,
10747 "two hundred edges cost something: {held} then {}",
10748 f.server.memory_bytes()
10749 );
10750 f.run(&[b"DEL", b"big"]);
10751
10752 assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10755 assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10756 assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10757 assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10758 assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10759 f.run(&[b"SELECT", b"1"]);
10760 assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10761
10762 assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10763 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10764 f.run(&[b"G.NADD", b"g", b"n"]);
10765 assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10766 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10767 }
10768
10769 #[test]
10772 fn a_graph_cannot_be_copied_or_dumped() {
10773 let mut f = Fixture::new();
10774 f.run(&[b"G.NADD", b"social", b"ada"]);
10775 assert_eq!(
10776 f.run(&[b"COPY", b"social", b"other"]),
10777 "-ERR COPY is not supported for a graph\r\n"
10778 );
10779 assert_eq!(
10780 f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10781 "-ERR COPY is not supported for a graph\r\n"
10782 );
10783 assert_eq!(
10784 f.run(&[b"DUMP", b"social"]),
10785 "-ERR DUMP is not supported for a graph\r\n"
10786 );
10787 assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10789 }
10790
10791 #[test]
10794 fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10795 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10796 let mut f = Fixture::new();
10797 f.run(&[b"G.NADD", b"social", b"ada"]);
10798 assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10799 assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10800 assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10801
10802 f.run(&[b"SET", b"str", b"v"]);
10803 for cmd in [
10804 vec![b"G.NADD".as_ref(), b"str", b"n"],
10805 vec![b"G.NGET".as_ref(), b"str", b"n"],
10806 vec![b"G.NDEL".as_ref(), b"str", b"n"],
10807 vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10808 vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10809 vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10810 vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10811 vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10812 vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10813 vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10814 ] {
10815 assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10816 }
10817 }
10818
10819 #[test]
10822 fn a_graph_goes_when_its_last_node_does() {
10823 let mut f = Fixture::new();
10824 f.run(&[
10825 b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10826 ]);
10827 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10828 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10830 assert_eq!(
10831 f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10832 ":0\r\n"
10833 );
10834 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10835 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10836
10837 assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10838 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10839 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10840 assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10841
10842 f.run(&[b"G.NADD", b"social", b"first"]);
10845 f.run(&[b"G.NADD", b"social", b"second"]);
10846 f.run(&[b"G.NDEL", b"social", b"first"]);
10847 f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10848 assert_eq!(
10849 f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10850 "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10851 );
10852 }
10853
10854 #[test]
10859 fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10860 let mut f = Fixture::new();
10861 let doc = br#"{"a":1,"b":{"c":true}}"#;
10862 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10863 assert_eq!(
10866 f.run(&[b"JSON.GET", b"doc"]),
10867 bulk(r#"{"a":1,"b":{"c":true}}"#)
10868 );
10869 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10870 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10871 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10872 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10875 assert_eq!(
10876 f.run(&[b"JSON.GET", b"doc", b".nope"]),
10877 "-ERR Path does not exist\r\n"
10878 );
10879 assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10880 assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10883 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10884 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10885 assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10886 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10887 }
10888
10889 #[test]
10895 fn the_two_json_errors_that_carry_no_prefix() {
10896 let mut f = Fixture::new();
10897 f.run(&[b"SET", b"plain", b"x"]);
10898 let wrong = "-Existing key has wrong Redis type\r\n";
10899 assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10900 assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10901 assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10902 assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10903 assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10904
10905 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10906 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10910 assert_eq!(
10911 f.run(&[b"JSON.GET", b"doc"]),
10912 bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10913 );
10914 assert_eq!(
10915 f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10916 "-Err wrong static path\r\n"
10917 );
10918 }
10919
10920 #[test]
10922 fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10923 let mut f = Fixture::new();
10924 assert_eq!(
10926 f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
10927 "-ERR new objects must be created at the root\r\n"
10928 );
10929 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
10930 assert_eq!(
10933 f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
10934 "-ERR new objects must be created at the root\r\n"
10935 );
10936 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
10937 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
10938
10939 f.run(&[
10940 b"JSON.SET",
10941 b"doc",
10942 b"$",
10943 br#"{"o":{},"arr":[1,2],"s":"x"}"#,
10944 ]);
10945 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10947 assert_eq!(
10950 f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
10951 "$-1\r\n"
10952 );
10953 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
10954 assert_eq!(
10956 f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
10957 "-ERR array index out of range\r\n"
10958 );
10959 assert_eq!(
10960 f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
10961 "-ERR array index out of range\r\n"
10962 );
10963 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
10964 assert_eq!(
10967 f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
10968 "$-1\r\n"
10969 );
10970 assert_eq!(
10971 f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
10972 "$-1\r\n"
10973 );
10974 assert_eq!(
10975 f.run(&[b"JSON.GET", b"doc"]),
10976 bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
10977 );
10978 assert!(
10982 f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
10983 .starts_with("-this is not the start of a value")
10984 );
10985 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
10986 }
10987
10988 #[test]
10991 fn the_json_commands_that_do_not_answer_text() {
10992 let mut f = Fixture::new();
10993 let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
10994 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10995
10996 assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
10997 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
10998 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
10999 assert_eq!(
11000 f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
11001 format!("*1\r\n{}", bulk("integer"))
11002 );
11003 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
11006 assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
11007
11008 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
11011 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
11012 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
11015 assert_eq!(
11016 f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
11017 "-ERR Path does not exist or not a bool\r\n"
11018 );
11019 assert_eq!(
11020 f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
11021 "-ERR Path does not exist or not a bool\r\n"
11022 );
11023 assert_eq!(
11024 f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
11025 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11026 );
11027
11028 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
11031 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
11032 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
11033 assert_eq!(
11034 f.run(&[b"JSON.GET", b"doc"]),
11035 bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
11036 );
11037
11038 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
11041 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
11042 assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
11047 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
11048 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
11049 assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
11050 assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
11051 assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
11052 assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
11053 assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
11054 }
11055
11056 #[test]
11063 fn json_get_lays_out_the_wrapper_it_builds() {
11064 let mut f = Fixture::new();
11065 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
11066
11067 assert_eq!(
11068 f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
11069 bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
11070 );
11071 assert_eq!(
11073 f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
11074 bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
11075 );
11076 let fmt: &[&[u8]] = &[b"INDENT", b" ", b"NEWLINE", b"\n", b"SPACE", b" "];
11077 let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
11078 one.extend_from_slice(fmt);
11079 one.push(b"$.b");
11080 assert_eq!(
11081 f.run(&one),
11082 bulk("[\n [\n 1,\n {\n \"c\": 2\n }\n ]\n]")
11083 );
11084 let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
11085 two.extend_from_slice(fmt);
11086 two.push(b"$.a");
11087 two.push(b"$.nope");
11088 assert_eq!(
11089 f.run(&two),
11090 bulk("{\n \"$.a\": [\n 1\n ],\n \"$.nope\": []\n}")
11091 );
11092 let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
11095 root.push(b".a");
11096 assert_eq!(f.run(&root), bulk("1"));
11097 }
11098
11099 #[test]
11102 fn json_mget_answers_once_per_key_whatever_is_under_them() {
11103 let mut f = Fixture::new();
11104 f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
11105 f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
11106 f.run(&[b"SET", b"plain", b"x"]);
11107 assert_eq!(
11108 f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
11109 format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
11110 );
11111 assert_eq!(
11114 f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
11115 format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
11116 );
11117 assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
11120 }
11121
11122 #[test]
11130 fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
11131 let mut f = Fixture::new();
11132 let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
11133 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11134
11135 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
11136 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
11137 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
11138 assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
11139 assert_eq!(
11140 f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
11141 format!("*2\r\n{}{}", bulk("x"), bulk("y"))
11142 );
11143 assert_eq!(
11146 f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
11147 "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
11148 );
11149
11150 assert_eq!(
11153 f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
11154 "-ERR Path does not exist\r\n"
11155 );
11156 assert_eq!(
11157 f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
11158 "-ERR Path does not exist\r\n"
11159 );
11160 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
11161 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
11164 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
11167
11168 assert_eq!(
11172 f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
11173 "-ERR Path does not exist or not an array\r\n"
11174 );
11175 assert_eq!(
11176 f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
11177 "-ERR Path does not exist or not an object\r\n"
11178 );
11179 assert_eq!(
11180 f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
11181 "-WRONGTYPE wrong type of path value - expected object\r\n"
11182 );
11183 assert_eq!(
11184 f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
11185 "-WRONGTYPE wrong type of path value - expected string\r\n"
11186 );
11187
11188 assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
11191 assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
11192 assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
11193 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
11194 assert_eq!(
11195 f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
11196 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11197 );
11198 assert_eq!(
11200 f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
11201 "-ERR Path does not exist or not an object\r\n"
11202 );
11203 }
11204
11205 #[test]
11211 fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
11212 let mut f = Fixture::new();
11213 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
11214
11215 assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
11216 assert_eq!(
11217 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
11218 "*1\r\n:6\r\n"
11219 );
11220 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
11221
11222 assert_eq!(
11225 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
11226 ":7\r\n"
11227 );
11228 assert_eq!(
11229 f.run(&[b"JSON.GET", b"doc", b".a"]),
11230 bulk("[1,2,3,4,5,0,6]")
11231 );
11232 assert_eq!(
11233 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
11234 ":8\r\n"
11235 );
11236 assert_eq!(
11238 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
11239 "-ERR index out of bounds\r\n"
11240 );
11241 assert_eq!(
11242 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
11243 "-ERR index out of bounds\r\n"
11244 );
11245
11246 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
11249 assert_eq!(
11250 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
11251 ":3\r\n"
11252 );
11253 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
11254 assert_eq!(
11255 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
11256 ":2\r\n"
11257 );
11258 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
11259 assert_eq!(
11260 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
11261 ":0\r\n"
11262 );
11263 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11264
11265 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
11268 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
11269 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
11270 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
11271 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
11272
11273 for call in [
11276 &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
11277 &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
11278 &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
11279 &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
11280 ] {
11281 for path in [&b".n"[..], &b".nope"[..]] {
11282 let args: Vec<&[u8]> = call
11283 .iter()
11284 .map(|a| if *a == b"PATH" { path } else { *a })
11285 .collect();
11286 assert_eq!(
11287 f.run(&args),
11288 "-ERR Path does not exist or not an array\r\n",
11289 "{} {}",
11290 String::from_utf8_lossy(call[0]),
11291 String::from_utf8_lossy(path)
11292 );
11293 }
11294 }
11295
11296 assert_eq!(
11299 f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
11300 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11301 );
11302 assert_eq!(
11303 f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
11304 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11305 );
11306
11307 assert!(
11312 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
11313 .starts_with("-this is not the start of a value")
11314 );
11315 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11316 }
11317
11318 #[test]
11326 fn json_arrinsert_is_all_or_nothing_across_the_matches() {
11327 let mut f = Fixture::new();
11328 let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
11329 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11330 assert_eq!(
11331 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
11332 "-ERR index out of bounds\r\n"
11333 );
11334 assert_eq!(
11335 f.run(&[b"JSON.GET", b"doc"]),
11336 bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
11337 );
11338 assert_eq!(
11340 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
11341 "*3\r\n:4\r\n:3\r\n:2\r\n"
11342 );
11343 assert_eq!(
11344 f.run(&[b"JSON.GET", b"doc"]),
11345 bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
11346 );
11347 }
11348
11349 #[test]
11357 fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
11358 let mut f = Fixture::new();
11359 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
11360
11361 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
11362 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
11363 assert_eq!(
11364 f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
11365 "*1\r\n:1\r\n"
11366 );
11367
11368 assert_eq!(
11371 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
11372 ":3\r\n"
11373 );
11374 assert_eq!(
11377 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
11378 ":-1\r\n"
11379 );
11380
11381 for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
11384 assert_eq!(
11385 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
11386 ":3\r\n",
11387 "{}",
11388 String::from_utf8_lossy(start)
11389 );
11390 }
11391 assert_eq!(
11392 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
11393 ":0\r\n"
11394 );
11395 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
11398 assert_eq!(
11399 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
11400 ":-1\r\n"
11401 );
11402
11403 f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
11407 assert_eq!(
11408 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11409 ":0\r\n"
11410 );
11411 assert_eq!(
11412 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11413 ":1\r\n"
11414 );
11415 assert_eq!(
11416 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11417 ":-1\r\n"
11418 );
11419
11420 assert_eq!(
11424 f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11425 "-ERR Path does not exist\r\n"
11426 );
11427 assert_eq!(
11428 f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11429 "-WRONGTYPE wrong type of path value - expected array\r\n"
11430 );
11431 assert_eq!(
11432 f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11433 "-ERR Path does not exist\r\n"
11434 );
11435 assert_eq!(
11436 f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11437 "-ERR Path does not exist\r\n"
11438 );
11439 }
11440
11441 #[test]
11444 fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11445 let mut f = Fixture::new();
11446 let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11447 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11448
11449 assert_eq!(
11452 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11453 bulk("9").as_str()
11454 );
11455 assert_eq!(
11457 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11458 bulk("[11]").as_str()
11459 );
11460 assert_eq!(
11463 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11464 bulk("13.0").as_str()
11465 );
11466 assert_eq!(
11467 f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11468 bulk("number").as_str()
11469 );
11470 assert_eq!(
11471 f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11472 bulk("3.0").as_str()
11473 );
11474 assert_eq!(
11475 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11476 bulk("-8").as_str()
11477 );
11478 f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11481 assert_eq!(
11482 f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11483 bulk("1.224744871391589").as_str()
11484 );
11485 assert_eq!(
11486 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11487 "-ERR result is not a number\r\n"
11488 );
11489 f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11493 assert_eq!(
11494 f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11495 "-ERR numeric overflow\r\n"
11496 );
11497 f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11498 assert_eq!(
11499 f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11500 "-ERR numeric overflow\r\n"
11501 );
11502 f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11504 assert_eq!(
11505 f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11506 "-ERR result is not a number\r\n"
11507 );
11508
11509 assert_eq!(
11513 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11514 bulk("[null]").as_str()
11515 );
11516 assert_eq!(
11517 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11518 bulk("[]").as_str()
11519 );
11520 assert_eq!(
11521 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11522 "-ERR Path does not exist or does not contains a number\r\n"
11523 );
11524 assert_eq!(
11525 f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11526 "-ERR Path does not exist or does not contains a number\r\n"
11527 );
11528 assert_eq!(
11531 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11532 "-bad input number\r\n"
11533 );
11534 assert_eq!(
11535 f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11536 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11537 );
11538 assert_eq!(
11539 f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11540 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11541 );
11542 }
11543
11544 #[test]
11547 fn json_strappend_reads_its_shape_off_the_argument_count() {
11548 let mut f = Fixture::new();
11549 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11550
11551 assert_eq!(
11552 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11553 ":3\r\n"
11554 );
11555 assert_eq!(
11556 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11557 "*1\r\n:4\r\n"
11558 );
11559 assert_eq!(
11562 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11563 ":6\r\n"
11564 );
11565 f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11568 assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11569 assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11570
11571 assert_eq!(
11575 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11576 "-WRONGTYPE wrong type of path value - expected string\r\n"
11577 );
11578 assert_eq!(
11579 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11580 "*1\r\n$-1\r\n"
11581 );
11582 assert_eq!(
11583 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11584 "-ERR Path does not exist or not a string\r\n"
11585 );
11586 assert_eq!(
11587 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11588 "*0\r\n"
11589 );
11590 assert_eq!(
11591 f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11592 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11593 );
11594 }
11595
11596 #[test]
11599 fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11600 let mut f = Fixture::new();
11601 let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11604
11605 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11606 assert_eq!(
11607 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11608 ":4\r\n"
11609 );
11610 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11611 assert_eq!(
11612 f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11613 ":2\r\n"
11614 );
11615 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11616 assert_eq!(
11617 f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11618 ":1\r\n"
11619 );
11620 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11621 assert_eq!(
11622 f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11623 bulk("1").as_str()
11624 );
11625 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11626 assert_eq!(
11627 f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11628 bulk("13").as_str()
11629 );
11630 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11631 assert_eq!(
11632 f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11633 ":4\r\n"
11634 );
11635 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11636 assert_eq!(
11637 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11638 bulk("false").as_str()
11639 );
11640 assert_eq!(
11643 f.run(&[b"JSON.GET", b"doc", b".a"]),
11644 bulk("[false,true,false]").as_str()
11645 );
11646
11647 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11650 assert_eq!(
11651 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11652 ":3\r\n"
11653 );
11654 assert_eq!(
11655 f.run(&[b"JSON.GET", b"doc", b".a"]),
11656 bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11657 );
11658 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11661 assert_eq!(
11662 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11663 "-ERR Path does not exist or not an array\r\n"
11664 );
11665 assert_eq!(
11666 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11667 "-ERR Path does not exist or not a bool\r\n"
11668 );
11669 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11673 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11674 assert_eq!(
11675 f.run(&[b"JSON.GET", b"doc", b".a"]),
11676 bulk("[[],[2]]").as_str()
11677 );
11678 }
11679
11680 #[test]
11683 fn a_write_reaches_a_match_that_sits_inside_another_match() {
11684 let mut f = Fixture::new();
11685 let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11686
11687 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11688 assert_eq!(
11689 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11690 "*3\r\n:3\r\n:2\r\n:3\r\n"
11691 );
11692 assert_eq!(
11693 f.run(&[b"JSON.GET", b"doc", b"$"]),
11694 bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11695 );
11696
11697 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11700 assert_eq!(
11701 f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11702 "*3\r\n:1\r\n:1\r\n:1\r\n"
11703 );
11704 assert_eq!(
11705 f.run(&[b"JSON.GET", b"doc", b"$"]),
11706 bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11707 );
11708
11709 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11712 assert_eq!(
11713 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11714 bulk("[null,8,8]").as_str()
11715 );
11716 }
11717
11718 #[test]
11721 fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11722 let mut f = Fixture::new();
11723 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11724
11725 assert_eq!(
11728 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11729 bulk("[null]").as_str()
11730 );
11731 assert_eq!(
11732 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11733 bulk("[null]").as_str()
11734 );
11735 assert_eq!(
11736 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11737 bulk("[]").as_str()
11738 );
11739 assert_eq!(
11740 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11741 "-ERR Path does not exist or does not contains a number\r\n"
11742 );
11743 assert_eq!(
11745 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11746 "-bad input number\r\n"
11747 );
11748
11749 assert_eq!(
11751 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11752 "*1\r\n$-1\r\n"
11753 );
11754 assert_eq!(
11755 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11756 "-ERR Path does not exist or not a string\r\n"
11757 );
11758 assert_eq!(
11759 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11760 "-WRONGTYPE wrong type of path value - expected string\r\n"
11761 );
11762
11763 assert_eq!(
11765 f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11766 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11767 );
11768 assert_eq!(
11769 f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11770 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11771 );
11772 }
11773
11774 #[test]
11777 fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11778 let mut f = Fixture::new();
11779
11780 assert_eq!(
11784 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11785 "+OK\r\n"
11786 );
11787 assert_eq!(
11788 f.run(&[b"JSON.GET", b"doc", b"$"]),
11789 bulk(r#"[{"x":null,"y":1}]"#).as_str()
11790 );
11791
11792 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11795 assert_eq!(
11796 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11797 "+OK\r\n"
11798 );
11799 assert_eq!(
11800 f.run(&[b"JSON.GET", b"doc", b"$"]),
11801 bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11802 );
11803
11804 assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11806 assert_eq!(
11807 f.run(&[b"JSON.GET", b"doc", b"$"]),
11808 bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11809 );
11810
11811 assert_eq!(
11815 f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11816 "+OK\r\n"
11817 );
11818 assert_eq!(
11819 f.run(&[b"JSON.GET", b"doc", b"$"]),
11820 bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11821 );
11822
11823 assert_eq!(
11827 f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11828 "+OK\r\n"
11829 );
11830 assert_eq!(
11831 f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11832 bulk(r#"[{"z":null}]"#).as_str()
11833 );
11834 assert_eq!(
11835 f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11836 "$-1\r\n"
11837 );
11838 assert_eq!(
11839 f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11840 "-Err wrong static path\r\n"
11841 );
11842
11843 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11845 assert_eq!(
11846 f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11847 "+OK\r\n"
11848 );
11849 assert_eq!(
11850 f.run(&[b"JSON.GET", b"doc", b"$"]),
11851 bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11852 );
11853
11854 assert_eq!(
11856 f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11857 "-ERR syntax error\r\n"
11858 );
11859 assert_eq!(
11860 f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11861 "-ERR new objects must be created at the root\r\n"
11862 );
11863 f.run(&[b"SET", b"str", b"x"]);
11864 assert_eq!(
11865 f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11866 "-Existing key has wrong Redis type\r\n"
11867 );
11868 }
11869
11870 #[test]
11873 fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11874 let mut f = Fixture::new();
11875 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11876 assert_eq!(
11877 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11878 "+OK\r\n"
11879 );
11880 assert_eq!(
11884 f.run(&[b"JSON.GET", b"doc", b"$"]),
11885 bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11886 );
11887
11888 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11891 assert_eq!(
11892 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11893 "+OK\r\n"
11894 );
11895 assert_eq!(
11896 f.run(&[b"JSON.GET", b"doc", b"$"]),
11897 bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11898 );
11899 }
11900
11901 #[test]
11904 fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11905 let mut f = Fixture::new();
11906 let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11907 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11908
11909 assert_eq!(
11910 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11911 bulk(r#"["a","c"]"#).as_str()
11912 );
11913 assert_eq!(
11916 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11917 bulk(r#"["a","c"]"#).as_str()
11918 );
11919 assert_eq!(
11921 f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
11922 bulk(r#""a""#).as_str()
11923 );
11924 assert_eq!(
11925 f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
11926 "*1\r\n$6\r\nobject\r\n"
11927 );
11928
11929 assert_eq!(
11933 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
11934 bulk("[9,10]").as_str()
11935 );
11936 assert_eq!(
11937 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
11938 "+OK\r\n"
11939 );
11940 assert_eq!(
11941 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
11942 "-Err wrong static path\r\n"
11943 );
11944 assert_eq!(
11945 f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
11946 ":2\r\n"
11947 );
11948 assert_eq!(
11949 f.run(&[b"JSON.GET", b"doc", b"$"]),
11950 bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
11951 );
11952
11953 assert!(
11956 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
11957 .starts_with("-ERR")
11958 );
11959 assert!(
11960 f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
11961 .starts_with("-ERR")
11962 );
11963 }
11964
11965 #[test]
11968 fn a_filter_takes_the_membership_operators_and_the_methods_too() {
11969 let mut f = Fixture::new();
11970 let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
11971 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11972
11973 for (path, want) in [
11974 (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
11975 (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
11976 (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
11977 (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
11978 (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
11979 (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
11980 (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
11981 (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
11982 (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
11983 (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
11984 (b"$.box[?(@.n~)].t", "[]"),
11985 (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
11986 (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
11987 (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
11988 (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
11989 ] {
11990 assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
11991 }
11992
11993 assert_eq!(
11996 f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
11997 "+OK\r\n"
11998 );
11999 assert_eq!(
12000 f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
12001 bulk(r#"["b"]"#).as_str()
12002 );
12003 }
12004
12005 #[test]
12008 fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
12009 let mut f = Fixture::new();
12010 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
12011 assert_eq!(
12012 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12013 "+OK\r\n"
12014 );
12015 assert_eq!(
12016 f.run(&[b"JSON.GET", b"doc", b"$"]),
12017 bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
12018 );
12019 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
12022 assert_eq!(
12023 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12024 "+OK\r\n"
12025 );
12026 assert_eq!(
12027 f.run(&[b"JSON.GET", b"doc", b"$"]),
12028 bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
12029 );
12030 }
12031
12032 #[test]
12035 fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
12036 let mut f = Fixture::new();
12037 assert_eq!(
12038 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
12039 "+OK\r\n"
12040 );
12041 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
12042 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
12043
12044 assert_eq!(
12046 f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
12047 "+OK\r\n"
12048 );
12049 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
12050
12051 f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
12056 assert_eq!(
12057 f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
12058 "$-1\r\n"
12059 );
12060 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
12061 assert_eq!(
12062 f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
12063 "$-1\r\n"
12064 );
12065 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12066
12067 f.run(&[b"SET", b"str", b"x"]);
12072 assert_eq!(
12073 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
12074 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
12075 );
12076 assert_eq!(
12077 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
12078 "-Existing key has wrong Redis type\r\n"
12079 );
12080 assert_eq!(
12081 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
12082 "-ERR new objects must be created at the root\r\n"
12083 );
12084 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
12085
12086 assert_eq!(
12091 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
12092 "-Err wrong static path\r\n"
12093 );
12094 assert_eq!(
12095 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
12096 "-ERR array index out of range\r\n"
12097 );
12098 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12099
12100 f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
12104 assert_eq!(
12105 f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
12106 "+OK\r\n"
12107 );
12108 assert_eq!(
12109 f.run(&[b"JSON.GET", b"c", b"$"]),
12110 bulk(r#"[{"n":3}]"#).as_str()
12111 );
12112
12113 assert_eq!(
12116 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
12117 "-ERR wrong number of arguments for 'json.mset' command\r\n"
12118 );
12119 }
12120
12121 #[test]
12124 fn json_resp_answers_the_document_as_resp_types() {
12125 let mut f = Fixture::new();
12126 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
12127 assert_eq!(
12128 f.run(&[b"JSON.RESP", b"doc"]),
12129 "*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"
12130 );
12131 assert_eq!(
12133 f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
12134 "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
12135 );
12136
12137 f.run(&[
12138 b"JSON.SET",
12139 b"doc",
12140 b"$",
12141 br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
12142 ]);
12143 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
12144 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
12145 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
12148 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
12149 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
12150
12151 assert_eq!(
12154 f.run(&[b"JSON.RESP", b"doc", b".nope"]),
12155 "-ERR Path does not exist\r\n"
12156 );
12157 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
12158 assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
12159 assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
12160 }
12161
12162 #[test]
12166 fn json_debug_answers_a_byte_count_and_its_own_help() {
12167 let mut f = Fixture::new();
12168 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
12169 let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
12170 assert!(one.starts_with(':'), "{one}");
12171 assert_eq!(
12172 f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
12173 format!("*1\r\n{one}")
12174 );
12175 let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
12176 assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
12177
12178 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
12182 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
12183 assert_eq!(
12184 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
12185 "-ERR Path does not exist\r\n"
12186 );
12187 assert_eq!(
12188 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
12189 "*0\r\n"
12190 );
12191
12192 assert_eq!(
12193 f.run(&[b"JSON.DEBUG", b"HELP"]),
12194 "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
12195 $34\r\nHELP - this message\r\n"
12196 );
12197 assert_eq!(
12198 f.run(&[b"JSON.DEBUG", b"NOPE"]),
12199 "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
12200 );
12201 assert_eq!(
12202 f.run(&[b"JSON.DEBUG", b"MEMORY"]),
12203 "-ERR wrong number of arguments for 'json.debug' command\r\n"
12204 );
12205 }
12206
12207 #[test]
12212 fn the_first_vadd_decides_how_wide_the_set_is() {
12213 let mut f = Fixture::new();
12214 assert_eq!(
12215 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
12216 ":1\r\n"
12217 );
12218 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12219 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12220 assert_eq!(
12223 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
12224 ":0\r\n"
12225 );
12226 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12227 assert_eq!(
12230 f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
12231 "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
12232 );
12233 assert_eq!(
12236 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
12237 ":1\r\n"
12238 );
12239 assert_eq!(
12240 f.run(&[b"VEMB", b"v", b"nowhere"]),
12241 "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
12242 );
12243 assert_eq!(
12247 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
12248 "-ERR asked quantization mismatch with existing vector set\r\n"
12249 );
12250 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
12253 assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
12254 assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
12255 }
12256
12257 #[test]
12260 fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
12261 let mut f = Fixture::new();
12262 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
12263 assert_eq!(
12269 f.run(&[b"VEMB", b"v", b"a"]),
12270 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12271 );
12272 f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
12274 assert_eq!(
12275 f.run(&[b"VEMB", b"n", b"a"]),
12276 "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
12277 );
12278 f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
12281 assert_eq!(
12282 f.run(&[b"VEMB", b"b", b"a"]),
12283 "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
12284 );
12285 assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
12286 assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
12287
12288 let mut f = Fixture::new();
12292 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
12293 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
12294 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
12295 assert_eq!(
12296 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
12297 "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
12298 $8\r\nopposite\r\n$1\r\n0\r\n"
12299 );
12300 assert_eq!(
12303 f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
12304 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12305 );
12306 assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
12309 assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
12310 assert_eq!(
12313 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
12314 "*1\r\n$6\r\nacross\r\n"
12315 );
12316 assert_eq!(
12317 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
12318 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12319 );
12320 assert_eq!(
12324 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
12325 "*1\r\n$6\r\nacross\r\n"
12326 );
12327
12328 let mut g = Fixture::new();
12331 g.run(&[b"HELLO", b"3"]);
12332 g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12333 assert_eq!(
12334 g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
12335 "%1\r\n$4\r\neast\r\n,1\r\n"
12336 );
12337 }
12338
12339 #[test]
12341 fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
12342 let mut f = Fixture::new();
12343 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12344 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12345 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
12346 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
12347 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
12350 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
12351 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
12353 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12354 assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
12357 assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
12358 assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
12359 assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
12362 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
12363 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
12364 assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
12365
12366 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12368 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12369 assert_eq!(
12370 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
12371 "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
12372 );
12373 }
12374
12375 #[test]
12378 fn vrem_takes_the_attribute_with_it() {
12379 let mut f = Fixture::new();
12380 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12381 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12382 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
12383 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
12384 assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
12385 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12388
12389 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12392 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12393 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12394 f.run(&[b"VREM", b"v", b"east"]);
12395 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
12396 assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
12397 }
12398
12399 #[test]
12402 fn vinfo_says_partition_first() {
12403 let mut f = Fixture::new();
12404 f.run(&[
12405 b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
12406 ]);
12407 f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
12408 let info = f.run(&[b"VINFO", b"v"]);
12409 assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12410 assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12413 assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12414 assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12415 assert!(
12418 info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12419 "{info}"
12420 );
12421 let mut f = Fixture::new();
12422 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12423 assert!(
12424 f.run(&[b"VINFO", b"v"])
12425 .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12426 );
12427 assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12428 }
12429
12430 fn named() -> Fixture {
12432 let mut f = Fixture::new();
12433 for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12434 .iter()
12435 .enumerate()
12436 {
12437 let x = (i + 1).to_string();
12438 f.run(&[
12439 b"VADD",
12440 b"r",
12441 b"VALUES",
12442 b"2",
12443 x.as_bytes(),
12444 b"1",
12445 name.as_bytes(),
12446 ]);
12447 }
12448 f
12449 }
12450
12451 #[test]
12454 fn vrange_walks_the_names_and_not_the_vectors() {
12455 let mut f = named();
12456 assert_eq!(
12457 f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12458 "*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"
12459 );
12460 assert_eq!(
12461 f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12462 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12463 "the high end is a name and not a prefix, so delta is past it"
12464 );
12465 assert_eq!(
12466 f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12467 "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12468 );
12469 assert_eq!(
12470 f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12471 "*1\r\n$4\r\nbeta\r\n"
12472 );
12473 assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12474 f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12477 assert_eq!(
12478 f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12479 "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12480 );
12481 assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12482 }
12483
12484 #[test]
12487 fn a_vrange_count_of_zero_asks_for_nothing() {
12488 let mut f = named();
12489 assert_eq!(
12490 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12491 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12492 );
12493 assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12494 assert!(
12495 f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12496 .starts_with("*5\r\n"),
12497 "a negative count is no limit at all"
12498 );
12499 }
12500
12501 #[test]
12504 fn vrange_says_which_end_it_could_not_read() {
12505 let mut f = named();
12506 assert_eq!(
12507 f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12508 "-ERR invalid start range format\r\n"
12509 );
12510 assert_eq!(
12511 f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12512 "-ERR invalid end range format\r\n",
12513 "the high end is spelled wrong, which is worth saying before the \
12514 low end being on the wrong side"
12515 );
12516 assert_eq!(
12517 f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12518 "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12519 );
12520 assert_eq!(
12523 f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12524 "-ERR invalid start range format\r\n"
12525 );
12526 assert_eq!(
12527 f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12528 "-ERR invalid COUNT value\r\n"
12529 );
12530 assert_eq!(
12531 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12532 "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12533 );
12534 f.run(&[b"SET", b"s", b"x"]);
12535 assert!(
12536 f.run(&[b"VRANGE", b"s", b"-", b"+"])
12537 .starts_with("-WRONGTYPE")
12538 );
12539 }
12540
12541 #[test]
12544 fn reduce_is_refused_and_not_ignored() {
12545 let mut f = Fixture::new();
12546 let reduce = f.run(&[
12547 b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12548 ]);
12549 assert!(
12550 reduce.starts_with("-ERR REDUCE is not supported."),
12551 "{reduce}"
12552 );
12553 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12554 }
12555
12556 #[test]
12559 fn vsim_filter_reads_the_attributes() {
12560 let mut f = Fixture::new();
12561 for (name, x, y, attr) in [
12562 ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12563 ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12564 ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12565 ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12566 ] {
12567 f.run(&[
12568 b"VADD",
12569 b"v",
12570 b"VALUES",
12571 b"2",
12572 x.as_bytes(),
12573 y.as_bytes(),
12574 name.as_bytes(),
12575 b"SETATTR",
12576 attr.as_bytes(),
12577 ]);
12578 }
12579 assert_eq!(
12582 f.run(&[
12583 b"VSIM",
12584 b"v",
12585 b"VALUES",
12586 b"2",
12587 b"9",
12588 b"1",
12589 b"COUNT",
12590 b"2",
12591 b"FILTER",
12592 b".lang == \"en\"",
12593 ]),
12594 "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12595 );
12596 assert_eq!(
12599 f.run(&[
12600 b"VSIM",
12601 b"v",
12602 b"VALUES",
12603 b"2",
12604 b"9",
12605 b"1",
12606 b"FILTER",
12607 b".lang == 'en' and .year > 1980",
12608 ]),
12609 "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12610 );
12611 assert_eq!(
12613 f.run(&[
12614 b"VSIM",
12615 b"v",
12616 b"VALUES",
12617 b"2",
12618 b"9",
12619 b"1",
12620 b"FILTER",
12621 b".lang in ['fr', 'de']",
12622 ]),
12623 "*1\r\n$1\r\nb\r\n"
12624 );
12625 assert_eq!(
12626 f.run(&[
12627 b"VSIM",
12628 b"v",
12629 b"VALUES",
12630 b"2",
12631 b"9",
12632 b"1",
12633 b"FILTER",
12634 b".rating > 3"
12635 ]),
12636 "*0\r\n"
12637 );
12638 assert_eq!(
12641 f.run(&[
12642 b"VSIM",
12643 b"v",
12644 b"VALUES",
12645 b"2",
12646 b"9",
12647 b"1",
12648 b"TRUTH",
12649 b"FILTER",
12650 b".year < 1980",
12651 ]),
12652 "*1\r\n$1\r\nc\r\n"
12653 );
12654 f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12657 assert_eq!(
12658 f.run(&[
12659 b"VSIM",
12660 b"v",
12661 b"VALUES",
12662 b"2",
12663 b"9",
12664 b"1",
12665 b"COUNT",
12666 b"1",
12667 b"FILTER",
12668 b".lang == \"en\"",
12669 ]),
12670 "*1\r\n$1\r\nb\r\n"
12671 );
12672 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12675 assert_eq!(
12676 f.run(&[
12677 b"VSIM",
12678 b"v",
12679 b"VALUES",
12680 b"2",
12681 b"9",
12682 b"1",
12683 b"COUNT",
12684 b"1",
12685 b"FILTER",
12686 b".lang == \"en\"",
12687 ]),
12688 "*1\r\n$1\r\nb\r\n"
12689 );
12690
12691 let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12694 assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12695 assert_eq!(
12696 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12697 "-ERR invalid FILTER expression\r\n"
12698 );
12699 assert_eq!(
12702 f.run(&[
12703 b"VSIM",
12704 b"v",
12705 b"VALUES",
12706 b"2",
12707 b"9",
12708 b"1",
12709 b"COUNT",
12710 b"1",
12711 b"FILTER-EF",
12712 b"500",
12713 b"FILTER",
12714 b".lang == 'en'",
12715 ]),
12716 "*1\r\n$1\r\nb\r\n"
12717 );
12718 assert_eq!(
12719 f.run(&[
12720 b"VSIM",
12721 b"v",
12722 b"VALUES",
12723 b"2",
12724 b"9",
12725 b"1",
12726 b"COUNT",
12727 b"1",
12728 b"FILTER-EF",
12729 b"0"
12730 ]),
12731 "*1\r\n$1\r\nb\r\n"
12732 );
12733 assert_eq!(
12734 f.run(&[
12735 b"VSIM",
12736 b"v",
12737 b"VALUES",
12738 b"2",
12739 b"9",
12740 b"1",
12741 b"FILTER-EF",
12742 b"lots"
12743 ]),
12744 "-ERR EF must be a positive integer\r\n"
12745 );
12746 }
12747
12748 #[test]
12751 fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12752 let mut f = Fixture::new();
12753 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12754 assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12755 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12756 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12757 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12758 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12759 assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12760 assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12761 assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12762 assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12763 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12764
12765 f.run(&[b"SET", b"s", b"1"]);
12767 assert_eq!(
12768 f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12769 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12770 );
12771 assert_eq!(
12772 f.run(&[b"VCARD", b"s"]),
12773 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12774 );
12775 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12776 assert_eq!(
12777 f.run(&[b"GET", b"v"]),
12778 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12779 );
12780 f.run(&[b"G.NADD", b"social", b"ada"]);
12784 assert_eq!(
12785 f.run(&[b"VCARD", b"social"]),
12786 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12787 );
12788 assert_eq!(
12789 f.run(&[b"G.NGET", b"v", b"ada"]),
12790 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12791 );
12792 }
12793
12794 #[test]
12797 fn vrandmember_has_the_two_shapes_srandmember_has() {
12798 let mut f = Fixture::new();
12799 for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12800 let x = (i + 1).to_string();
12801 f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12802 }
12803 let one = f.run(&[b"VRANDMEMBER", b"v"]);
12805 assert!(one.starts_with("$1\r\n"), "{one}");
12806 let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12808 assert!(all.starts_with("*3\r\n"), "{all}");
12809 for name in ["a", "b", "c"] {
12810 assert!(all.contains(name), "{all} is missing {name}");
12811 }
12812 all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12813 assert!(all.starts_with("*2\r\n"), "{all}");
12814 let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12816 assert!(many.starts_with("*5\r\n"), "{many}");
12817 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12819 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12820 }
12821
12822 #[test]
12825 fn vlinks_reports_one_layer_of_partition_neighbours() {
12826 let mut f = Fixture::new();
12827 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12828 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12829 assert_eq!(
12832 f.run(&[b"VLINKS", b"v", b"east"]),
12833 "*1\r\n*1\r\n$5\r\nnorth\r\n"
12834 );
12835 assert_eq!(
12836 f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12837 "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12838 );
12839 assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12840 assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12841 }
12842
12843 #[test]
12846 fn fp32_and_values_are_the_same_vector() {
12847 let mut f = Fixture::new();
12848 let mut blob = Vec::new();
12849 for x in [3.0f32, 4.0] {
12850 blob.extend_from_slice(&x.to_le_bytes());
12851 }
12852 assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12853 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12854 assert_eq!(
12855 f.run(&[b"VEMB", b"v", b"a"]),
12856 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12857 );
12858 assert_eq!(
12864 f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
12865 "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
12866 );
12867 assert_eq!(
12869 f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12870 "-ERR invalid vector specification\r\n"
12871 );
12872 assert_eq!(
12874 f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12875 "-ERR syntax error\r\n"
12876 );
12877 assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12878 }
12879
12880 #[test]
12885 fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12886 let mut f = Fixture::new();
12887 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12888 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12889 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12890 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12891 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12892 assert_eq!(
12895 f.run(&[b"BF.INFO", b"b"]),
12896 "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12897 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12898 +Expansion rate\r\n:2\r\n"
12899 );
12900 assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12901 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12902 assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12905 assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12906 }
12907
12908 #[test]
12914 fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12915 let mut f = Fixture::new();
12916 f.run(&[b"SET", b"s", b"text"]);
12917 assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12918 assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12919 for cmd in [
12920 vec![&b"BF.ADD"[..], b"s", b"x"],
12921 vec![&b"BF.MADD"[..], b"s", b"x"],
12922 vec![&b"BF.CARD"[..], b"s"],
12923 vec![&b"BF.INFO"[..], b"s"],
12924 vec![&b"BF.DEBUG"[..], b"s"],
12925 vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
12926 ] {
12927 let name = String::from_utf8_lossy(cmd[0]).into_owned();
12928 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12929 }
12930 assert_eq!(
12933 f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
12934 "-ERR bad error rate\r\n"
12935 );
12936 assert!(
12937 f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
12938 .starts_with("-WRONGTYPE")
12939 );
12940 }
12941
12942 #[test]
12945 fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
12946 let mut f = Fixture::new();
12947 assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
12948 for i in 0..10u32 {
12949 assert_eq!(
12950 f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
12951 ":1\r\n"
12952 );
12953 }
12954 assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
12955 assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
12956 assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
12957 assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
12960 assert_eq!(
12961 f.run(&[b"BF.DEBUG", b"g"]),
12962 "*3\r\n$7\r\nsize:11\r\n\
12963 $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
12964 $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
12965 );
12966
12967 assert_eq!(
12969 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
12970 "+OK\r\n"
12971 );
12972 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
12973 assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
12974 assert_eq!(
12975 f.run(&[b"BF.ADD", b"n", b"c"]),
12976 "-ERR non scaling filter is full\r\n"
12977 );
12978 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12981 assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
12984 f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
12985 assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
12986 assert_eq!(
12989 f.run(&[
12990 b"BF.RESERVE",
12991 b"q",
12992 b"0.01",
12993 b"2",
12994 b"NONSCALING",
12995 b"EXPANSION",
12996 b"2"
12997 ]),
12998 "-Nonscaling filters cannot expand\r\n"
12999 );
13000 }
13001
13002 #[test]
13005 fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
13006 let mut f = Fixture::new();
13007 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
13008 assert_eq!(
13009 f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
13010 "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
13011 );
13012 assert_eq!(
13013 f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
13014 "*2\r\n:1\r\n:0\r\n"
13015 );
13016 }
13017
13018 #[test]
13021 fn insert_is_a_reserve_and_a_madd_with_different_errors() {
13022 let mut f = Fixture::new();
13023 assert_eq!(
13024 f.run(&[
13025 b"BF.INSERT",
13026 b"i",
13027 b"CAPACITY",
13028 b"50",
13029 b"ERROR",
13030 b"0.001",
13031 b"ITEMS",
13032 b"a",
13033 b"b"
13034 ]),
13035 "*2\r\n:1\r\n:1\r\n"
13036 );
13037 assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
13038 assert_eq!(
13040 f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13041 "-ERR not found\r\n"
13042 );
13043 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13044 assert_eq!(
13047 f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13048 "-Bad capacity\r\n"
13049 );
13050 assert_eq!(
13051 f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
13052 "-Bad error rate\r\n"
13053 );
13054 assert_eq!(
13055 f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
13056 "-Bad expansion\r\n"
13057 );
13058 assert_eq!(
13063 f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
13064 "*1\r\n:1\r\n"
13065 );
13066 assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
13067 assert_eq!(
13071 f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
13072 "*1\r\n:1\r\n"
13073 );
13074 assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
13075 assert_eq!(
13076 f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
13077 "*1\r\n:1\r\n"
13078 );
13079 assert_eq!(
13080 f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
13081 "-ERR not found\r\n"
13082 );
13083 assert_eq!(
13085 f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13086 "-Unknown argument received\r\n"
13087 );
13088 assert_eq!(
13090 f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13091 "*1\r\n:1\r\n"
13092 );
13093 assert!(
13095 f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
13096 .contains("wrong number of arguments")
13097 );
13098 }
13099
13100 #[test]
13103 fn a_dump_replays_into_a_filter_that_answers_the_same() {
13104 let mut f = Fixture::new();
13105 f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
13106 for i in 0..25u32 {
13107 f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
13108 }
13109 assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
13110
13111 let mut iter = b"0".to_vec();
13114 let mut chunks = 0;
13115 loop {
13116 let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
13117 let text = String::from_utf8_lossy(&raw).into_owned();
13118 let next = text
13119 .split("\r\n")
13120 .nth(1)
13121 .and_then(|n| n.strip_prefix(':'))
13122 .expect("a two element reply of an iterator and a chunk")
13123 .to_owned();
13124 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13125 let data = &body[body
13126 .windows(2)
13127 .position(|w| w == b"\r\n")
13128 .expect("a length line")
13129 + 2..body.len() - 2];
13130 if next == "0" {
13131 assert!(data.is_empty(), "the last chunk is empty");
13132 break;
13133 }
13134 let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
13135 assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
13136 iter = next.into_bytes();
13137 chunks += 1;
13138 }
13139 assert_eq!(chunks, 3, "a header and one chunk per link");
13140
13141 assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
13142 assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
13143 for i in 0..25u32 {
13144 assert_eq!(
13145 f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
13146 ":1\r\n"
13147 );
13148 }
13149
13150 assert_eq!(
13153 f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
13154 "-ERR received bad data\r\n"
13155 );
13156 assert_eq!(
13157 f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
13158 "-ERR received bad data\r\n"
13159 );
13160 assert_eq!(
13162 f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
13163 "-ERR invalid offset - no link found\r\n"
13164 );
13165 assert_eq!(
13166 f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
13167 "-ERR Second argument must be numeric\r\n"
13168 );
13169 assert_eq!(
13172 f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
13173 "-Second argument must be numeric\r\n"
13174 );
13175 }
13176
13177 #[test]
13180 fn reserve_reads_its_numbers_the_way_string2ll_does() {
13181 let mut f = Fixture::new();
13182 for (args, want) in [
13183 (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
13184 (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
13185 (
13186 vec![&b"0"[..], b"10"],
13187 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13188 ),
13189 (
13190 vec![&b"1"[..], b"10"],
13191 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13192 ),
13193 (
13194 vec![&b"inf"[..], b"10"],
13195 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13196 ),
13197 (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
13198 (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
13199 (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
13200 (
13201 vec![&b"0.01"[..], b"0"],
13202 "-ERR capacity must be in the range [1, 1073741824]\r\n",
13203 ),
13204 (
13205 vec![&b"0.01"[..], b"1073741825"],
13206 "-ERR capacity must be in the range [1, 1073741824]\r\n",
13207 ),
13208 ] {
13209 let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
13210 cmd.extend(args.iter().copied());
13211 assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
13212 }
13213 assert_eq!(
13214 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
13215 "-ERR no expansion\r\n"
13216 );
13217 assert_eq!(
13218 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
13219 "-ERR bad expansion\r\n"
13220 );
13221 assert_eq!(
13222 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
13223 "-ERR expansion must be in the range [0, 32768]\r\n"
13224 );
13225 assert_eq!(
13227 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
13228 "+OK\r\n"
13229 );
13230 assert_eq!(
13231 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
13232 "-ERR item exists\r\n"
13233 );
13234 assert_eq!(
13235 f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
13236 "-Invalid information value\r\n"
13237 );
13238 assert!(
13239 f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
13240 .contains("wrong number of arguments")
13241 );
13242 }
13243
13244 #[test]
13246 fn the_bloom_family_answers_in_resp3_spelling_too() {
13247 let mut f = Fixture::new();
13248 f.out.set_proto(Proto::Resp3);
13249 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
13250 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
13251 assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
13252 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
13253 assert_eq!(
13254 f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
13255 "*2\r\n#t\r\n#f\r\n"
13256 );
13257 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
13259 assert_eq!(
13260 f.run(&[b"BF.INFO", b"b"]),
13261 "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
13262 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
13263 +Expansion rate\r\n:2\r\n"
13264 );
13265 assert_eq!(
13268 f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
13269 "%1\r\n+Capacity\r\n:100\r\n"
13270 );
13271 }
13272
13273 fn cf_header(
13282 items: u64,
13283 buckets: u64,
13284 deletes: u64,
13285 filters: u64,
13286 geometry: [u16; 3],
13287 ) -> Vec<u8> {
13288 let mut out = Vec::with_capacity(38);
13289 for n in [items, buckets, deletes, filters] {
13290 out.extend_from_slice(&n.to_le_bytes());
13291 }
13292 for n in geometry {
13293 out.extend_from_slice(&n.to_le_bytes());
13294 }
13295 out
13296 }
13297
13298 #[test]
13302 fn cf_add_makes_the_filter_and_counts_the_copies() {
13303 let mut f = Fixture::new();
13304 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13305 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13306 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
13307 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
13310 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
13311 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
13312 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
13313 assert_eq!(
13314 f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
13315 "*2\r\n:1\r\n:0\r\n"
13316 );
13317 assert_eq!(
13320 f.run(&[b"CF.INFO", b"d"]),
13321 "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13322 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
13323 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
13324 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13325 );
13326 assert_eq!(
13327 f.run(&[b"CF.DEBUG", b"d"]),
13328 "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
13329 max_iterations:20 expansion:1\r\n"
13330 );
13331 assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
13332 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13333
13334 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13336 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
13337 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13338 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
13339 assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
13340
13341 assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
13344 assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
13345 assert_eq!(
13346 f.run(&[b"CF.COMPACT", b"gone"]),
13347 "-Cuckoo filter was not found\r\n"
13348 );
13349 assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
13350 assert!(
13354 f.run(&[b"CF.COMPACT", b"a", b"b"])
13355 .contains("wrong number of arguments")
13356 );
13357 }
13358
13359 #[test]
13362 fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
13363 let mut f = Fixture::new();
13364 f.run(&[b"SET", b"s", b"text"]);
13365 assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
13366 assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
13367 assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
13368 assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
13372 assert_eq!(
13373 f.run(&[b"CF.COMPACT", b"s"]),
13374 "-Cuckoo filter was not found\r\n"
13375 );
13376 for cmd in [
13377 vec![&b"CF.ADD"[..], b"s", b"x"],
13378 vec![&b"CF.ADDNX"[..], b"s", b"x"],
13379 vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
13380 vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
13381 vec![&b"CF.INFO"[..], b"s"],
13382 vec![&b"CF.DEBUG"[..], b"s"],
13383 vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
13384 vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
13385 vec![&b"CF.RESERVE"[..], b"s", b"64"],
13386 ] {
13387 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13388 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
13389 }
13390 }
13391
13392 #[test]
13395 fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
13396 let mut f = Fixture::new();
13397 assert_eq!(
13398 f.run(&[
13399 b"CF.RESERVE",
13400 b"r",
13401 b"64",
13402 b"BUCKETSIZE",
13403 b"1",
13404 b"MAXITERATIONS",
13405 b"7",
13406 b"EXPANSION",
13407 b"4"
13408 ]),
13409 "+OK\r\n"
13410 );
13411 assert_eq!(
13412 f.run(&[b"CF.DEBUG", b"r"]),
13413 "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13414 max_iterations:7 expansion:4\r\n"
13415 );
13416 assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13417
13418 assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13419 assert_eq!(
13420 f.run(&[b"CF.RESERVE", b"q", b"1"]),
13421 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13422 );
13423 assert_eq!(
13426 f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13427 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13428 );
13429 assert_eq!(
13430 f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13431 "+OK\r\n"
13432 );
13433
13434 assert_eq!(
13439 f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13440 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13441 );
13442 assert_eq!(
13443 f.run(&[
13444 b"CF.RESERVE",
13445 b"q2",
13446 b"64",
13447 b"EXPANSION",
13448 b"xx",
13449 b"BUCKETSIZE",
13450 b"0"
13451 ]),
13452 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13453 );
13454 assert_eq!(
13455 f.run(&[
13456 b"CF.RESERVE",
13457 b"q2",
13458 b"64",
13459 b"MAXITERATIONS",
13460 b"0",
13461 b"BUCKETSIZE",
13462 b"0"
13463 ]),
13464 "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13465 );
13466 assert_eq!(
13470 f.run(&[
13471 b"CF.RESERVE",
13472 b"q3",
13473 b"64",
13474 b"BUCKETSIZE",
13475 b"1",
13476 b"BUCKETSIZE",
13477 b"0"
13478 ]),
13479 "+OK\r\n"
13480 );
13481 assert_eq!(
13484 f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13485 "+OK\r\n"
13486 );
13487 assert_eq!(
13488 f.run(&[b"CF.DEBUG", b"q4"]),
13489 "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13490 max_iterations:20 expansion:1\r\n"
13491 );
13492 assert!(
13495 f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13496 .contains("wrong number of arguments")
13497 );
13498 }
13499
13500 #[test]
13503 fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13504 let mut f = Fixture::new();
13505 assert_eq!(
13506 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13507 "*2\r\n:1\r\n:1\r\n"
13508 );
13509 assert_eq!(
13510 f.run(&[b"CF.DEBUG", b"i"]),
13511 "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13512 max_iterations:20 expansion:1\r\n"
13513 );
13514 assert_eq!(
13517 f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13518 "*2\r\n:0\r\n:1\r\n"
13519 );
13520 assert_eq!(
13521 f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13522 "-ERR not found\r\n"
13523 );
13524 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13525
13526 assert_eq!(
13527 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13528 "-Bad capacity\r\n"
13529 );
13530 assert_eq!(
13533 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13534 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13535 );
13536 assert_eq!(
13540 f.run(&[
13541 b"CF.INSERT",
13542 b"i",
13543 b"CAPACITY",
13544 b"8",
13545 b"CAPACITY",
13546 b"2",
13547 b"ITEMS",
13548 b"a"
13549 ]),
13550 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13551 );
13552 assert_eq!(
13556 f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13557 "*1\r\n:1\r\n"
13558 );
13559 assert_eq!(
13560 f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13561 "*1\r\n:1\r\n"
13562 );
13563 assert_eq!(
13564 f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13565 "-Unknown argument received\r\n"
13566 );
13567 assert_eq!(
13569 f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13570 "*1\r\n:1\r\n"
13571 );
13572 assert!(
13574 f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13575 .contains("wrong number of arguments")
13576 );
13577 assert!(
13578 f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13579 .contains("wrong number of arguments")
13580 );
13581 }
13582
13583 #[test]
13586 fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13587 let mut f = Fixture::new();
13588 f.run(&[
13589 b"CF.RESERVE",
13590 b"s",
13591 b"4",
13592 b"BUCKETSIZE",
13593 b"1",
13594 b"EXPANSION",
13595 b"0",
13596 ]);
13597 for i in 0..4u32 {
13598 assert_eq!(
13599 f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13600 ":1\r\n"
13601 );
13602 }
13603 assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13604 assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13605 assert_eq!(
13608 f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13609 "*2\r\n:-1\r\n:-1\r\n"
13610 );
13611 assert_eq!(
13612 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13613 "*2\r\n:0\r\n:-1\r\n"
13614 );
13615
13616 let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13622 assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13623 assert_eq!(
13624 f.run(&[b"CF.ADD", b"g", b"q"]),
13625 "-Maximum expansions reached\r\n"
13626 );
13627 assert_eq!(
13628 f.run(&[b"CF.INFO", b"g"]),
13629 "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13630 +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13631 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13632 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13633 );
13634 }
13635
13636 #[test]
13640 fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13641 let mut f = Fixture::new();
13642 f.run(&[
13643 b"CF.RESERVE",
13644 b"src",
13645 b"8",
13646 b"BUCKETSIZE",
13647 b"2",
13648 b"EXPANSION",
13649 b"2",
13650 ]);
13651 for i in 0..40u32 {
13652 f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13653 }
13654 let mut pos = b"0".to_vec();
13658 let mut chunks = 0;
13659 loop {
13660 let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13661 let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13662 let next = head
13663 .split("\r\n")
13664 .nth(1)
13665 .and_then(|n| n.strip_prefix(':'))
13666 .expect("a two element reply of a position and a chunk")
13667 .to_owned();
13668 if next == "0" {
13669 assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13670 break;
13671 }
13672 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13673 let at = body
13674 .windows(2)
13675 .position(|w| w == b"\r\n")
13676 .expect("a length line")
13677 + 2;
13678 let data = &body[at..body.len() - 2];
13679 assert_eq!(
13680 f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13681 "+OK\r\n",
13682 "loading chunk {chunks}"
13683 );
13684 pos = next.into_bytes();
13685 chunks += 1;
13686 }
13687 assert!(chunks >= 2, "a header and at least one chunk");
13688
13689 assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13690 assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13691 for i in 0..40u32 {
13692 assert_eq!(
13693 f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13694 ":1\r\n"
13695 );
13696 }
13697
13698 f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13701 assert_eq!(
13702 f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13703 "*2\r\n:0\r\n$-1\r\n"
13704 );
13705
13706 assert_eq!(
13710 f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13711 "-Invalid position\r\n"
13712 );
13713 assert_eq!(
13714 f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13715 "-Invalid position\r\n"
13716 );
13717 assert_eq!(
13718 f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13719 "-Invalid position\r\n"
13720 );
13721 assert_eq!(
13722 f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13723 "-Couldn't load chunk!\r\n"
13724 );
13725 let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13727 assert_eq!(
13728 f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13729 "-ERR item exists\r\n"
13730 );
13731 assert_eq!(
13735 f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13736 "-Invalid header\r\n"
13737 );
13738 for (why, bad) in [
13739 ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13740 ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13741 (
13742 "a bucket count that is not a power of two",
13743 cf_header(0, 3, 0, 1, [2, 20, 1]),
13744 ),
13745 ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13746 ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13747 (
13748 "a growth nobody could reach",
13749 cf_header(0, 8, 0, 1, [2, 20, 32769]),
13750 ),
13751 (
13752 "a chain that cannot grow and did",
13753 cf_header(0, 8, 0, 2, [2, 20, 0]),
13754 ),
13755 (
13758 "a filter count that wraps",
13759 cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13760 ),
13761 ] {
13762 assert_eq!(
13763 f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13764 "-Couldn't create filter!\r\n",
13765 "{why}"
13766 );
13767 }
13768 }
13769
13770 #[test]
13773 fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13774 let mut f = Fixture::new();
13775 f.out.set_proto(Proto::Resp3);
13776 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13777 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13778 assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13779 assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13780 assert_eq!(
13781 f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13782 "*2\r\n#t\r\n#f\r\n"
13783 );
13784 assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13785 assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13786 assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13788 assert_eq!(
13789 f.run(&[b"CF.INFO", b"c"]),
13790 "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13791 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13792 +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13793 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13794 );
13795
13796 f.run(&[
13801 b"CF.RESERVE",
13802 b"s",
13803 b"4",
13804 b"BUCKETSIZE",
13805 b"1",
13806 b"EXPANSION",
13807 b"0",
13808 ]);
13809 assert_eq!(
13810 f.run(&[
13811 b"CF.INSERT",
13812 b"s",
13813 b"ITEMS",
13814 b"a",
13815 b"b",
13816 b"c",
13817 b"d",
13818 b"e",
13819 b"f"
13820 ]),
13821 "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13822 );
13823 assert_eq!(
13824 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13825 "*2\r\n:0\r\n:-1\r\n"
13826 );
13827 assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13828 assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13831 }
13832
13833 #[test]
13838 fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13839 let mut f = Fixture::new();
13840 assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13841 assert_eq!(
13842 f.run(&[b"CMS.INFO", b"d"]),
13843 "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13844 );
13845 assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13846 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13847 assert_eq!(
13850 f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13851 "+OK\r\n"
13852 );
13853 assert_eq!(
13854 f.run(&[b"CMS.INFO", b"p"]),
13855 "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13856 );
13857 assert_eq!(
13860 f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13861 "-CMS: key already exists\r\n"
13862 );
13863 assert_eq!(
13864 f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13865 "-CMS: invalid width\r\n"
13866 );
13867 assert_eq!(
13868 f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13869 "-CMS: invalid depth\r\n"
13870 );
13871 assert_eq!(
13872 f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13873 "-CMS: invalid overestimation value\r\n"
13874 );
13875 assert_eq!(
13876 f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13877 "-CMS: invalid prob value\r\n"
13878 );
13879 assert_eq!(
13883 f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13884 "-CMS: invalid init arguments\r\n"
13885 );
13886 assert_eq!(
13890 f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13891 "-CMS: Insufficient memory to create the key\r\n"
13892 );
13893 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13894 }
13895
13896 #[test]
13899 fn increments_are_parsed_whole_and_the_counters_saturate() {
13900 let mut f = Fixture::new();
13901 f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13902 assert_eq!(
13903 f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13904 "*2\r\n:3\r\n:4\r\n"
13905 );
13906 assert_eq!(
13909 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13910 "*2\r\n:4\r\n:5\r\n"
13911 );
13912 assert_eq!(
13914 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13915 "-CMS: Cannot parse number\r\n"
13916 );
13917 assert_eq!(
13918 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
13919 "-CMS: Number cannot be negative\r\n"
13920 );
13921 assert_eq!(
13922 f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
13923 "*2\r\n:5\r\n:4\r\n"
13924 );
13925 f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
13928 assert_eq!(
13929 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
13930 "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
13931 );
13932 assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
13933 f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
13936 f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
13937 f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
13938 assert_eq!(
13939 f.run(&[b"CMS.INFO", b"w"]),
13940 "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
13941 );
13942 assert!(
13945 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
13946 .contains("wrong number of arguments")
13947 );
13948 assert_eq!(
13949 f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
13950 "-CMS: key does not exist\r\n"
13951 );
13952 assert_eq!(
13953 f.run(&[b"CMS.QUERY", b"nope", b"a"]),
13954 "-CMS: key does not exist\r\n"
13955 );
13956 }
13957
13958 #[test]
13961 fn a_merge_lands_whole_or_not_at_all() {
13962 let mut f = Fixture::new();
13963 for name in [&b"m1"[..], b"m2", b"dst"] {
13964 f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
13965 }
13966 f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
13967 f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
13968 assert_eq!(
13969 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13970 "+OK\r\n"
13971 );
13972 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13973 assert_eq!(
13976 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13977 "+OK\r\n"
13978 );
13979 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13980 assert_eq!(
13981 f.run(&[
13982 b"CMS.MERGE",
13983 b"dst",
13984 b"2",
13985 b"m1",
13986 b"m2",
13987 b"WEIGHTS",
13988 b"2",
13989 b"3"
13990 ]),
13991 "+OK\r\n"
13992 );
13993 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13994 assert_eq!(
13997 f.run(&[
13998 b"CMS.MERGE",
13999 b"dst",
14000 b"1",
14001 b"m1",
14002 b"WEIGHTS",
14003 b"4611686018427387904"
14004 ]),
14005 "-CMS: MERGE overflow\r\n"
14006 );
14007 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
14008 f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
14011 assert_eq!(
14012 f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
14013 "-CMS: key does not exist\r\n"
14014 );
14015 assert_eq!(
14016 f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
14017 "-CMS: Number of keys must be positive\r\n"
14018 );
14019 assert_eq!(
14020 f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
14021 "-CMS: wrong number of keys\r\n"
14022 );
14023 assert_eq!(
14024 f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
14025 "-CMS: wrong number of keys/weights\r\n"
14026 );
14027 assert_eq!(
14028 f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
14029 "-CMS: width/depth is not equal\r\n"
14030 );
14031 assert_eq!(
14032 f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
14033 "-CMS: key does not exist\r\n"
14034 );
14035 }
14036
14037 #[test]
14040 fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14041 let mut f = Fixture::new();
14042 f.run(&[b"SET", b"s", b"text"]);
14043 for cmd in [
14044 vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
14045 vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
14046 vec![&b"CMS.QUERY"[..], b"s", b"a"],
14047 vec![&b"CMS.INFO"[..], b"s"],
14048 vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
14049 ] {
14050 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14051 let reply = f.run(&cmd);
14052 assert!(
14055 reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
14056 "{name}: {reply}"
14057 );
14058 }
14059 f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
14060 assert_eq!(
14064 f.run(&[b"COPY", b"c", b"c2"]),
14065 "-ERR not supported for this module key\r\n"
14066 );
14067 assert_eq!(
14068 f.run(&[b"DUMP", b"c"]),
14069 "-ERR DUMP is not supported for this module key\r\n"
14070 );
14071 f.run(&[b"G.NADD", b"g", b"a"]);
14073 assert_eq!(
14074 f.run(&[b"COPY", b"g", b"g2"]),
14075 "-ERR COPY is not supported for a graph\r\n"
14076 );
14077 assert_eq!(
14078 f.run(&[b"DUMP", b"g"]),
14079 "-ERR DUMP is not supported for a graph\r\n"
14080 );
14081 assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
14084 assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
14085 assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
14086 assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
14087 assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
14088 }
14089
14090 #[test]
14095 fn a_reserve_takes_three_arguments_or_six() {
14096 let mut f = Fixture::new();
14097 assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
14098 assert_eq!(
14099 f.run(&[b"TOPK.INFO", b"t"]),
14100 "*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"
14101 );
14102 for cmd in [
14105 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
14106 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
14107 ] {
14108 assert!(f.run(&cmd).contains("wrong number of arguments"));
14109 }
14110 assert_eq!(
14111 f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
14112 "+OK\r\n"
14113 );
14114 assert_eq!(
14117 f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
14118 "-TopK: key already exists\r\n"
14119 );
14120 assert_eq!(
14121 f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
14122 "-TopK: invalid k\r\n"
14123 );
14124 assert_eq!(
14125 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
14126 "-TopK: invalid width\r\n"
14127 );
14128 assert_eq!(
14129 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
14130 "-TopK: invalid depth\r\n"
14131 );
14132 assert_eq!(
14134 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
14135 "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
14136 );
14137 assert_eq!(
14138 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
14139 "+OK\r\n"
14140 );
14141 assert_eq!(
14143 f.run(&[
14144 b"TOPK.RESERVE",
14145 b"w",
14146 b"1",
14147 b"4294967295",
14148 b"4294967295",
14149 b"0.9"
14150 ]),
14151 "-ERR Insufficient memory to create topk data structure\r\n"
14152 );
14153 }
14154
14155 #[test]
14157 fn the_kept_set_is_what_query_and_list_answer_from() {
14158 let mut f = Fixture::new();
14159 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
14160 assert_eq!(
14163 f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
14164 "*2\r\n$-1\r\n$-1\r\n"
14165 );
14166 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
14167 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
14170 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
14171 assert_eq!(
14172 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
14173 "*3\r\n:1\r\n:0\r\n:1\r\n"
14174 );
14175 assert_eq!(
14177 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14178 "*3\r\n:11\r\n:1\r\n:6\r\n"
14179 );
14180 assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
14181 assert_eq!(
14182 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
14183 "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
14184 );
14185 assert_eq!(
14188 f.run(&[b"TOPK.LIST", b"t", b"w"]),
14189 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14190 );
14191 assert_eq!(
14192 f.run(&[b"TOPK.LIST", b"t", b""]),
14193 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14194 );
14195 assert_eq!(
14196 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
14197 "-WITHCOUNT keyword expected\r\n"
14198 );
14199 assert_eq!(
14202 f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
14203 "-WITHCOUNT keyword expected\r\n"
14204 );
14205 assert_eq!(
14206 f.run(&[b"TOPK.LIST", b"missing"]),
14207 "-TopK: key does not exist\r\n"
14208 );
14209 f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
14211 assert_eq!(
14212 f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
14213 "*1\r\n$-1\r\n"
14214 );
14215 assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
14216 assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
14217 }
14218
14219 #[test]
14222 fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
14223 let mut f = Fixture::new();
14224 f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
14225 assert_eq!(
14229 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
14230 format!(
14231 "*2\r\n$-1\r\n-{}\r\n",
14232 "TopK: increment must be an integer greater or equal to 0 and smaller or equal to 100,000"
14233 )
14234 );
14235 assert_eq!(
14236 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14237 "*3\r\n:3\r\n:0\r\n:0\r\n"
14238 );
14239 assert_eq!(
14241 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
14242 "*1\r\n$-1\r\n"
14243 );
14244 assert!(
14245 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
14246 .contains("smaller or equal to 100,000")
14247 );
14248 assert!(
14250 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
14251 .contains("wrong number of arguments")
14252 );
14253 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
14254 }
14255
14256 #[test]
14258 fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
14259 let mut f = Fixture::new();
14260 f.run(&[b"HELLO", b"3"]);
14261 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
14262 f.run(&[b"TOPK.ADD", b"t", b"a"]);
14263 assert_eq!(
14264 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
14265 "*2\r\n#t\r\n#f\r\n"
14266 );
14267 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
14269 assert_eq!(
14270 f.run(&[b"TOPK.INFO", b"t"]),
14271 "%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"
14272 );
14273 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
14274 }
14275
14276 #[test]
14279 fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14280 let mut f = Fixture::new();
14281 f.run(&[b"SET", b"s", b"text"]);
14282 for cmd in [
14283 vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
14284 vec![&b"TOPK.ADD"[..], b"s", b"a"],
14285 vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
14286 vec![&b"TOPK.QUERY"[..], b"s", b"a"],
14287 vec![&b"TOPK.COUNT"[..], b"s", b"a"],
14288 vec![&b"TOPK.LIST"[..], b"s"],
14289 vec![&b"TOPK.INFO"[..], b"s"],
14290 ] {
14291 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14292 let reply = f.run(&cmd);
14293 assert!(
14294 reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
14295 "{name}: {reply}"
14296 );
14297 }
14298 f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
14299 assert_eq!(
14300 f.run(&[b"COPY", b"t", b"t2"]),
14301 "-ERR not supported for this module key\r\n"
14302 );
14303 assert_eq!(
14304 f.run(&[b"DUMP", b"t"]),
14305 "-ERR DUMP is not supported for this module key\r\n"
14306 );
14307 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14308 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14309 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14310 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
14311 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14312 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14313 assert_eq!(
14316 f.run(&[b"TOPK.INFO", b"t3"]),
14317 "-TopK: key does not exist\r\n"
14318 );
14319 }
14320
14321 #[test]
14326 fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
14327 let mut f = Fixture::new();
14328 assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
14329 assert_eq!(
14331 f.run(&[b"TDIGEST.INFO", b"t"]),
14332 "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
14333 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
14334 +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
14335 );
14336 assert_eq!(
14337 f.run(&[b"TDIGEST.CREATE", b"t"]),
14338 "-ERR T-Digest: key already exists\r\n"
14339 );
14340 assert!(
14342 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
14343 .contains("wrong number of arguments")
14344 );
14345 assert_eq!(
14346 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
14347 "+OK\r\n"
14348 );
14349 assert_eq!(
14350 f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
14351 "+OK\r\n"
14352 );
14353 assert_eq!(
14357 f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
14358 "-ERR T-Digest: error parsing compression parameter\r\n"
14359 );
14360 assert_eq!(
14361 f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
14362 "-ERR T-Digest: wrong keyword\r\n"
14363 );
14364 assert_eq!(
14365 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
14366 "-ERR T-Digest: error parsing compression parameter\r\n"
14367 );
14368 assert_eq!(
14369 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
14370 "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
14371 );
14372 assert_eq!(
14375 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
14376 "-ERR T-Digest: allocation failed\r\n"
14377 );
14378 assert_eq!(
14380 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
14381 "-ERR T-Digest: allocation failed\r\n"
14382 );
14383 assert_eq!(
14386 f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
14387 "-ERR T-Digest: key already exists\r\n"
14388 );
14389 }
14390
14391 #[test]
14394 fn the_quantile_family_answers_what_the_module_answers() {
14395 let mut f = Fixture::new();
14396 f.run(&[b"TDIGEST.CREATE", b"s"]);
14397 assert_eq!(
14398 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
14399 "+OK\r\n"
14400 );
14401 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
14402 assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
14403 assert_eq!(
14405 f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
14406 "*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"
14407 );
14408 assert_eq!(
14409 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14410 "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14411 );
14412 assert_eq!(
14415 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14416 "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14417 );
14418 assert_eq!(
14419 f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14420 "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14421 );
14422 assert_eq!(
14423 f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14424 "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14425 );
14426 assert_eq!(
14427 f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14428 "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14429 );
14430 assert_eq!(
14431 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14432 "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14433 );
14434 assert_eq!(
14435 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14436 "$3\r\n2.5\r\n"
14437 );
14438 assert_eq!(
14439 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14440 "$3\r\n2.5\r\n"
14441 );
14442 assert_eq!(
14444 f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14445 "-ERR T-Digest: quantile should be in [0,1]\r\n"
14446 );
14447 assert_eq!(
14448 f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14449 "-ERR T-Digest: error parsing quantile\r\n"
14450 );
14451 assert_eq!(
14452 f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14453 "-ERR T-Digest: error parsing cdf\r\n"
14454 );
14455 assert_eq!(
14456 f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14457 "-ERR T-Digest: error parsing value\r\n"
14458 );
14459 assert_eq!(
14460 f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14461 "-ERR T-Digest: rank needs to be non negative\r\n"
14462 );
14463 assert_eq!(
14464 f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14465 "-ERR T-Digest: error parsing rank\r\n"
14466 );
14467 assert_eq!(
14470 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14471 "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14472 );
14473 assert_eq!(
14474 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14475 "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14476 );
14477 assert_eq!(
14478 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14479 "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14480 );
14481 assert_eq!(
14482 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14483 "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14484 );
14485 }
14486
14487 #[test]
14490 fn an_empty_digest_has_an_answer_for_everything() {
14491 let mut f = Fixture::new();
14492 f.run(&[b"TDIGEST.CREATE", b"e"]);
14493 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14494 assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14495 assert_eq!(
14496 f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14497 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14498 );
14499 assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14500 assert_eq!(
14501 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14502 "$3\r\nnan\r\n"
14503 );
14504 assert_eq!(
14507 f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14508 "*2\r\n:-2\r\n:-2\r\n"
14509 );
14510 assert_eq!(
14511 f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14512 "*2\r\n:-2\r\n:-2\r\n"
14513 );
14514 assert_eq!(
14515 f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14516 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14517 );
14518 f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14520 assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14521 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14522 f.run(&[b"TDIGEST.CREATE", b"e2"]);
14525 assert_eq!(
14526 f.run(&[b"TDIGEST.INFO", b"e"]),
14527 f.run(&[b"TDIGEST.INFO", b"e2"])
14528 );
14529 }
14530
14531 #[test]
14534 fn a_sample_is_read_the_way_redis_reads_a_double() {
14535 let mut f = Fixture::new();
14536 f.run(&[b"TDIGEST.CREATE", b"a"]);
14537 for bad in [
14540 &b"nan"[..],
14541 b"1e400",
14542 b"-1e400",
14543 b"1e309",
14544 b"1e-400",
14545 b"",
14546 b" 1",
14547 b"1 ",
14548 b"1e",
14549 b"--1",
14550 ] {
14551 assert_eq!(
14552 f.run(&[b"TDIGEST.ADD", b"a", bad]),
14553 "-ERR T-Digest: error parsing val parameter\r\n",
14554 "{}",
14555 String::from_utf8_lossy(bad)
14556 );
14557 }
14558 for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14561 assert_eq!(
14562 f.run(&[b"TDIGEST.ADD", b"a", word]),
14563 "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14564 "{}",
14565 String::from_utf8_lossy(word)
14566 );
14567 }
14568 for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14571 assert_eq!(
14572 f.run(&[b"TDIGEST.ADD", b"a", good]),
14573 "+OK\r\n",
14574 "{}",
14575 String::from_utf8_lossy(good)
14576 );
14577 }
14578 assert!(
14580 f.run(&[b"TDIGEST.INFO", b"a"])
14581 .contains("Observations\r\n:6\r\n")
14582 );
14583 assert_eq!(
14586 f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14587 "-ERR T-Digest: error parsing val parameter\r\n"
14588 );
14589 assert!(
14590 f.run(&[b"TDIGEST.INFO", b"a"])
14591 .contains("Observations\r\n:6\r\n")
14592 );
14593 }
14594
14595 #[test]
14598 fn a_merge_sweeps_the_destination_between_its_inputs() {
14599 let mut f = Fixture::new();
14600 f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14601 f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14602 f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14603 f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14604 assert_eq!(
14605 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14606 "+OK\r\n"
14607 );
14608 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14613 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14614 assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14615 assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14616 assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14617 assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14618 assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14619 assert!(
14622 f.run(&[b"TDIGEST.INFO", b"m1"])
14623 .contains("Merged nodes\r\n:3\r\n")
14624 );
14625 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14628 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14629 assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14630 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14631 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14634 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14635 assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14636 assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14637 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14639 assert!(
14640 f.run(&[b"TDIGEST.INFO", b"d"])
14641 .contains("Compression\r\n:500\r\n")
14642 );
14643 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14645 assert!(
14646 f.run(&[b"TDIGEST.INFO", b"d"])
14647 .contains("Observations\r\n:12\r\n")
14648 );
14649 assert_eq!(
14651 f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14652 "-ERR T-Digest: error parsing numkeys\r\n"
14653 );
14654 assert_eq!(
14655 f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14656 "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14657 );
14658 assert!(
14659 f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14660 .contains("wrong number of arguments")
14661 );
14662 assert!(
14663 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14664 .contains("wrong number of arguments")
14665 );
14666 assert_eq!(
14667 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14668 "-ERR T-Digest: wrong keyword\r\n"
14669 );
14670 assert_eq!(
14673 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14674 "-ERR T-Digest: key does not exist\r\n"
14675 );
14676 assert!(
14677 f.run(&[b"TDIGEST.INFO", b"d"])
14678 .contains("Observations\r\n:12\r\n")
14679 );
14680 assert_eq!(
14683 f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14684 "-ERR T-Digest: key does not exist\r\n"
14685 );
14686 }
14687
14688 #[test]
14690 fn a_digest_answers_doubles_and_a_map_on_resp3() {
14691 let mut f = Fixture::new();
14692 f.run(&[b"HELLO", b"3"]);
14693 f.run(&[b"TDIGEST.CREATE", b"s"]);
14694 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14695 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14696 assert_eq!(
14697 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14698 "*2\r\n,1\r\n,4\r\n"
14699 );
14700 assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14701 assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14703 assert_eq!(
14704 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14705 "*1\r\n,-inf\r\n"
14706 );
14707 f.run(&[b"TDIGEST.CREATE", b"e"]);
14708 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14709 assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14711 assert_eq!(
14714 f.run(&[b"TDIGEST.INFO", b"s"]),
14715 "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14716 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14717 +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14718 );
14719 }
14720
14721 #[test]
14724 fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14725 let mut f = Fixture::new();
14726 f.run(&[b"SET", b"s", b"text"]);
14727 for cmd in [
14728 vec![&b"TDIGEST.CREATE"[..], b"s"],
14729 vec![&b"TDIGEST.RESET"[..], b"s"],
14730 vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14731 vec![&b"TDIGEST.MIN"[..], b"s"],
14732 vec![&b"TDIGEST.MAX"[..], b"s"],
14733 vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14734 vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14735 vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14736 vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14737 vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14738 vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14739 vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14740 vec![&b"TDIGEST.INFO"[..], b"s"],
14741 ] {
14742 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14743 let reply = f.run(&cmd);
14744 assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14745 }
14746 f.run(&[b"TDIGEST.CREATE", b"t"]);
14748 assert!(
14749 f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14750 .starts_with("-WRONGTYPE")
14751 );
14752 assert!(
14753 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14754 .starts_with("-WRONGTYPE")
14755 );
14756 assert_eq!(
14757 f.run(&[b"COPY", b"t", b"t2"]),
14758 "-ERR not supported for this module key\r\n"
14759 );
14760 assert_eq!(
14761 f.run(&[b"DUMP", b"t"]),
14762 "-ERR DUMP is not supported for this module key\r\n"
14763 );
14764 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14765 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14766 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14767 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14768 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14769 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14770 assert_eq!(
14773 f.run(&[b"TDIGEST.INFO", b"t3"]),
14774 "-ERR T-Digest: key does not exist\r\n"
14775 );
14776 assert_eq!(
14779 f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14780 "-ERR T-Digest: key does not exist\r\n"
14781 );
14782 }
14783
14784 fn without_memory(reply: &str) -> String {
14794 let head = "+memoryUsage\r\n:";
14795 let at = reply.find(head).expect("every TS.INFO reports memory");
14796 let rest = &reply[at + head.len()..];
14797 let end = rest.find("\r\n").expect("and it is a whole number");
14798 format!("{}{}", &reply[..at + head.len()], &rest[end..])
14799 }
14800
14801 #[test]
14804 fn a_series_is_made_empty_and_reports_on_itself() {
14805 let mut f = Fixture::new();
14806 assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
14807 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14808 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
14809 assert_eq!(
14813 without_memory(&f.run(&[b"TS.INFO", b"t"])),
14814 "*28\r\n\
14815 +totalSamples\r\n:0\r\n\
14816 +memoryUsage\r\n:\r\n\
14817 +firstTimestamp\r\n:0\r\n\
14818 +lastTimestamp\r\n:0\r\n\
14819 +retentionTime\r\n:0\r\n\
14820 +chunkCount\r\n:1\r\n\
14821 +chunkSize\r\n:4096\r\n\
14822 +chunkType\r\n+compressed\r\n\
14823 +duplicatePolicy\r\n+block\r\n\
14824 +labels\r\n*0\r\n\
14825 +sourceKey\r\n$-1\r\n\
14826 +rules\r\n*0\r\n\
14827 +ignoreMaxTimeDiff\r\n:0\r\n\
14828 +ignoreMaxValDiff\r\n$1\r\n0\r\n"
14829 );
14830 assert_eq!(
14833 f.run(&[b"TS.CREATE", b"t"]),
14834 "-ERR TSDB: key already exists\r\n"
14835 );
14836 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14837 assert_eq!(
14838 f.run(&[b"TS.CREATE", b"str"]),
14839 "-ERR TSDB: key already exists\r\n"
14840 );
14841 assert_eq!(
14844 f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
14845 "-ERR TSDB: Couldn't parse RETENTION\r\n"
14846 );
14847 let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14853 assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
14854 assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
14855 assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
14856 assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
14857 assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
14858 assert_eq!(
14859 f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
14860 "-ERR TSDB: the key is not a TSDB key\r\n"
14861 );
14862 assert_eq!(
14864 f.run(&[b"TS.INFO", b"nope"]),
14865 "-ERR TSDB: the key does not exist\r\n"
14866 );
14867 assert_eq!(
14868 f.run(&[b"TS.GET", b"nope"]),
14869 "-ERR TSDB: the key does not exist\r\n"
14870 );
14871 assert_eq!(
14872 f.run(&[b"TS.ALTER", b"nope"]),
14873 "-ERR TSDB: the key does not exist\r\n"
14874 );
14875 assert_eq!(
14876 f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
14877 "-ERR TSDB: the key does not exist\r\n"
14878 );
14879 }
14880
14881 #[test]
14884 fn the_options_are_a_keyword_scan_and_not_a_grammar() {
14885 let mut f = Fixture::new();
14886 assert_eq!(
14887 f.run(&[
14888 b"TS.CREATE",
14889 b"t",
14890 b"RETENTION",
14891 b"5000",
14892 b"ENCODING",
14893 b"UNCOMPRESSED",
14894 b"CHUNK_SIZE",
14895 b"128",
14896 b"DUPLICATE_POLICY",
14897 b"LAST",
14898 b"IGNORE",
14899 b"10",
14900 b"0.5",
14901 b"LABELS",
14902 b"room",
14903 b"kitchen"
14904 ]),
14905 "+OK\r\n"
14906 );
14907 let info = f.run(&[b"TS.INFO", b"t"]);
14908 assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
14909 assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
14910 assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
14911 assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
14912 assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
14913 assert!(
14916 info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
14917 "{info}"
14918 );
14919 assert!(
14920 info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
14921 "{info}"
14922 );
14923
14924 assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
14926 assert_eq!(
14930 f.run(&[
14931 b"TS.CREATE",
14932 b"g",
14933 b"LABELS",
14934 b"a",
14935 b"b",
14936 b"RETENTION",
14937 b"5"
14938 ]),
14939 "+OK\r\n"
14940 );
14941 let greedy = f.run(&[b"TS.INFO", b"g"]);
14942 assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
14943 assert!(
14944 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"),
14945 "{greedy}"
14946 );
14947
14948 assert_eq!(
14950 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
14951 "-ERR TSDB: Couldn't parse LABELS\r\n"
14952 );
14953 assert_eq!(
14954 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
14955 "-ERR TSDB: Couldn't parse LABELS\r\n"
14956 );
14957 assert_eq!(
14958 f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
14959 "-ERR TSDB: Couldn't parse RETENTION\r\n"
14960 );
14961 assert_eq!(
14964 f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
14965 "-TSDB: Couldn't parse RETENTION\r\n"
14966 );
14967 assert_eq!(
14968 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
14969 "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
14970 );
14971 assert_eq!(
14972 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
14973 "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
14974 );
14975 assert_eq!(
14976 f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
14977 "-ERR TSDB: unknown ENCODING parameter\r\n"
14978 );
14979 assert!(
14982 f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
14983 .contains("wrong number of arguments for 'ts.create' command")
14984 );
14985 assert_eq!(
14986 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
14987 "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
14988 );
14989 assert_eq!(
14990 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
14991 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14992 );
14993 assert_eq!(
14994 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
14995 "-ERR TSDB: Couldn't parse IGNORE\r\n"
14996 );
14997 assert_eq!(
14998 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
14999 "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
15000 );
15001 assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
15002
15003 assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
15006 let after = f.run(&[b"TS.INFO", b"t"]);
15007 assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
15008 assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
15009 assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
15010 assert_eq!(
15011 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
15012 "-ERR TSDB: unknown ENCODING parameter\r\n"
15013 );
15014 assert_eq!(
15016 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
15017 "+OK\r\n"
15018 );
15019 assert!(
15020 f.run(&[b"TS.INFO", b"t"])
15021 .contains("+chunkType\r\n+uncompressed\r\n")
15022 );
15023 }
15024
15025 #[test]
15028 fn samples_land_where_they_are_put_and_the_newest_comes_back() {
15029 let mut f = Fixture::new();
15030 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
15031 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
15033 assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
15034 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
15037 assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
15038 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
15039 assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
15042 assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
15043
15044 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15047 assert_eq!(
15048 f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
15049 "-ERR TSDB: invalid value\r\n"
15050 );
15051 for bad in [
15055 &b".5"[..],
15056 b"1.",
15057 b"+1",
15058 b" 1",
15059 b"0x10",
15060 b"inf",
15061 b"1e400",
15062 b"--1",
15063 b"1e",
15064 ] {
15065 assert_eq!(
15066 f.run(&[b"TS.ADD", b"v", b"1", bad]),
15067 "-ERR TSDB: invalid value\r\n",
15068 "{}",
15069 String::from_utf8_lossy(bad)
15070 );
15071 }
15072 assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
15074
15075 assert_eq!(
15078 f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
15079 "-ERR TSDB: invalid timestamp\r\n"
15080 );
15081 assert_eq!(
15082 f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
15083 "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
15084 );
15085
15086 assert_eq!(
15089 f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
15090 "-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"
15091 );
15092 assert_eq!(
15093 f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
15094 ":300\r\n"
15095 );
15096 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
15097 assert_eq!(
15100 f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
15101 ":1\r\n"
15102 );
15103 assert_eq!(
15104 f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
15105 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
15106 );
15107
15108 assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
15111 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
15112 assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
15113 assert_eq!(
15114 f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
15115 "-ERR TSDB: Timestamp is older than retention\r\n"
15116 );
15117 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
15119 assert!(
15120 f.run(&[b"TS.INFO", b"r"])
15121 .contains("+totalSamples\r\n:1\r\n")
15122 );
15123
15124 assert_eq!(
15127 f.run(&[
15128 b"TS.CREATE",
15129 b"i",
15130 b"DUPLICATE_POLICY",
15131 b"LAST",
15132 b"IGNORE",
15133 b"10",
15134 b"0.5"
15135 ]),
15136 "+OK\r\n"
15137 );
15138 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
15139 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
15140 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
15141 }
15142
15143 #[test]
15146 fn a_madd_answers_each_triple_and_creates_nothing() {
15147 let mut f = Fixture::new();
15148 assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
15149 assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
15150 assert_eq!(
15151 f.run(&[
15152 b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
15153 ]),
15154 "*3\r\n:100\r\n:100\r\n:200\r\n"
15155 );
15156 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15159 assert_eq!(
15160 f.run(&[
15161 b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
15162 ]),
15163 "*3\r\n\
15164 -ERR TSDB: the key is not a TSDB key\r\n\
15165 -ERR TSDB: the key is not a TSDB key\r\n\
15166 :300\r\n"
15167 );
15168 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
15169 assert_eq!(
15171 f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
15172 "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
15173 );
15174 assert!(
15176 f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
15177 .contains("wrong number of arguments for 'ts.madd' command")
15178 );
15179 }
15180
15181 #[test]
15183 fn an_increment_walks_the_newest_value_up_and_down() {
15184 let mut f = Fixture::new();
15185 assert_eq!(
15186 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15187 ":100\r\n"
15188 );
15189 assert_eq!(
15190 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15191 ":100\r\n"
15192 );
15193 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
15196 assert_eq!(
15197 f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
15198 ":200\r\n"
15199 );
15200 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
15201 assert_eq!(
15204 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
15205 "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
15206 );
15207 assert_eq!(
15211 f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
15212 ":1\r\n"
15213 );
15214 assert_eq!(
15215 f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
15216 ":1\r\n"
15217 );
15218 assert_eq!(
15219 f.run(&[b"TS.INCRBY", b"t", b"nan"]),
15220 "-ERR TSDB: invalid increase/decrease value\r\n"
15221 );
15222 assert_eq!(
15223 f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
15224 "-ERR TSDB: invalid increase/decrease value\r\n"
15225 );
15226 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15229 assert_eq!(
15230 f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
15231 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15232 );
15233 assert_eq!(
15238 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
15239 "-ERR TSDB: invalid timestamp\r\n"
15240 );
15241 assert_eq!(
15244 f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
15245 format!(":{}\r\n", f.server.now_ms())
15246 );
15247 assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
15249 assert_eq!(
15250 f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
15251 "-ERR TSDB: cannot increment/decrement NaN value\r\n"
15252 );
15253 }
15254
15255 #[test]
15257 fn deleting_takes_out_a_span_and_answers_how_many_went() {
15258 let mut f = Fixture::new();
15259 for at in [b"100".as_slice(), b"200", b"300", b"400"] {
15260 f.run(&[b"TS.ADD", b"t", at, b"1"]);
15261 }
15262 assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
15263 assert!(
15264 f.run(&[b"TS.INFO", b"t"])
15265 .contains("+totalSamples\r\n:2\r\n")
15266 );
15267 assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
15269 assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
15271 let empty = f.run(&[b"TS.INFO", b"t"]);
15274 assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
15275 assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
15276 assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
15277 assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
15278 assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
15279 assert_eq!(
15281 f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
15282 "-ERR TSDB: wrong fromTimestamp\r\n"
15283 );
15284 assert_eq!(
15285 f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
15286 "-ERR TSDB: wrong toTimestamp\r\n"
15287 );
15288 assert_eq!(
15289 f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
15290 "-ERR TSDB: wrong fromTimestamp\r\n"
15291 );
15292 }
15293
15294 #[test]
15297 fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
15298 let mut f = Fixture::new();
15299 f.out = Out::new(Proto::Resp3);
15300 assert_eq!(
15301 f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
15302 "+OK\r\n"
15303 );
15304 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
15305 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
15307 assert_eq!(
15308 without_memory(&f.run(&[b"TS.INFO", b"t"])),
15309 "%14\r\n\
15310 +totalSamples\r\n:1\r\n\
15311 +memoryUsage\r\n:\r\n\
15312 +firstTimestamp\r\n:100\r\n\
15313 +lastTimestamp\r\n:100\r\n\
15314 +retentionTime\r\n:0\r\n\
15315 +chunkCount\r\n:1\r\n\
15316 +chunkSize\r\n:4096\r\n\
15317 +chunkType\r\n+compressed\r\n\
15318 +duplicatePolicy\r\n+block\r\n\
15319 +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
15320 +sourceKey\r\n_\r\n\
15321 +rules\r\n%0\r\n\
15322 +ignoreMaxTimeDiff\r\n:0\r\n\
15323 +ignoreMaxValDiff\r\n,0\r\n"
15324 );
15325 }
15326
15327 #[test]
15330 fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
15331 let mut f = Fixture::new();
15332 for (at, v) in [
15333 (b"100".as_slice(), b"1".as_slice()),
15334 (b"200", b"2"),
15335 (b"300", b"3"),
15336 (b"400", b"4"),
15337 ] {
15338 f.run(&[b"TS.ADD", b"t", at, v]);
15339 }
15340 assert_eq!(
15341 f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
15342 "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
15343 *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
15344 );
15345 assert_eq!(
15347 f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
15348 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15349 );
15350 assert_eq!(
15353 f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
15354 "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
15355 );
15356 assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
15358 assert_eq!(
15360 f.run(&[
15361 b"TS.RANGE",
15362 b"t",
15363 b"-",
15364 b"+",
15365 b"FILTER_BY_VALUE",
15366 b"2",
15367 b"3"
15368 ]),
15369 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15370 );
15371 assert_eq!(
15372 f.run(&[
15373 b"TS.RANGE",
15374 b"t",
15375 b"-",
15376 b"+",
15377 b"FILTER_BY_TS",
15378 b"100",
15379 b"400"
15380 ]),
15381 "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
15382 );
15383 assert_eq!(
15385 f.run(&[
15386 b"TS.RANGE",
15387 b"t",
15388 b"-",
15389 b"+",
15390 b"ZZZ",
15391 b"FILTER_BY_TS",
15392 b"400"
15393 ]),
15394 "*1\r\n*2\r\n:400\r\n+4\r\n"
15395 );
15396 assert_eq!(
15398 f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
15399 "*1\r\n*2\r\n:100\r\n+1\r\n"
15400 );
15401 }
15402
15403 #[test]
15405 fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
15406 let mut f = Fixture::new();
15407 for (at, v) in [
15408 (b"100".as_slice(), b"1".as_slice()),
15409 (b"200", b"2"),
15410 (b"300", b"3"),
15411 (b"400", b"4"),
15412 ] {
15413 f.run(&[b"TS.ADD", b"t", at, v]);
15414 }
15415 assert_eq!(
15416 f.run(&[
15417 b"TS.RANGE",
15418 b"t",
15419 b"-",
15420 b"+",
15421 b"AGGREGATION",
15422 b"avg",
15423 b"200"
15424 ]),
15425 "*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"
15426 );
15427 assert_eq!(
15430 f.run(&[
15431 b"TS.RANGE",
15432 b"t",
15433 b"-",
15434 b"+",
15435 b"AGGREGATION",
15436 b"min,max,count",
15437 b"200"
15438 ]),
15439 "*3\r\n\
15440 *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15441 *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15442 *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15443 );
15444 assert_eq!(
15446 f.run(&[
15447 b"TS.RANGE",
15448 b"t",
15449 b"-",
15450 b"+",
15451 b"AGGREGATION",
15452 b"avg",
15453 b"200",
15454 b"BUCKETTIMESTAMP",
15455 b"+"
15456 ]),
15457 "*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"
15458 );
15459 assert_eq!(
15461 f.run(&[
15462 b"TS.RANGE",
15463 b"t",
15464 b"100",
15465 b"400",
15466 b"ALIGN",
15467 b"100",
15468 b"AGGREGATION",
15469 b"sum",
15470 b"200"
15471 ]),
15472 "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15473 );
15474 assert_eq!(
15477 f.run(&[
15478 b"TS.RANGE",
15479 b"t",
15480 b"-",
15481 b"+",
15482 b"AGGREGATION",
15483 b"count",
15484 b"200"
15485 ]),
15486 "*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"
15487 );
15488 assert_eq!(
15489 f.run(&[
15490 b"TS.RANGE",
15491 b"t",
15492 b"-",
15493 b"+",
15494 b"AGGREGATION",
15495 b"count",
15496 b"200",
15497 b"COUNT",
15498 b"1"
15499 ]),
15500 "*1\r\n*2\r\n:0\r\n+1\r\n"
15501 );
15502 }
15503
15504 #[test]
15507 fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15508 let mut f = Fixture::new();
15509 for (at, v) in [
15510 (b"0".as_slice(), b"1".as_slice()),
15511 (b"100", b"2"),
15512 (b"500", b"nan"),
15513 (b"600", b"3"),
15514 ] {
15515 f.run(&[b"TS.ADD", b"g", at, v]);
15516 }
15517 assert_eq!(
15520 f.run(&[
15521 b"TS.RANGE",
15522 b"g",
15523 b"-",
15524 b"+",
15525 b"AGGREGATION",
15526 b"avg",
15527 b"100"
15528 ]),
15529 "*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"
15530 );
15531 assert_eq!(
15533 f.run(&[
15534 b"TS.RANGE",
15535 b"g",
15536 b"-",
15537 b"+",
15538 b"AGGREGATION",
15539 b"sum",
15540 b"100",
15541 b"EMPTY"
15542 ]),
15543 "*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\
15544 *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\
15545 *2\r\n:600\r\n+3\r\n"
15546 );
15547 assert_eq!(
15552 f.run(&[
15553 b"TS.RANGE",
15554 b"g",
15555 b"-",
15556 b"+",
15557 b"AGGREGATION",
15558 b"last",
15559 b"100",
15560 b"EMPTY"
15561 ]),
15562 "*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\
15563 *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\
15564 *2\r\n:600\r\n+3\r\n"
15565 );
15566 assert_eq!(
15567 f.run(&[
15568 b"TS.REVRANGE",
15569 b"g",
15570 b"-",
15571 b"+",
15572 b"AGGREGATION",
15573 b"last",
15574 b"100",
15575 b"EMPTY"
15576 ]),
15577 "*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\
15578 *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\
15579 *2\r\n:0\r\n+1\r\n"
15580 );
15581 assert_eq!(
15584 f.run(&[
15585 b"TS.RANGE",
15586 b"g",
15587 b"500",
15588 b"600",
15589 b"AGGREGATION",
15590 b"last",
15591 b"100",
15592 b"EMPTY"
15593 ]),
15594 "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15595 );
15596 }
15597
15598 #[test]
15601 fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15602 let mut f = Fixture::new();
15603 f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15604 f.run(&[b"SET", b"str", b"x"]);
15605 let cases: &[(&[&[u8]], &str)] = &[
15606 (
15607 &[b"TS.RANGE", b"t"],
15608 "-ERR wrong number of arguments for 'ts.range' command\r\n",
15609 ),
15610 (
15612 &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15613 "-ERR TSDB: the key does not exist\r\n",
15614 ),
15615 (
15616 &[b"TS.RANGE", b"str", b"-", b"+"],
15617 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15618 ),
15619 (
15620 &[b"TS.RANGE", b"t", b"abc", b"+"],
15621 "-ERR TSDB: wrong fromTimestamp\r\n",
15622 ),
15623 (
15624 &[b"TS.RANGE", b"t", b"-", b"abc"],
15625 "-ERR TSDB: wrong toTimestamp\r\n",
15626 ),
15627 (
15628 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15629 "-ERR TSDB: COUNT argument is missing\r\n",
15630 ),
15631 (
15632 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15633 "-ERR TSDB: Couldn't parse COUNT\r\n",
15634 ),
15635 (
15636 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15637 "-ERR TSDB: Invalid COUNT value\r\n",
15638 ),
15639 (
15640 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15641 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15642 ),
15643 (
15644 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15645 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15646 ),
15647 (
15648 &[
15649 b"TS.RANGE",
15650 b"t",
15651 b"-",
15652 b"+",
15653 b"AGGREGATION",
15654 b"nope",
15655 b"100",
15656 ],
15657 "-ERR TSDB: Unknown aggregation type\r\n",
15658 ),
15659 (
15660 &[
15661 b"TS.RANGE",
15662 b"t",
15663 b"-",
15664 b"+",
15665 b"AGGREGATION",
15666 b"avg,,min",
15667 b"100",
15668 ],
15669 "-ERR TSDB: Empty aggregation type in list\r\n",
15670 ),
15671 (
15673 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15674 "-ERR TSDB: Unknown aggregation type\r\n",
15675 ),
15676 (
15677 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15678 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15679 ),
15680 (
15681 &[
15682 b"TS.RANGE",
15683 b"t",
15684 b"-",
15685 b"+",
15686 b"AGGREGATION",
15687 b"avg",
15688 b"100",
15689 b"X",
15690 b"EMPTY",
15691 ],
15692 "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15693 ),
15694 (
15695 &[
15696 b"TS.RANGE",
15697 b"t",
15698 b"-",
15699 b"+",
15700 b"AGGREGATION",
15701 b"avg",
15702 b"100",
15703 b"BUCKETTIMESTAMP",
15704 b"z",
15705 ],
15706 "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15707 ),
15708 (
15709 &[
15710 b"TS.RANGE",
15711 b"t",
15712 b"-",
15713 b"+",
15714 b"AGGREGATION",
15715 b"avg",
15716 b"100",
15717 b"X",
15718 b"Y",
15719 b"BUCKETTIMESTAMP",
15720 b"-",
15721 ],
15722 "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15723 AGGREGATION flag\r\n",
15724 ),
15725 (
15726 &[
15727 b"TS.RANGE",
15728 b"t",
15729 b"-",
15730 b"+",
15731 b"ALIGN",
15732 b"z",
15733 b"AGGREGATION",
15734 b"avg",
15735 b"100",
15736 ],
15737 "-ERR TSDB: unknown ALIGN parameter\r\n",
15738 ),
15739 (
15740 &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15741 "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15742 ),
15743 (
15744 &[
15745 b"TS.RANGE",
15746 b"t",
15747 b"-",
15748 b"+",
15749 b"ALIGN",
15750 b"-",
15751 b"AGGREGATION",
15752 b"avg",
15753 b"100",
15754 ],
15755 "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15756 ),
15757 (
15758 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15759 "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15760 ),
15761 (
15762 &[
15763 b"TS.RANGE",
15764 b"t",
15765 b"-",
15766 b"+",
15767 b"FILTER_BY_VALUE",
15768 b"x",
15769 b"2",
15770 ],
15771 "-ERR TSDB: Couldn't parse MIN\r\n",
15772 ),
15773 (
15774 &[
15775 b"TS.RANGE",
15776 b"t",
15777 b"-",
15778 b"+",
15779 b"FILTER_BY_VALUE",
15780 b"1",
15781 b"y",
15782 ],
15783 "-ERR TSDB: Couldn't parse MAX\r\n",
15784 ),
15785 (
15786 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15787 "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15788 ),
15789 ];
15790 for (argv, want) in cases {
15791 let got = f.run(argv);
15792 assert_eq!(&got, want, "{:?}", argv.last());
15793 }
15794 f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
15798 f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
15799 assert_eq!(
15800 f.run(&[
15801 b"TS.RANGE",
15802 b"wide",
15803 b"-",
15804 b"+",
15805 b"AGGREGATION",
15806 b"avg",
15807 b"1",
15808 b"EMPTY"
15809 ]),
15810 "-ERR TSDB: the requested range holds too many empty buckets\r\n"
15811 );
15812 }
15813
15814 #[test]
15816 fn resp3_writes_a_read_value_as_a_double() {
15817 let mut f = Fixture::new();
15818 f.out = Out::new(Proto::Resp3);
15819 for (at, v) in [
15820 (b"0".as_slice(), b"1".as_slice()),
15821 (b"100", b"2"),
15822 (b"500", b"nan"),
15823 (b"600", b"3"),
15824 ] {
15825 f.run(&[b"TS.ADD", b"g", at, v]);
15826 }
15827 assert_eq!(
15828 f.run(&[
15829 b"TS.RANGE",
15830 b"g",
15831 b"0",
15832 b"100",
15833 b"AGGREGATION",
15834 b"avg,min",
15835 b"200"
15836 ]),
15837 "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
15838 );
15839 assert_eq!(
15840 f.run(&[
15841 b"TS.RANGE",
15842 b"g",
15843 b"500",
15844 b"600",
15845 b"AGGREGATION",
15846 b"last",
15847 b"100",
15848 b"EMPTY"
15849 ]),
15850 "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
15851 );
15852 }
15853
15854 fn joined() -> Fixture {
15857 let mut f = Fixture::new();
15858 f.run(&[b"TS.CREATE", b"z"]);
15859 for (at, v) in [
15860 (b"10".as_slice(), b"1".as_slice()),
15861 (b"20", b"2"),
15862 (b"40", b"4"),
15863 (b"50", b"5"),
15864 ] {
15865 f.run(&[b"TS.ADD", b"x", at, v]);
15866 }
15867 for (at, v) in [
15868 (b"20".as_slice(), b"20".as_slice()),
15869 (b"30", b"30"),
15870 (b"50", b"50"),
15871 (b"60", b"60"),
15872 ] {
15873 f.run(&[b"TS.ADD", b"y", at, v]);
15874 }
15875 f
15876 }
15877
15878 #[test]
15882 fn an_nrange_joins_its_keys_on_the_timestamp() {
15883 let mut f = joined();
15884 assert_eq!(
15886 f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
15887 "*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\
15888 *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
15889 );
15890 assert_eq!(
15892 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
15893 "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
15894 *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15895 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15896 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15897 *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
15898 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15899 );
15900 assert_eq!(
15903 f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
15904 "*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"
15905 );
15906 assert_eq!(
15907 f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
15908 "*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"
15909 );
15910 assert_eq!(
15913 f.run(&[
15914 b"TS.NREVRANGE",
15915 b"2",
15916 b"x",
15917 b"y",
15918 b"-",
15919 b"+",
15920 b"COUNT",
15921 b"1"
15922 ]),
15923 "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15924 );
15925 assert_eq!(
15926 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
15927 "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
15928 );
15929 assert_eq!(
15931 f.run(&[
15932 b"TS.NRANGE",
15933 b"2",
15934 b"x",
15935 b"y",
15936 b"-",
15937 b"+",
15938 b"FILTER_BY_VALUE",
15939 b"2",
15940 b"30"
15941 ]),
15942 "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15943 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15944 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15945 *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
15946 );
15947 }
15948
15949 #[test]
15953 fn an_nrange_aggregation_names_one_reduction_a_key() {
15954 let mut f = joined();
15955 assert_eq!(
15956 f.run(&[
15957 b"TS.NRANGE",
15958 b"2",
15959 b"x",
15960 b"y",
15961 b"-",
15962 b"+",
15963 b"AGGREGATION",
15964 b"sum",
15965 b"sum",
15966 b"20"
15967 ]),
15968 "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
15969 *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
15970 *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
15971 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15972 );
15973 assert_eq!(
15975 f.run(&[
15976 b"TS.NRANGE",
15977 b"2",
15978 b"x",
15979 b"y",
15980 b"-",
15981 b"+",
15982 b"AGGREGATION",
15983 b"sum,count",
15984 b"avg",
15985 b"20"
15986 ]),
15987 "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
15988 *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
15989 *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
15990 *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
15991 );
15992 assert_eq!(
15995 f.run(&[
15996 b"TS.NRANGE",
15997 b"2",
15998 b"x",
15999 b"y",
16000 b"-",
16001 b"+",
16002 b"AGGREGATION",
16003 b"avg",
16004 b"sum",
16005 b"100",
16006 b"EMPTY",
16007 b"BUCKETTIMESTAMP",
16008 b"end"
16009 ]),
16010 "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
16011 );
16012 assert_eq!(
16015 f.run(&[
16016 b"TS.NRANGE",
16017 b"2",
16018 b"x",
16019 b"y",
16020 b"-",
16021 b"+",
16022 b"AGGREGATION",
16023 b"avg",
16024 b"COUNT",
16025 b"100"
16026 ]),
16027 "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
16028 );
16029 }
16030
16031 #[test]
16034 fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
16035 let mut f = joined();
16036 f.run(&[b"SET", b"str", b"hi"]);
16037 let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
16038 let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
16039 must be equal to numkeys\r\n";
16040 let cases: &[(&[&[u8]], &str)] = &[
16041 (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
16042 (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
16043 (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
16044 (
16047 &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
16048 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16049 ),
16050 (
16051 &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
16052 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16053 ),
16054 (
16057 &[
16058 b"TS.NRANGE",
16059 b"2",
16060 b"x",
16061 b"y",
16062 b"abc",
16063 b"+",
16064 b"AGGREGATION",
16065 b"nope",
16066 b"sum",
16067 b"100",
16068 ],
16069 "-ERR TSDB: Unknown aggregation type\r\n",
16070 ),
16071 (
16072 &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
16073 "-ERR TSDB: wrong fromTimestamp\r\n",
16074 ),
16075 (
16076 &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
16077 "-ERR TSDB: wrong toTimestamp\r\n",
16078 ),
16079 (
16083 &[
16084 b"TS.NRANGE",
16085 b"2",
16086 b"x",
16087 b"y",
16088 b"-",
16089 b"+",
16090 b"AGGREGATION",
16091 b"avg",
16092 ],
16093 numkeys,
16094 ),
16095 (
16096 &[
16097 b"TS.NRANGE",
16098 b"2",
16099 b"x",
16100 b"y",
16101 b"-",
16102 b"+",
16103 b"AGGREGATION",
16104 b"100",
16105 b"sum",
16106 b"100",
16107 ],
16108 numkeys,
16109 ),
16110 (
16111 &[
16112 b"TS.NRANGE",
16113 b"2",
16114 b"x",
16115 b"y",
16116 b"-",
16117 b"+",
16118 b"AGGREGATION",
16119 b"avg",
16120 b"sum",
16121 b"sum",
16122 b"100",
16123 ],
16124 numkeys,
16125 ),
16126 (
16127 &[
16128 b"TS.NRANGE",
16129 b"2",
16130 b"x",
16131 b"y",
16132 b"-",
16133 b"+",
16134 b"AGGREGATION",
16135 b"avg",
16136 b"sum",
16137 b"abc",
16138 ],
16139 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16140 ),
16141 (
16142 &[
16143 b"TS.NRANGE",
16144 b"2",
16145 b"x",
16146 b"y",
16147 b"-",
16148 b"+",
16149 b"AGGREGATION",
16150 b"avg",
16151 b"sum",
16152 b"0",
16153 ],
16154 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16155 ),
16156 (
16159 &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
16160 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16161 ),
16162 (
16163 &[
16164 b"TS.NRANGE",
16165 b"1",
16166 b"x",
16167 b"-",
16168 b"+",
16169 b"AGGREGATION",
16170 b"100",
16171 b"200",
16172 ],
16173 "-ERR TSDB: Unknown aggregation type\r\n",
16174 ),
16175 (
16177 &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
16178 "-ERR TSDB: the key does not exist\r\n",
16179 ),
16180 (
16181 &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
16182 "-ERR WRONGTYPE Operation against a key \
16183 holding the wrong kind of value\r\n",
16184 ),
16185 ];
16186 for (argv, want) in cases {
16187 let got = f.run(argv);
16188 assert_eq!(&got, want, "{argv:?}");
16189 }
16190 }
16191
16192 #[test]
16194 fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
16195 let mut f = joined();
16196 assert_eq!(
16197 f.run(&[b"TS.READ", b"x", b"-"]),
16198 "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
16199 *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16200 );
16201 assert_eq!(
16204 f.run(&[b"TS.READ", b"x", b"+"]),
16205 "*1\r\n*2\r\n:50\r\n+5\r\n"
16206 );
16207 assert_eq!(
16208 f.run(&[b"TS.READ", b"x", b"25"]),
16209 "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16210 );
16211 assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
16214 assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
16215 assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
16216 assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
16217 assert_eq!(
16221 f.run(&[b"TS.READ", b"x", b"abc"]),
16222 "-TSDB: invalid timestamp\r\n"
16223 );
16224 assert_eq!(
16225 f.run(&[b"TS.READ", b"x", b"-1"]),
16226 "-TSDB: invalid timestamp\r\n"
16227 );
16228 f.run(&[b"SET", b"str", b"hi"]);
16229 assert_eq!(
16230 f.run(&[b"TS.READ", b"str", b"-"]),
16231 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
16232 );
16233 assert_eq!(
16236 f.run(&[b"TS.READ", b"x"]),
16237 "-ERR wrong number of arguments for 'ts.read' command\r\n"
16238 );
16239 assert_eq!(
16240 f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
16241 "-ERR wrong number of arguments for 'ts.read' command\r\n"
16242 );
16243 }
16244
16245 #[test]
16248 fn getkeys_reads_the_count_of_a_joined_read() {
16249 let mut f = Fixture::new();
16250 assert_eq!(
16251 f.run(&[
16252 b"COMMAND",
16253 b"GETKEYS",
16254 b"TS.NRANGE",
16255 b"2",
16256 b"a",
16257 b"b",
16258 b"-",
16259 b"+"
16260 ]),
16261 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
16262 );
16263 assert_eq!(
16264 f.run(&[
16265 b"COMMAND",
16266 b"GETKEYS",
16267 b"TS.NREVRANGE",
16268 b"1",
16269 b"a",
16270 b"-",
16271 b"+"
16272 ]),
16273 "*1\r\n$1\r\na\r\n"
16274 );
16275 for n in [b"0".as_slice(), b"9", b"abc"] {
16278 assert_eq!(
16279 f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
16280 "-ERR Invalid arguments specified for command\r\n"
16281 );
16282 }
16283 }
16284
16285 fn labelled() -> Fixture {
16287 let mut f = Fixture::new();
16288 f.run(&[
16289 b"TS.CREATE",
16290 b"a",
16291 b"LABELS",
16292 b"room",
16293 b"kitchen",
16294 b"x",
16295 b"1",
16296 ]);
16297 f.run(&[
16298 b"TS.CREATE",
16299 b"b",
16300 b"LABELS",
16301 b"room",
16302 b"bedroom",
16303 b"x",
16304 b"2",
16305 ]);
16306 f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
16307 f.run(&[b"TS.CREATE", b"d"]);
16308 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
16309 f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
16310 f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
16311 f
16312 }
16313
16314 #[test]
16317 fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
16318 let mut f = labelled();
16319 let cases: &[(&[&[u8]], &str)] = &[
16320 (
16323 &[b"TS.QUERYINDEX", b"room=kitchen"],
16324 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16325 ),
16326 (
16327 &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
16328 "*1\r\n$1\r\na\r\n",
16329 ),
16330 (
16331 &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
16332 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
16333 ),
16334 (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
16337 (
16339 &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
16340 "*1\r\n$1\r\nc\r\n",
16341 ),
16342 (
16343 &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
16344 "*1\r\n$1\r\na\r\n",
16345 ),
16346 (
16347 &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
16348 "-ERR TSDB: please provide at least one matcher\r\n",
16349 ),
16350 (
16353 &[b"TS.QUERYINDEX", b"room==kitchen"],
16354 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16355 ),
16356 (
16357 &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
16358 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16359 ),
16360 (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
16361 (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
16364 (
16365 &[b"TS.QUERYINDEX", b"=(1)"],
16366 "-ERR TSDB: failed parsing labels\r\n",
16367 ),
16368 (
16369 &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
16370 "-ERR TSDB: failed parsing labels\r\n",
16371 ),
16372 (
16373 &[b"TS.QUERYINDEX", b"room=(kitchen"],
16374 "-ERR TSDB: failed parsing labels\r\n",
16375 ),
16376 (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
16377 (
16378 &[b"TS.QUERYINDEX", b"nonsense"],
16379 "-ERR TSDB: failed parsing labels\r\n",
16380 ),
16381 (
16383 &[b"TS.QUERYINDEX", b"room!=kitchen"],
16384 "-ERR TSDB: please provide at least one matcher\r\n",
16385 ),
16386 (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
16388 (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
16389 (
16390 &[b"TS.QUERYINDEX"],
16391 "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
16392 ),
16393 ];
16394 for (argv, want) in cases {
16395 let got = f.run(argv);
16396 assert_eq!(&got, want, "{:?}", argv.last());
16397 }
16398 }
16399
16400 #[test]
16402 fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
16403 let mut f = labelled();
16404 let cases: &[(&[&[u8]], &str)] = &[
16405 (
16406 &[b"TS.QUERYLABELS", b"LABELS"],
16407 "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16408 ),
16409 (
16410 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16411 "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16412 ),
16413 (
16414 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16415 "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16416 ),
16417 (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16420 (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16421 (
16422 &[b"TS.QUERYLABELS", b"VALUES"],
16423 "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16424 ),
16425 (
16426 &[b"TS.QUERYLABELS", b"ZZZ"],
16427 "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16428 ),
16429 (
16430 &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16431 "-ERR TSDB: unknown argument, expected FILTER\r\n",
16432 ),
16433 (
16434 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16435 "-ERR TSDB: FILTER given with no filter expressions\r\n",
16436 ),
16437 (
16441 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16442 "-ERR TSDB: please provide at least one matcher\r\n",
16443 ),
16444 (
16445 &[
16446 b"TS.QUERYLABELS",
16447 b"LABELS",
16448 b"FILTER",
16449 b"room=kitchen",
16450 b"x=",
16451 ],
16452 "*1\r\n$4\r\nroom\r\n",
16453 ),
16454 ];
16455 for (argv, want) in cases {
16456 let got = f.run(argv);
16457 assert_eq!(&got, want, "{:?}", argv.last());
16458 }
16459 }
16460
16461 #[test]
16464 fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16465 let mut f = labelled();
16466 let cases: &[(&[&[u8]], &str)] = &[
16467 (
16470 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16471 "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16472 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16473 ),
16474 (
16475 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16476 "*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\
16477 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16478 *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",
16479 ),
16480 (
16482 &[
16483 b"TS.MGET",
16484 b"SELECTED_LABELS",
16485 b"x",
16486 b"FILTER",
16487 b"room=kitchen",
16488 ],
16489 "*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\
16490 *2\r\n:100\r\n+1.5\r\n\
16491 *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",
16492 ),
16493 (
16496 &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16497 "*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",
16498 ),
16499 (
16500 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16501 "*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\
16502 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16503 ),
16504 (
16507 &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16508 "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16509 ),
16510 (
16511 &[b"TS.MGET", b"a", b"b", b"c"],
16512 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16513 ),
16514 (
16515 &[b"TS.MGET", b"FILTER"],
16516 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16517 ),
16518 (
16521 &[
16522 b"TS.MGET",
16523 b"WITHLABELS",
16524 b"SELECTED_LABELS",
16525 b"x",
16526 b"FILTER",
16527 b"bad",
16528 ],
16529 "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16530 ),
16531 (
16532 &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16533 "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16534 ),
16535 ];
16536 for (argv, want) in cases {
16537 let got = f.run(argv);
16538 assert_eq!(&got, want, "{:?}", argv.last());
16539 }
16540 }
16541
16542 #[test]
16545 fn resp3_writes_the_label_surface_as_sets_and_maps() {
16546 let mut f = labelled();
16547 f.out = Out::new(Proto::Resp3);
16548 let cases: &[(&[&[u8]], &str)] = &[
16549 (
16550 &[b"TS.QUERYINDEX", b"room=kitchen"],
16551 "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16552 ),
16553 (
16554 &[b"TS.QUERYLABELS", b"LABELS"],
16555 "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16556 ),
16557 (
16558 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16559 "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16560 ),
16561 (
16564 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16565 "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16566 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16567 ),
16568 (
16569 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16570 "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16571 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16572 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16573 ),
16574 (
16575 &[
16576 b"TS.MGET",
16577 b"SELECTED_LABELS",
16578 b"x",
16579 b"FILTER",
16580 b"room=kitchen",
16581 ],
16582 "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16583 *2\r\n:100\r\n,1.5\r\n\
16584 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16585 ),
16586 (
16589 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16590 "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16591 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16592 ),
16593 ];
16594 for (argv, want) in cases {
16595 let got = f.run(argv);
16596 assert_eq!(&got, want, "{:?}", argv.last());
16597 }
16598 }
16599
16600 fn spanned() -> Fixture {
16603 let mut f = labelled();
16604 f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16605 f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16606 f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16607 f
16608 }
16609
16610 #[test]
16613 fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16614 let mut f = spanned();
16615 let cases: &[(&[&[u8]], &str)] = &[
16616 (
16617 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16618 "*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\
16619 *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",
16620 ),
16621 (
16624 &[
16625 b"TS.MREVRANGE",
16626 b"-",
16627 b"+",
16628 b"WITHLABELS",
16629 b"FILTER",
16630 b"room=kitchen",
16631 ],
16632 "*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\
16633 *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\
16634 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16635 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16636 ),
16637 (
16640 &[
16641 b"TS.MRANGE",
16642 b"-",
16643 b"+",
16644 b"SELECTED_LABELS",
16645 b"x",
16646 b"FILTER",
16647 b"room=kitchen",
16648 ],
16649 "*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\
16650 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16651 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16652 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16653 ),
16654 (
16657 &[
16658 b"TS.MRANGE",
16659 b"-",
16660 b"+",
16661 b"FILTER",
16662 b"room=kitchen",
16663 b"GROUPBY",
16664 b"room",
16665 b"REDUCE",
16666 b"sum",
16667 ],
16668 "*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\
16669 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16670 ),
16671 (
16674 &[
16675 b"TS.MRANGE",
16676 b"-",
16677 b"+",
16678 b"WITHLABELS",
16679 b"FILTER",
16680 b"room=kitchen",
16681 b"GROUPBY",
16682 b"room",
16683 b"REDUCE",
16684 b"max",
16685 ],
16686 "*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\
16687 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16688 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16689 *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",
16690 ),
16691 (
16693 &[
16694 b"TS.MREVRANGE",
16695 b"-",
16696 b"+",
16697 b"COUNT",
16698 b"1",
16699 b"FILTER",
16700 b"room=kitchen",
16701 b"GROUPBY",
16702 b"room",
16703 b"REDUCE",
16704 b"count",
16705 ],
16706 "*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",
16707 ),
16708 (
16710 &[
16711 b"TS.MRANGE",
16712 b"-",
16713 b"+",
16714 b"FILTER",
16715 b"room=kitchen",
16716 b"GROUPBY",
16717 b"nope",
16718 b"REDUCE",
16719 b"sum",
16720 ],
16721 "*0\r\n",
16722 ),
16723 (
16724 &[
16725 b"TS.MRANGE",
16726 b"-",
16727 b"+",
16728 b"AGGREGATION",
16729 b"sum,avg",
16730 b"100",
16731 b"FILTER",
16732 b"room=bedroom",
16733 ],
16734 "*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",
16735 ),
16736 (
16738 &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16739 "-ERR TSDB: missing FILTER argument\r\n",
16740 ),
16741 (
16742 &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16743 "-ERR TSDB: missing labels for filter argument\r\n",
16744 ),
16745 (
16746 &[
16747 b"TS.MRANGE",
16748 b"-",
16749 b"+",
16750 b"GROUPBY",
16751 b"room",
16752 b"REDUCE",
16753 b"sum",
16754 b"FILTER",
16755 b"room=kitchen",
16756 ],
16757 "-ERR TSDB: GROUPBY should always come after filter\r\n",
16758 ),
16759 (
16762 &[
16763 b"TS.MRANGE",
16764 b"-",
16765 b"+",
16766 b"FILTER",
16767 b"room=kitchen",
16768 b"GROUPBY",
16769 b"room",
16770 b"REDUCE",
16771 b"sum",
16772 b"x",
16773 ],
16774 "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16775 ),
16776 (
16778 &[
16779 b"TS.MRANGE",
16780 b"-",
16781 b"+",
16782 b"FILTER",
16783 b"nope",
16784 b"GROUPBY",
16785 b"room",
16786 b"REDUCE",
16787 b"sum",
16788 b"x",
16789 ],
16790 "-ERR TSDB: failed parsing labels\r\n",
16791 ),
16792 (
16793 &[
16794 b"TS.MRANGE",
16795 b"-",
16796 b"+",
16797 b"FILTER",
16798 b"room=kitchen",
16799 b"GROUPBY",
16800 b"room",
16801 b"REDUCE",
16802 b"twa",
16803 ],
16804 "-ERR TSDB: Invalid reducer type\r\n",
16805 ),
16806 (
16807 &[
16808 b"TS.MRANGE",
16809 b"-",
16810 b"+",
16811 b"AGGREGATION",
16812 b"sum,avg",
16813 b"100",
16814 b"FILTER",
16815 b"room=kitchen",
16816 b"GROUPBY",
16817 b"room",
16818 b"REDUCE",
16819 b"sum",
16820 ],
16821 "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
16822 ),
16823 (
16826 &[
16827 b"TS.MRANGE",
16828 b"-",
16829 b"+",
16830 b"SELECTED_LABELS",
16831 b"COUNT",
16832 b"FILTER",
16833 b"room=kitchen",
16834 ],
16835 "-ERR TSDB: Couldn't parse COUNT\r\n",
16836 ),
16837 ];
16838 for (argv, want) in cases {
16839 let got = f.run(argv);
16840 assert_eq!(&got, want, "{argv:?}");
16841 }
16842 }
16843
16844 #[test]
16847 fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
16848 let mut f = spanned();
16849 f.out = Out::new(Proto::Resp3);
16850 let cases: &[(&[&[u8]], &str)] = &[
16851 (
16852 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
16853 "%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\
16854 *1\r\n*2\r\n:200\r\n,2\r\n",
16855 ),
16856 (
16859 &[
16860 b"TS.MRANGE",
16861 b"-",
16862 b"+",
16863 b"AGGREGATION",
16864 b"sum,avg",
16865 b"100",
16866 b"FILTER",
16867 b"room=bedroom",
16868 ],
16869 "%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\
16870 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
16871 ),
16872 (
16873 &[
16874 b"TS.MRANGE",
16875 b"-",
16876 b"+",
16877 b"FILTER",
16878 b"room=kitchen",
16879 b"GROUPBY",
16880 b"room",
16881 b"REDUCE",
16882 b"sum",
16883 ],
16884 "%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\
16885 $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\
16886 *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",
16887 ),
16888 (
16891 &[
16892 b"TS.MRANGE",
16893 b"-",
16894 b"+",
16895 b"WITHLABELS",
16896 b"FILTER",
16897 b"room=kitchen",
16898 b"GROUPBY",
16899 b"room",
16900 b"REDUCE",
16901 b"max",
16902 ],
16903 "%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\
16904 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
16905 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16906 *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",
16907 ),
16908 (
16909 &[
16910 b"TS.MRANGE",
16911 b"-",
16912 b"+",
16913 b"FILTER",
16914 b"room=kitchen",
16915 b"GROUPBY",
16916 b"nope",
16917 b"REDUCE",
16918 b"sum",
16919 ],
16920 "%0\r\n",
16921 ),
16922 ];
16923 for (argv, want) in cases {
16924 let got = f.run(argv);
16925 assert_eq!(&got, want, "{argv:?}");
16926 }
16927 }
16928
16929 #[test]
16931 fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
16932 let mut f = Fixture::new();
16933 f.run(&[b"TS.CREATE", b"src"]);
16934 f.run(&[b"TS.CREATE", b"dst"]);
16935 f.run(&[b"SET", b"plain", b"v"]);
16936 let cases: &[(&[&[u8]], &str)] = &[
16937 (
16941 &[
16942 b"TS.CREATERULE",
16943 b"src",
16944 b"dst",
16945 b"AGGREGATION",
16946 b"nope",
16947 b"x",
16948 ],
16949 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16950 ),
16951 (
16952 &[
16953 b"TS.CREATERULE",
16954 b"src",
16955 b"dst",
16956 b"AGGREGATION",
16957 b"nope",
16958 b"10",
16959 ],
16960 "-ERR TSDB: Unknown aggregation type\r\n",
16961 ),
16962 (
16963 &[
16964 b"TS.CREATERULE",
16965 b"src",
16966 b"dst",
16967 b"AGGREGATION",
16968 b"avg",
16969 b"0",
16970 ],
16971 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16972 ),
16973 (
16974 &[
16975 b"TS.CREATERULE",
16976 b"src",
16977 b"dst",
16978 b"AGGREGATION",
16979 b"avg",
16980 b"10",
16981 b"x",
16982 ],
16983 "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
16984 ),
16985 (
16986 &[
16987 b"TS.CREATERULE",
16988 b"src",
16989 b"src",
16990 b"AGGREGATION",
16991 b"avg",
16992 b"10",
16993 ],
16994 "-ERR TSDB: the source key and destination key should be different\r\n",
16995 ),
16996 (
17000 &[
17001 b"TS.CREATERULE",
17002 b"nope",
17003 b"plain",
17004 b"AGGREGATION",
17005 b"avg",
17006 b"10",
17007 ],
17008 "-ERR TSDB: the key does not exist\r\n",
17009 ),
17010 (
17011 &[
17012 b"TS.CREATERULE",
17013 b"src",
17014 b"nope",
17015 b"AGGREGATION",
17016 b"avg",
17017 b"10",
17018 ],
17019 "-ERR TSDB: the key does not exist\r\n",
17020 ),
17021 (
17024 &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
17025 "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
17026 ),
17027 (
17028 &[
17029 b"TS.CREATERULE",
17030 b"src",
17031 b"dst",
17032 b"AGGREGATION",
17033 b"avg",
17034 b"10",
17035 ],
17036 "+OK\r\n",
17037 ),
17038 (
17041 &[
17042 b"TS.CREATERULE",
17043 b"src",
17044 b"dst",
17045 b"AGGREGATION",
17046 b"avg",
17047 b"10",
17048 ],
17049 "-ERR TSDB: the destination key already has a src rule\r\n",
17050 ),
17051 (
17054 &[
17055 b"TS.CREATERULE",
17056 b"dst",
17057 b"src",
17058 b"AGGREGATION",
17059 b"avg",
17060 b"10",
17061 ],
17062 "-ERR TSDB: the source key already has a source rule\r\n",
17063 ),
17064 (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
17065 (
17066 &[b"TS.DELETERULE", b"src", b"dst"],
17067 "-ERR TSDB: compaction rule does not exist\r\n",
17068 ),
17069 (
17073 &[b"TS.DELETERULE", b"src", b"nope"],
17074 "-ERR TSDB: compaction rule does not exist\r\n",
17075 ),
17076 (
17077 &[b"TS.DELETERULE", b"nope", b"dst"],
17078 "-ERR TSDB: the key does not exist\r\n",
17079 ),
17080 ];
17081 for (argv, want) in cases {
17082 let got = f.run(argv);
17083 assert_eq!(&got, want, "{argv:?}");
17084 }
17085 }
17086
17087 #[test]
17089 fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
17090 let mut f = Fixture::new();
17091 f.run(&[b"TS.CREATE", b"src"]);
17092 f.run(&[b"TS.CREATE", b"dst"]);
17093 f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
17096 f.run(&[
17097 b"TS.CREATERULE",
17098 b"src",
17099 b"dst",
17100 b"AGGREGATION",
17101 b"sum",
17102 b"100",
17103 ]);
17104 f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
17105 assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
17106 assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
17109 f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
17110 assert_eq!(
17111 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17112 "*1\r\n*2\r\n:0\r\n+2\r\n"
17113 );
17114 f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
17117 assert_eq!(
17118 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17119 "*1\r\n*2\r\n:0\r\n+11\r\n"
17120 );
17121 assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
17124 assert_eq!(
17125 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17126 "*1\r\n*2\r\n:0\r\n+8\r\n"
17127 );
17128 assert_eq!(
17129 f.run(&[b"TS.GET", b"dst", b"LATEST"]),
17130 "*2\r\n:100\r\n+4\r\n"
17131 );
17132 assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
17134 f.run(&[b"DEL", b"dst"]);
17135 assert_eq!(
17136 f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
17137 "-ERR TSDB: compaction rule does not exist\r\n"
17138 );
17139 }
17140
17141 #[test]
17144 fn xadd_ids_only_ever_go_up() {
17145 let mut f = Fixture::new();
17146 assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
17148 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
17150 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
17151 assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
17152 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17153
17154 assert!(
17155 f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
17156 .contains("equal or smaller")
17157 );
17158 assert!(
17159 f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
17160 .contains("must be greater than 0-0")
17161 );
17162 assert!(
17163 f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
17164 .contains("Invalid stream ID")
17165 );
17166 assert!(
17169 f.run(&[b"XADD", b"s", b"*", b"a"])
17170 .contains("wrong number of arguments")
17171 );
17172
17173 assert_eq!(
17176 f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
17177 "$-1\r\n"
17178 );
17179 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
17180 assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
17181 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
17182 }
17183
17184 #[test]
17187 fn trimming_reads_its_options_the_way_redis_does() {
17188 let mut f = Fixture::new();
17189 for i in 1..=10u32 {
17190 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17191 }
17192 assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
17193 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17194 assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
17195 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17196
17197 assert!(
17201 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
17202 .contains("not an integer")
17203 );
17204 assert!(
17205 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
17206 .contains("MAXLEN argument must be >= 0")
17207 );
17208 assert!(
17211 f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
17212 .contains("without specifying a trimming strategy")
17213 );
17214 assert!(
17215 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
17216 .contains("without the special ~ option")
17217 );
17218 assert!(
17219 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
17220 .contains("at the same time are not compatible")
17221 );
17222 assert!(
17224 f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
17225 .contains("syntax error")
17226 );
17227 assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
17228 }
17229
17230 #[test]
17232 fn xrange_looks_the_key_up_before_it_reads_the_count() {
17233 let mut f = Fixture::new();
17234 f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
17235 f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
17236
17237 assert_eq!(
17238 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17239 "*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\
17240 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17241 );
17242 assert_eq!(
17243 f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
17244 "*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"
17245 );
17246 assert_eq!(
17250 f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
17251 "*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\
17252 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17253 );
17254 assert_eq!(
17255 f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
17256 "*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"
17257 );
17258 assert!(
17259 f.run(&[b"XRANGE", b"s", b"(-", b"+"])
17260 .contains("Invalid stream ID")
17261 );
17262
17263 assert_eq!(
17267 f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
17268 "*0\r\n"
17269 );
17270 assert_eq!(
17271 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
17272 "*-1\r\n"
17273 );
17274 f.run(&[b"SET", b"str", b"v"]);
17275 assert!(
17276 f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
17277 .starts_with("-WRONGTYPE")
17278 );
17279 assert_eq!(
17281 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
17282 "*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"
17283 );
17284 }
17285
17286 #[test]
17288 fn a_bad_id_late_in_the_list_stops_the_whole_command() {
17289 let mut f = Fixture::new();
17290 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17291 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17292 assert!(
17293 f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
17294 .contains("Invalid stream ID")
17295 );
17296 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17297 assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
17298 assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
17299 assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
17300 assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
17301 }
17302
17303 #[test]
17305 fn xgroup_has_an_arity_per_subcommand() {
17306 let mut f = Fixture::new();
17307 assert!(
17308 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17309 .contains("requires the key")
17310 );
17311 assert_eq!(
17312 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
17313 "+OK\r\n"
17314 );
17315 assert!(
17318 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17319 .starts_with("-BUSYGROUP")
17320 );
17321 assert_eq!(
17322 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17323 ":1\r\n"
17324 );
17325 assert_eq!(
17326 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17327 ":0\r\n"
17328 );
17329 assert_eq!(
17330 f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
17331 ":0\r\n"
17332 );
17333
17334 let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
17336 assert!(
17337 short.contains("wrong number of arguments for 'xgroup|destroy' command"),
17338 "{short}"
17339 );
17340 let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
17342 assert!(
17343 odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
17344 "{odd}"
17345 );
17346 assert!(
17347 f.run(&[b"XGROUP", b"NOSUCH", b"s"])
17348 .contains("Try XGROUP HELP")
17349 );
17350
17351 assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
17352 assert!(
17353 f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
17354 .starts_with("-NOGROUP")
17355 );
17356 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
17357 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
17358 assert!(
17359 f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
17360 .contains("requires the key")
17361 );
17362 }
17363
17364 #[test]
17366 fn xreadgroup_hands_out_and_xack_takes_back() {
17367 let mut f = Fixture::new();
17368 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17369 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17370 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17371
17372 let first = f.run(&[
17373 b"XREADGROUP",
17374 b"GROUP",
17375 b"g",
17376 b"c1",
17377 b"COUNT",
17378 b"1",
17379 b"STREAMS",
17380 b"s",
17381 b">",
17382 ]);
17383 assert_eq!(
17384 first,
17385 "*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"
17386 );
17387 assert_eq!(
17390 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
17391 "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
17392 );
17393 assert_eq!(
17394 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17395 "*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"
17396 );
17397
17398 assert_eq!(
17399 f.run(&[b"XPENDING", b"s", b"g"]),
17400 "*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"
17401 );
17402 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
17403 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
17404 assert_eq!(
17406 f.run(&[b"XPENDING", b"s", b"g"]),
17407 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
17408 );
17409
17410 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17413 f.run(&[b"XDEL", b"s", b"2-1"]);
17414 assert_eq!(
17415 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17416 "*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"
17417 );
17418
17419 assert!(
17422 f.run(&[
17423 b"XREADGROUP",
17424 b"GROUP",
17425 b"nope",
17426 b"c",
17427 b"STREAMS",
17428 b"s",
17429 b"+"
17430 ])
17431 .starts_with("-NOGROUP")
17432 );
17433 assert!(
17434 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17435 .contains("meaningless in the context of XREADGROUP")
17436 );
17437 assert!(
17438 f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17439 .contains("only supported by XREADGROUP")
17440 );
17441 assert!(
17442 f.run(&[
17443 b"XREADGROUP",
17444 b"GROUP",
17445 b"g",
17446 b"c",
17447 b"STREAMS",
17448 b"s",
17449 b"a",
17450 b"b"
17451 ])
17452 .contains("Unbalanced 'xreadgroup' list of streams")
17453 );
17454 }
17455
17456 #[test]
17459 fn xread_with_no_block_writes_the_null_itself() {
17460 let mut f = Fixture::new();
17461 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17462 assert_eq!(
17463 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17464 "*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"
17465 );
17466 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17469 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17470 f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17471 assert_eq!(
17472 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17473 "*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"
17474 );
17475 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17477 assert_eq!(
17479 f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17480 "*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"
17481 );
17482 assert_eq!(
17485 f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17486 "*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"
17487 );
17488 assert!(
17490 f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17491 .contains("not an integer")
17492 );
17493 assert!(
17494 f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17495 .contains("timeout is negative")
17496 );
17497 assert!(
17498 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17499 .contains("Unbalanced 'xread' list of streams")
17500 );
17501 }
17502
17503 #[test]
17505 fn a_blocked_xread_wakes_on_the_next_entry() {
17506 let mut f = Fixture::new();
17507 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17508 let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17509 assert_eq!(flow, Flow::Block);
17510 assert!(reply.is_empty());
17511
17512 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17515 assert_eq!(flow, Flow::Block);
17516 assert_eq!(f.server.parked(), 2);
17517
17518 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17519 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";
17520 for at in 0..2 {
17521 let mut out = Out::new(Proto::Resp2);
17522 assert!(f.server.serve_waiter(at, 0, &mut out));
17523 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17524 }
17525
17526 f.server.forget_waiters(7);
17529 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17530 assert_eq!(flow, Flow::Block);
17531 let mut out = Out::new(Proto::Resp2);
17532 assert!(!f.server.serve_waiter(0, 0, &mut out));
17533 assert!(out.as_slice().is_empty());
17534 assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
17535 assert_eq!(
17536 core::str::from_utf8(out.as_slice()).expect("ascii"),
17537 "*-1\r\n"
17538 );
17539 }
17540
17541 #[test]
17543 fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17544 let mut f = Fixture::new();
17545 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17546 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17547 let (flow, _) = f.flow(&[
17548 b"XREADGROUP",
17549 b"GROUP",
17550 b"g",
17551 b"c",
17552 b"BLOCK",
17553 b"0",
17554 b"STREAMS",
17555 b"s",
17556 b">",
17557 ]);
17558 assert_eq!(flow, Flow::Block);
17559
17560 f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17561 let mut out = Out::new(Proto::Resp2);
17562 assert!(f.server.serve_waiter(0, 0, &mut out));
17563 assert_eq!(
17566 core::str::from_utf8(out.as_slice()).expect("ascii"),
17567 "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17568 );
17569 }
17570
17571 #[test]
17573 fn xclaim_reads_ids_until_one_will_not_parse() {
17574 let mut f = Fixture::new();
17575 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17576 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17577 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17578 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17579
17580 assert!(
17583 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17584 .contains("Unrecognized XCLAIM option '-'")
17585 );
17586 assert_eq!(
17587 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17588 "*1\r\n$3\r\n1-1\r\n"
17589 );
17590 f.run(&[b"XDEL", b"s", b"2-1"]);
17593 assert_eq!(
17594 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17595 "*0\r\n"
17596 );
17597 assert!(
17598 f.run(&[b"XPENDING", b"s", b"g"])
17599 .starts_with("*4\r\n:1\r\n")
17600 );
17601 assert!(
17602 f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17603 .starts_with("-NOGROUP")
17604 );
17605 assert!(
17606 f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17607 .contains("Invalid min-idle-time argument for XCLAIM")
17608 );
17609 }
17610
17611 #[test]
17613 fn xautoclaim_reports_what_it_dropped() {
17614 let mut f = Fixture::new();
17615 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17616 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17617 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17618 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17619 f.run(&[b"XDEL", b"s", b"1-1"]);
17620
17621 assert_eq!(
17624 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17625 "*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"
17626 );
17627 assert!(
17628 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17629 .contains("COUNT must be > 0")
17630 );
17631 assert!(
17632 f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17633 .starts_with("-NOGROUP")
17634 );
17635 }
17636
17637 #[test]
17639 fn xdelex_answers_one_integer_an_id() {
17640 let mut f = Fixture::new();
17641 for i in 1..=4 {
17642 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17643 }
17644 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17645 f.run(&[
17646 b"XREADGROUP",
17647 b"GROUP",
17648 b"g",
17649 b"c",
17650 b"COUNT",
17651 b"2",
17652 b"STREAMS",
17653 b"s",
17654 b">",
17655 ]);
17656
17657 assert_eq!(
17659 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17660 "*2\r\n:1\r\n:-1\r\n"
17661 );
17662 assert!(
17665 f.run(&[b"XPENDING", b"s", b"g"])
17666 .starts_with("*4\r\n:2\r\n")
17667 );
17668 assert_eq!(
17670 f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17671 "*1\r\n:1\r\n"
17672 );
17673 assert_eq!(
17675 f.run(&[b"XPENDING", b"s", b"g"]),
17676 "*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"
17677 );
17678
17679 assert_eq!(
17683 f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17684 "*2\r\n:2\r\n:2\r\n"
17685 );
17686
17687 assert_eq!(
17689 f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17690 "*2\r\n:-1\r\n:-1\r\n"
17691 );
17692 assert!(
17694 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17695 .starts_with("-ERR Invalid stream ID")
17696 );
17697 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17698
17699 assert!(
17700 f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17701 .contains("Number of IDs must be a positive integer")
17702 );
17703 assert!(
17704 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17705 .contains("The `numids` parameter must match the number of arguments")
17706 );
17707 assert!(
17710 f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17711 .starts_with("-ERR syntax error")
17712 );
17713 assert!(
17714 f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17715 .starts_with("-ERR syntax error")
17716 );
17717 f.run(&[b"SET", b"str", b"v"]);
17719 assert!(
17720 f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17721 .starts_with("-WRONGTYPE")
17722 );
17723 }
17724
17725 #[test]
17727 fn xackdel_reports_what_the_group_was_holding() {
17728 let mut f = Fixture::new();
17729 for i in 1..=3 {
17730 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17731 }
17732 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17733 f.run(&[
17734 b"XREADGROUP",
17735 b"GROUP",
17736 b"g",
17737 b"c",
17738 b"COUNT",
17739 b"1",
17740 b"STREAMS",
17741 b"s",
17742 b">",
17743 ]);
17744
17745 assert_eq!(
17749 f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17750 "*2\r\n:1\r\n:-1\r\n"
17751 );
17752 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17753
17754 assert_eq!(
17756 f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17757 "*1\r\n:-1\r\n"
17758 );
17759 assert_eq!(
17760 f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17761 "*1\r\n:-1\r\n"
17762 );
17763
17764 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17767 f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17768 assert_eq!(
17769 f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17770 "*1\r\n:2\r\n"
17771 );
17772 assert_eq!(
17773 f.run(&[b"XPENDING", b"s", b"g"]),
17774 "*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"
17775 );
17776 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17777 }
17778
17779 #[test]
17781 fn xnack_releases_an_entry_for_the_next_claim() {
17782 let mut f = Fixture::new();
17783 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17784 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17785 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17786 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17787 f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17790
17791 assert_eq!(
17792 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
17793 ":1\r\n"
17794 );
17795 assert_eq!(
17799 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
17800 "*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"
17801 );
17802 assert_eq!(
17804 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17805 "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
17806 );
17807 assert_eq!(
17809 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17810 "*-1\r\n"
17811 );
17812 assert_eq!(
17814 f.run(&[
17815 b"XAUTOCLAIM",
17816 b"s",
17817 b"g",
17818 b"c2",
17819 b"99999999",
17820 b"-",
17821 b"JUSTID"
17822 ]),
17823 "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
17824 );
17825
17826 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17830 assert!(
17831 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17832 .contains(":-1\r\n:1\r\n")
17833 );
17834 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17836 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17837 assert!(
17838 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17839 .contains(":-1\r\n:0\r\n")
17840 );
17841 f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
17843 assert!(
17844 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17845 .contains(":9223372036854775807\r\n")
17846 );
17847 f.run(&[
17848 b"XNACK",
17849 b"s",
17850 b"g",
17851 b"FATAL",
17852 b"IDS",
17853 b"1",
17854 b"1-1",
17855 b"RETRYCOUNT",
17856 b"3",
17857 ]);
17858 assert!(
17859 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17860 .contains(":-1\r\n:3\r\n")
17861 );
17862
17863 f.run(&[b"XACK", b"s", b"g", b"2-1"]);
17867 assert_eq!(
17868 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
17869 ":0\r\n"
17870 );
17871 assert_eq!(
17872 f.run(&[
17873 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
17874 ]),
17875 ":1\r\n"
17876 );
17877 assert!(
17878 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17879 .contains(":-1\r\n:0\r\n")
17880 );
17881 assert_eq!(
17883 f.run(&[
17884 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
17885 ]),
17886 ":0\r\n"
17887 );
17888
17889 assert_eq!(
17892 f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
17893 "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
17894 );
17895 assert!(
17896 f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
17897 .starts_with("-ERR")
17898 );
17899 assert!(
17901 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
17902 .contains("numids must be a positive integer")
17903 );
17904 assert!(
17905 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
17906 .contains("number of IDs doesn't match numids")
17907 );
17908 assert!(
17911 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
17912 .contains("Unrecognized XNACK option '2-1'")
17913 );
17914 }
17915
17916 #[test]
17918 fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
17919 let mut f = Fixture::new();
17920 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17921 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17922 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17923 f.run(&[
17924 b"XREADGROUP",
17925 b"GROUP",
17926 b"g",
17927 b"c1",
17928 b"COUNT",
17929 b"1",
17930 b"STREAMS",
17931 b"s",
17932 b">",
17933 ]);
17934
17935 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17936 assert!(info.starts_with("*20\r\n"), "{info}");
17939 assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
17940 assert!(
17941 info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
17942 "{info}"
17943 );
17944 assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
17945 assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
17946
17947 let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
17948 assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
17949 assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
17950 assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
17951 assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
17952
17953 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
17957 let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
17958 assert!(consumers.starts_with("*2\r\n"), "{consumers}");
17959 assert!(
17960 consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
17961 "{consumers}"
17962 );
17963 let c1 = consumers.find("c1").unwrap();
17965 let c2 = consumers.find("c2").unwrap();
17966 assert!(c1 < c2, "{consumers}");
17967
17968 let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
17969 assert!(full.starts_with("*18\r\n"), "{full}");
17970 assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
17971 assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
17972
17973 assert!(
17974 f.run(&[b"XINFO", b"STREAM", b"missing"])
17975 .contains("no such key")
17976 );
17977 assert!(
17978 f.run(&[b"XINFO", b"GROUPS", b"missing"])
17979 .contains("no such key")
17980 );
17981 assert!(
17982 f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
17983 .starts_with("-NOGROUP")
17984 );
17985 assert!(
17986 f.run(&[b"XINFO", b"NOSUCH", b"s"])
17987 .contains("Try XINFO HELP")
17988 );
17989 assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
17990 assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
17991 }
17992
17993 #[test]
17995 fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
17996 let mut f = Fixture::new();
17997 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17998 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17999 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
18000
18001 let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
18002 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");
18003 assert_eq!(
18004 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
18005 "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
18006 );
18007 assert_eq!(
18009 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
18010 "*0\r\n"
18011 );
18012 assert_eq!(
18013 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
18014 list
18015 );
18016 assert!(
18018 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
18019 .contains("syntax error")
18020 );
18021 assert!(
18022 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
18023 .contains("syntax error")
18024 );
18025 assert_eq!(
18026 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
18027 "*0\r\n"
18028 );
18029 assert!(
18030 f.run(&[b"XPENDING", b"missing", b"g"])
18031 .starts_with("-NOGROUP")
18032 );
18033 }
18034
18035 #[test]
18037 fn xsetid_will_not_go_below_what_is_there() {
18038 let mut f = Fixture::new();
18039 f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
18040 assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
18041 assert_eq!(
18042 f.run(&[
18043 b"XSETID",
18044 b"s",
18045 b"10-1",
18046 b"ENTRIESADDED",
18047 b"7",
18048 b"MAXDELETEDID",
18049 b"9-1"
18050 ]),
18051 "+OK\r\n"
18052 );
18053 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
18054 assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
18055 assert!(
18056 info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
18057 "{info}"
18058 );
18059
18060 assert!(
18061 f.run(&[b"XSETID", b"s", b"1-1"])
18062 .contains("smaller than the target stream top item")
18063 );
18064 assert!(
18065 f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
18066 .contains("entries_added must be positive")
18067 );
18068 assert!(
18069 f.run(&[b"XSETID", b"missing", b"1-1"])
18070 .contains("no such key")
18071 );
18072 }
18073
18074 #[test]
18076 fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
18077 let mut f = Fixture::new();
18078 f.run(&[b"HELLO", b"3"]);
18079 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18080 assert_eq!(
18083 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
18084 "%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"
18085 );
18086 assert_eq!(
18089 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
18090 "*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"
18091 );
18092 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
18093 }
18094
18095 struct Mem {
18101 blobs: Vec<Vec<u8>>,
18102 }
18103
18104 impl yo_kv::cold::Blocks for Mem {
18105 fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
18106 self.blobs.push(bytes.to_vec());
18107 Ok(yo_common::Addr::new(
18108 yo_common::Space::Log,
18109 (self.blobs.len() - 1) as u64,
18110 ))
18111 }
18112
18113 fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
18114 self.blobs
18115 .get(at.offset() as usize)
18116 .map(Vec::as_slice)
18117 .ok_or_else(|| {
18118 yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
18119 })
18120 }
18121
18122 fn bytes(&self) -> u64 {
18123 self.blobs.iter().map(|b| b.len() as u64).sum()
18124 }
18125 }
18126
18127 fn filled(attach: bool) -> (Fixture, usize) {
18131 let mut f = Fixture::new();
18132 if attach {
18133 f.server
18134 .striped(0)
18135 .hold_stripe(0)
18136 .attach(Box::new(Mem { blobs: Vec::new() }));
18137 }
18138 let val = vec![b'v'; 256];
18139 for i in 0..24000u32 {
18140 let k = format!("key:{i:08}");
18141 f.run(&[b"SET", k.as_bytes(), &val]);
18142 }
18143 let full = f.server.memory_bytes();
18144 assert!(full > 3 * 1024 * 1024, "the arena is several segments");
18145 (f, full)
18146 }
18147
18148 fn press(f: &mut Fixture, limit: usize) {
18154 let val = vec![b'v'; 256];
18155 for i in 0..3000u32 {
18156 let k = format!("new:{i:08}");
18157 assert_eq!(
18158 f.run(&[b"SET", k.as_bytes(), &val]),
18159 "+OK\r\n",
18160 "write {i} was refused"
18161 );
18162 f.server.refresh_memory();
18163 if f.server.memory_bytes() <= limit {
18164 return;
18165 }
18166 }
18167 panic!(
18168 "it never got under: {} against {limit}",
18169 f.server.memory_bytes()
18170 );
18171 }
18172
18173 #[test]
18174 fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
18175 let mut f = Fixture::new();
18176 assert_eq!(
18177 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18178 "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
18179 "no limit is the default"
18180 );
18181 for (typed, bytes) in [
18184 (&b"0"[..], "0"),
18185 (b"1024", "1024"),
18186 (b"1k", "1000"),
18187 (b"1gb", "1073741824"),
18188 (b"-1", "-1"),
18189 ] {
18190 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
18191 assert_eq!(
18192 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18193 format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
18194 "set {}",
18195 String::from_utf8_lossy(typed)
18196 );
18197 }
18198 for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
18199 assert_eq!(
18200 f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
18201 "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
18202 "refused {}",
18203 String::from_utf8_lossy(bad)
18204 );
18205 }
18206 let info = f.run(&[b"INFO", b"memory"]);
18208 assert!(info.contains("maxstore:-1"), "{info}");
18209 assert!(info.contains("yo_memory_regime:evict"), "{info}");
18210 assert!(info.contains("yo_store_bytes:0"), "{info}");
18211 }
18212
18213 #[test]
18214 fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
18215 let (mut f, full) = filled(true);
18219 let keys = f.run(&[b"DBSIZE"]);
18220 assert!(
18221 f.run(&[b"INFO", b"memory"])
18222 .contains("yo_memory_regime:migrate"),
18223 "a database with somewhere to put values migrates"
18224 );
18225
18226 let limit = full - 2 * 1024 * 1024;
18227 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18228 f.run(&[
18229 b"CONFIG",
18230 b"SET",
18231 b"maxmemory",
18232 limit.to_string().as_bytes(),
18233 ]);
18234 press(&mut f, limit);
18235
18236 assert!(
18237 f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18238 "nothing was thrown away"
18239 );
18240 let after: usize = f.run(&[b"DBSIZE"])[1..]
18241 .trim_end()
18242 .parse()
18243 .expect("a count");
18244 let before: usize = keys[1..].trim_end().parse().expect("a count");
18245 assert!(after > before, "the keys that came in are all still here");
18246 assert!(
18247 f.server.store_bytes() > 0,
18248 "and what came out of memory went to the file"
18249 );
18250 let val = format!("$256\r\n{}\r\n", "v".repeat(256));
18253 assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
18254 assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
18255 }
18256
18257 #[test]
18258 fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
18259 let (mut f, full) = filled(true);
18263 f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
18264 assert!(
18265 f.run(&[b"INFO", b"memory"])
18266 .contains("yo_memory_regime:evict"),
18267 "nothing may go to the file"
18268 );
18269
18270 let limit = full - 2 * 1024 * 1024;
18271 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18272 f.run(&[
18273 b"CONFIG",
18274 b"SET",
18275 b"maxmemory",
18276 limit.to_string().as_bytes(),
18277 ]);
18278 press(&mut f, limit);
18279
18280 assert!(
18281 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18282 "keys were thrown away, which is what was asked for"
18283 );
18284 assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
18285 }
18286
18287 #[test]
18288 fn a_full_file_goes_back_to_evicting() {
18289 let (mut f, full) = filled(true);
18293 f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
18294 let limit = full - 2 * 1024 * 1024;
18295 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18296 f.run(&[
18297 b"CONFIG",
18298 b"SET",
18299 b"maxmemory",
18300 limit.to_string().as_bytes(),
18301 ]);
18302 press(&mut f, limit);
18303
18304 assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
18305 assert!(
18306 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18307 "and then it started evicting"
18308 );
18309 assert!(
18310 f.run(&[b"INFO", b"memory"])
18311 .contains("yo_memory_regime:evict"),
18312 "and it says so"
18313 );
18314 }
18315 #[test]
18331 fn the_string_group_answers_the_same_however_many_stripes_there_are() {
18332 let script: &[&[&[u8]]] = &[
18333 &[b"SET", b"k1", b"v1"],
18336 &[b"SET", b"k2", b"v2"],
18337 &[b"GET", b"k1"],
18338 &[b"GET", b"nothing"],
18339 &[b"GETSET", b"k1", b"v1b"],
18340 &[b"SETNX", b"k1", b"no"],
18341 &[b"SETNX", b"k3", b"yes"],
18342 &[b"APPEND", b"k3", b"!"],
18343 &[b"STRLEN", b"k3"],
18344 &[b"SETRANGE", b"k3", b"1", b"XY"],
18345 &[b"GETRANGE", b"k3", b"0", b"-1"],
18346 &[b"INCR", b"n1"],
18347 &[b"INCRBY", b"n1", b"41"],
18348 &[b"DECRBY", b"n1", b"2"],
18349 &[b"INCRBYFLOAT", b"f1", b"1.5"],
18350 &[b"SETEX", b"e1", b"100", b"v"],
18351 &[b"PSETEX", b"e2", b"100000", b"v"],
18352 &[b"GETEX", b"e1", b"PERSIST"],
18353 &[b"GETDEL", b"k2"],
18354 &[b"GET", b"k2"],
18355 &[b"DIGEST", b"k1"],
18356 &[b"DELEX", b"k3"],
18357 &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
18360 &[b"MGET", b"a", b"b", b"c", b"missing"],
18361 &[b"MSETNX", b"d", b"4", b"e", b"5"],
18362 &[b"MSETNX", b"e", b"6", b"f", b"7"],
18363 &[b"MGET", b"d", b"e", b"f"],
18364 &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
18365 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
18366 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
18367 &[b"MGET", b"g", b"h"],
18368 &[b"SET", b"s1", b"ohmytext"],
18369 &[b"SET", b"s2", b"mynewtext"],
18370 &[b"LCS", b"s1", b"s2"],
18371 &[b"LCS", b"s1", b"s2", b"LEN"],
18372 &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
18373 &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
18374 &[b"LCS", b"s1", b"gone"],
18375 &[b"MSET", b"odd"],
18377 &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
18378 &[b"MGET"],
18379 ];
18380
18381 let mut one = Fixture::new();
18382 let mut many = Fixture::striped(8);
18383 for parts in script {
18384 let a = one.run(parts);
18385 let b = many.run(parts);
18386 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18387 }
18388 }
18389
18390 #[test]
18395 fn a_striped_database_spreads_the_keys_it_is_given() {
18396 let mut f = Fixture::striped(8);
18397 for i in 0..256 {
18398 let key = format!("key:{i}");
18399 f.run(&[b"SET", key.as_bytes(), b"v"]);
18400 }
18401 assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
18402 }
18403
18404 #[test]
18407 fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
18408 let mut one = Fixture::new();
18409 let mut many = Fixture::striped(8);
18410 for f in [&mut one, &mut many] {
18411 f.run(&[b"SET", b"str", b"v"]);
18412 f.server
18418 .striped(0)
18419 .hold(b"list")
18420 .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18421 .expect("a new list");
18422 }
18423 assert_eq!(
18424 one.run(&[b"MGET", b"str", b"list", b"gone"]),
18425 many.run(&[b"MGET", b"str", b"list", b"gone"])
18426 );
18427 }
18428
18429 #[test]
18437 fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18438 let script: &[&[&[u8]]] = &[
18439 &[b"SET", b"k1", b"v1"],
18440 &[b"SET", b"k2", b"v2"],
18441 &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18442 &[b"TYPE", b"k1"],
18443 &[b"TYPE", b"gone"],
18444 &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18445 &[b"EXPIRE", b"k1", b"100"],
18446 &[b"TTL", b"k1"],
18447 &[b"EXPIRE", b"k1", b"200", b"NX"],
18448 &[b"PERSIST", b"k1"],
18449 &[b"TTL", b"k1"],
18450 &[b"PEXPIREAT", b"k2", b"1900000000000"],
18451 &[b"EXPIRETIME", b"k2"],
18452 &[b"PEXPIRETIME", b"k2"],
18453 &[b"PERSIST", b"k2"],
18454 &[b"OBJECT", b"ENCODING", b"k1"],
18455 &[b"OBJECT", b"REFCOUNT", b"k1"],
18456 &[b"OBJECT", b"IDLETIME", b"k1"],
18457 &[b"OBJECT", b"FREQ", b"k1"],
18458 &[b"OBJECT", b"ENCODING", b"gone"],
18459 &[b"OBJECT", b"HELP"],
18460 &[b"RENAME", b"k1", b"k9"],
18461 &[b"GET", b"k9"],
18462 &[b"RENAME", b"gone", b"x"],
18463 &[b"RENAMENX", b"k9", b"k2"],
18464 &[b"RENAMENX", b"k9", b"k8"],
18465 &[b"GET", b"k8"],
18466 &[b"COPY", b"k8", b"c1"],
18467 &[b"COPY", b"k8", b"c1"],
18468 &[b"COPY", b"k8", b"c1", b"REPLACE"],
18469 &[b"COPY", b"k8", b"k8"],
18470 &[b"COPY", b"gone", b"c2"],
18471 &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18472 &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18473 &[b"MOVE", b"c1", b"1"],
18474 &[b"MOVE", b"c1", b"1"],
18475 &[b"MOVE", b"k8", b"0"],
18476 &[b"DEL", b"k2", b"gone"],
18477 &[b"UNLINK", b"k8", b"k8"],
18478 &[b"DBSIZE"],
18479 ];
18480
18481 let mut one = Fixture::new();
18482 let mut many = Fixture::striped(8);
18483 for parts in script {
18484 let a = one.run(parts);
18485 let b = many.run(parts);
18486 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18487 }
18488
18489 for f in [&mut one, &mut many] {
18495 f.run(&[b"SET", b"d1", b"payload"]);
18496 let payload = f
18497 .server
18498 .striped(0)
18499 .hold(b"d1")
18500 .dump(b"d1")
18501 .expect("a key that is there");
18502 assert!(
18503 f.run(&[b"DUMP", b"d1"])
18504 .starts_with(&format!("${}", payload.len())),
18505 "a payload of the length the store gave"
18506 );
18507 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18508 assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18509 assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18510 assert_eq!(
18511 f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18512 "-BUSYKEY Target key name already exists.\r\n"
18513 );
18514 assert_eq!(
18515 f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18516 "-ERR DUMP payload version or checksum are wrong\r\n"
18517 );
18518 }
18519 }
18520
18521 #[test]
18528 fn a_scan_of_a_striped_database_walks_all_of_it() {
18529 let mut f = Fixture::striped(8);
18530 for i in 0..500 {
18531 let key = format!("key:{i}");
18532 f.run(&[b"SET", key.as_bytes(), b"v"]);
18533 }
18534
18535 let mut seen = Vec::new();
18536 let mut cursor = "0".to_owned();
18537 let mut calls = 0;
18538 loop {
18539 let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18540 let (next, keys) = scan_reply(&reply);
18541 seen.extend(keys);
18542 cursor = next;
18543 calls += 1;
18544 assert!(calls < 5_000, "a scan that will not finish");
18545 if cursor == "0" {
18546 break;
18547 }
18548 }
18549 seen.sort();
18550 assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18551 assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18552
18553 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18557 let (_, keys) = scan_reply(&reply);
18558 assert_eq!(keys.len(), 10, "key:40 through key:49");
18559 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18560 let (_, keys) = scan_reply(&reply);
18561 assert!(keys.is_empty(), "nothing here is a list");
18562 }
18563
18564 #[test]
18570 fn a_random_key_can_come_from_any_stripe() {
18571 let mut f = Fixture::striped(8);
18572 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18573 for i in 0..200 {
18574 let key = format!("key:{i}");
18575 f.run(&[b"SET", key.as_bytes(), b"v"]);
18576 }
18577 let mut homes = std::collections::HashSet::new();
18578 for _ in 0..200 {
18579 let got = f.run(&[b"RANDOMKEY"]);
18580 let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18581 assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18582 homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18583 }
18584 assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18585 }
18586
18587 fn apart(f: &mut Fixture, src: &str) -> String {
18591 let home = f.server.striped(0).stripe_of(src.as_bytes());
18592 for i in 0..1_000 {
18593 let dst = format!("dst:{i}");
18594 if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18595 return dst;
18596 }
18597 }
18598 panic!("eight stripes and a thousand keys all landed in one place");
18599 }
18600
18601 #[test]
18604 fn a_rename_across_stripes_takes_everything_with_it() {
18605 let mut f = Fixture::striped(8);
18606 let dst = apart(&mut f, "src");
18607 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18608
18609 f.run(&[b"SET", src, b"v"]);
18610 f.run(&[b"EXPIRE", src, b"100"]);
18611 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18612 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18613 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18614 assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18615
18616 f.server
18621 .striped(0)
18622 .hold(src)
18623 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18624 .expect("a new list");
18625 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18626 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18627 assert_eq!(
18628 f.server.striped(0).hold(dst).llen(dst).expect("a list"),
18629 2,
18630 "the members are on the stripe the key moved to"
18631 );
18632
18633 f.run(&[b"SET", src, b"v"]);
18636 assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18637 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18638 assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18639 }
18640
18641 #[test]
18643 fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18644 let mut f = Fixture::striped(8);
18645 let dst = apart(&mut f, "src");
18646 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18647
18648 f.run(&[b"SET", src, b"v"]);
18649 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18650 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18651 assert_eq!(
18652 f.run(&[b"COPY", src, dst]),
18653 ":0\r\n",
18654 "the destination is taken"
18655 );
18656 f.run(&[b"SET", src, b"w"]);
18657 assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18658 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18659
18660 f.run(&[b"DEL", src, dst]);
18664 f.server
18665 .striped(0)
18666 .hold(src)
18667 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18668 .expect("a new list");
18669 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18670 f.server
18671 .striped(0)
18672 .hold(src)
18673 .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18674 .expect("a list that is there");
18675 assert_eq!(f.server.striped(0).hold(src).llen(src).expect("a list"), 3);
18676 assert_eq!(f.server.striped(0).hold(dst).llen(dst).expect("a list"), 2);
18677 }
18678
18679 #[test]
18687 fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18688 let script: &[&[&[u8]]] = &[
18689 &[b"SET", b"k1", b"foobar"],
18690 &[b"SETBIT", b"b1", b"7", b"1"],
18691 &[b"SETBIT", b"b1", b"7", b"0"],
18692 &[b"GETBIT", b"k1", b"6"],
18693 &[b"GETBIT", b"k1", b"100"],
18694 &[b"BITCOUNT", b"k1"],
18695 &[b"BITCOUNT", b"k1", b"0", b"0"],
18696 &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18697 &[b"BITPOS", b"k1", b"1"],
18698 &[b"BITPOS", b"k1", b"0", b"2"],
18699 &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18700 &[
18701 b"BITFIELD",
18702 b"bf",
18703 b"SET",
18704 b"u8",
18705 b"0",
18706 b"255",
18707 b"GET",
18708 b"u8",
18709 b"0",
18710 ],
18711 &[
18712 b"BITFIELD",
18713 b"bf",
18714 b"OVERFLOW",
18715 b"SAT",
18716 b"INCRBY",
18717 b"u8",
18718 b"0",
18719 b"10",
18720 ],
18721 &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18722 &[b"SET", b"s1", b"abc"],
18725 &[b"SET", b"s2", b"abd"],
18726 &[b"SET", b"s3", b"a"],
18727 &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18728 &[b"GET", b"d1"],
18729 &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18730 &[b"GET", b"d2"],
18731 &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18732 &[b"STRLEN", b"d3"],
18733 &[b"BITOP", b"NOT", b"d4", b"s1"],
18734 &[b"STRLEN", b"d4"],
18735 &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18736 &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18737 &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18738 &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18739 &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18742 &[b"EXISTS", b"d1"],
18743 &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18744 &[b"GET", b"d9"],
18745 &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18749 &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18750 &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18751 &[b"BITOP", b"DIFF", b"d1", b"s1"],
18752 &[b"BITOP", b"NOPE", b"d1", b"s1"],
18753 &[b"BITCOUNT", b"list"],
18754 &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18755 ];
18756
18757 let mut one = Fixture::new();
18758 let mut many = Fixture::striped(8);
18759 for f in [&mut one, &mut many] {
18760 plant_list(f, b"list");
18761 }
18762 for parts in script {
18763 let a = one.run(parts);
18764 let b = many.run(parts);
18765 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18766 }
18767 }
18768
18769 fn plant_list(f: &mut Fixture, key: &[u8]) {
18775 f.server
18776 .striped(0)
18777 .hold(key)
18778 .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18779 .expect("a new list");
18780 }
18781
18782 #[test]
18789 fn a_bitop_across_stripes_reads_every_source() {
18790 let mut f = Fixture::striped(8);
18791 let other = apart(&mut f, "src");
18792 let (src, far) = (b"src".as_slice(), other.as_bytes());
18793 assert_ne!(
18794 f.server.striped(0).stripe_of(src),
18795 f.server.striped(0).stripe_of(far),
18796 "the two keys are the point of the test"
18797 );
18798
18799 f.run(&[b"SET", src, b"abc"]);
18800 f.run(&[b"SET", far, b"abd"]);
18801 assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
18802 assert_eq!(
18803 f.run(&[b"GET", far]),
18804 "$3\r\nab`\r\n",
18805 "a destination that is also a source"
18806 );
18807 f.run(&[b"SET", far, b"abd"]);
18808 assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
18809 assert_eq!(
18810 f.run(&[b"GET", src]),
18811 "$3\r\n\0\0\x07\r\n",
18812 "and the other way round"
18813 );
18814
18815 f.run(&[b"SET", src, b"abc"]);
18819 f.run(&[b"DEL", far]);
18820 assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
18821 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
18822 f.run(&[b"SET", src, b"abc"]);
18823 f.run(&[b"DEL", far]);
18824 plant_list(&mut f, far);
18825 assert_eq!(
18826 f.run(&[b"BITOP", b"OR", b"out", src, far]),
18827 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18828 );
18829 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
18830 }
18831
18832 #[test]
18834 fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
18835 let script: &[&[&[u8]]] = &[
18836 &[b"PFADD", b"h1", b"a", b"b", b"c"],
18837 &[b"PFADD", b"h1", b"a"],
18838 &[b"PFADD", b"h2"],
18839 &[b"PFADD", b"h2", b"c", b"d", b"e"],
18840 &[b"PFCOUNT", b"h1"],
18841 &[b"PFCOUNT", b"h2"],
18842 &[b"PFCOUNT", b"missing"],
18843 &[b"PFCOUNT", b"h1", b"h2"],
18845 &[b"PFCOUNT", b"h1", b"missing"],
18846 &[b"PFMERGE", b"m", b"h1", b"h2"],
18847 &[b"PFCOUNT", b"m"],
18848 &[b"STRLEN", b"m"],
18849 &[b"PFMERGE", b"m"],
18850 &[b"PFCOUNT", b"m"],
18851 &[b"PFMERGE", b"m2", b"missing"],
18852 &[b"PFCOUNT", b"m2"],
18853 &[b"PFDEBUG", b"ENCODING", b"h1"],
18856 &[b"PFDEBUG", b"DECODE", b"h1"],
18857 &[b"PFDEBUG", b"TODENSE", b"h1"],
18858 &[b"PFDEBUG", b"ENCODING", b"h1"],
18859 &[b"PFDEBUG", b"TODENSE", b"h1"],
18860 &[b"PFCOUNT", b"h1", b"h2"],
18861 &[b"PFSELFTEST"],
18862 &[b"SET", b"plain", b"not a sketch at all"],
18864 &[b"PFADD", b"plain", b"a"],
18865 &[b"PFCOUNT", b"plain"],
18866 &[b"PFCOUNT", b"h1", b"plain"],
18867 &[b"PFMERGE", b"plain", b"h1"],
18868 &[b"PFMERGE", b"m", b"plain"],
18869 &[b"PFDEBUG", b"ENCODING", b"gone"],
18870 &[b"PFDEBUG", b"NOPE", b"h1"],
18871 ];
18872
18873 let mut one = Fixture::new();
18874 let mut many = Fixture::striped(8);
18875 for parts in script {
18876 let a = one.run(parts);
18877 let b = many.run(parts);
18878 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18879 }
18880 }
18881
18882 #[test]
18891 fn the_set_group_answers_the_same_however_many_stripes_there_are() {
18892 const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
18893 let script: &[&[&[u8]]] = &[
18894 &[b"SADD", b"s1", b"a", b"b", b"c"],
18895 &[b"SADD", b"s1", b"a"],
18896 &[b"SADD", b"s2", b"b", b"c", b"d"],
18897 &[b"SADD", b"ints", b"1", b"2", b"3"],
18898 &[b"SCARD", b"s1"],
18899 &[b"SISMEMBER", b"s1", b"a"],
18900 &[b"SISMEMBER", b"s1", b"z"],
18901 &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
18902 &[b"SMEMBERS", b"s1"],
18903 &[b"SREM", b"s1", b"c"],
18904 &[b"SADD", b"s1", b"c"],
18905 &[b"SSCAN", b"s1", b"0"],
18906 &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
18907 &[b"SADD", b"one", b"m"],
18910 &[b"SRANDMEMBER", b"one"],
18911 &[b"SRANDMEMBER", b"one", b"-3"],
18912 &[b"SRANDMEMBER", b"gone"],
18913 &[b"SPOP", b"one"],
18914 &[b"SPOP", b"one"],
18915 &[b"SPOP", b"gone", b"2"],
18916 &[b"SMOVE", b"s1", b"s2", b"a"],
18918 &[b"SMOVE", b"s1", b"s2", b"zzz"],
18919 &[b"SMOVE", b"gone", b"s2", b"a"],
18920 &[b"SMEMBERS", b"s1"],
18921 &[b"SMEMBERS", b"s2"],
18922 &[b"SINTER", b"s1", b"s2"],
18924 &[b"SUNION", b"s1", b"s2"],
18925 &[b"SDIFF", b"s2", b"s1"],
18926 &[b"SINTER", b"s1", b"gone"],
18927 &[b"SUNION", b"s1", b"gone"],
18928 &[b"SDIFF", b"gone", b"s1"],
18929 &[b"SINTER", b"ints", b"s1"],
18930 &[b"SINTERCARD", b"2", b"s1", b"s2"],
18931 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
18932 &[b"SUNIONCARD", b"2", b"s1", b"s2"],
18933 &[b"SDIFFCARD", b"2", b"s2", b"s1"],
18934 &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
18935 &[b"SMEMBERS", b"d1"],
18936 &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
18937 &[b"SCARD", b"d2"],
18938 &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
18939 &[b"SCARD", b"d3"],
18940 &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
18943 &[b"EXISTS", b"d4"],
18944 &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
18946 &[b"SCARD", b"s2"],
18947 &[b"SET", b"str", b"v"],
18949 &[b"SADD", b"str", b"a"],
18950 &[b"SINTER", b"s1", b"str"],
18951 &[b"SINTERSTORE", b"d5", b"s1", b"str"],
18952 &[b"EXISTS", b"d5"],
18953 &[b"SMOVE", b"str", b"s2", b"a"],
18954 &[b"SMOVE", b"s1", b"str", b"b"],
18955 &[b"SMOVE", b"gone", b"str", b"b"],
18956 &[b"SINTERCARD", b"0", b"s1"],
18957 &[b"SINTERCARD", b"3", b"s1", b"s2"],
18958 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
18959 &[b"SPOP", b"s1", b"-1"],
18960 ];
18961
18962 let mut one = Fixture::new();
18963 let mut many = Fixture::striped(8);
18964 for parts in script {
18965 let a = one.run(parts);
18966 let b = many.run(parts);
18967 let name = String::from_utf8_lossy(parts[0]).to_uppercase();
18968 if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
18969 assert_eq!(sorted(&a), sorted(&b), "{name}");
18970 } else {
18971 assert_eq!(a, b, "{name}");
18972 }
18973 }
18974 }
18975
18976 #[test]
18978 fn a_set_operation_across_stripes_reads_every_set() {
18979 let mut f = Fixture::striped(8);
18980 let second = apart(&mut f, "s1");
18981 let third = apart(&mut f, &second);
18982 let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
18983
18984 f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
18985 f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
18986 assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
18987 assert_eq!(
18988 sorted(&f.run(&[b"SUNION", s1, s2])),
18989 ["a", "b", "c", "d"],
18990 "a union of two stripes is both of them"
18991 );
18992 assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
18993 assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
18994 assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
18995 assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
18996
18997 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
18999 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
19000 assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
19001 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
19002 assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
19003 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
19004
19005 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
19008 assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
19009 f.run(&[b"SET", s3, b"v"]);
19010 assert_eq!(
19011 f.run(&[b"SINTER", s1, s3]),
19012 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19013 );
19014 assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
19015 }
19016
19017 #[test]
19019 fn a_move_across_stripes_takes_the_member_with_it() {
19020 let mut f = Fixture::striped(8);
19021 let other = apart(&mut f, "src");
19022 let (src, dst) = (b"src".as_slice(), other.as_bytes());
19023
19024 f.run(&[b"SADD", src, b"a", b"b"]);
19025 f.run(&[b"SADD", dst, b"c"]);
19026 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
19027 assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
19028 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
19029 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
19030
19031 f.run(&[b"DEL", dst]);
19034 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
19035 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
19036 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
19037
19038 f.run(&[b"SET", dst, b"v"]);
19042 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
19043 f.run(&[b"SADD", src, b"b"]);
19044 assert_eq!(
19045 f.run(&[b"SMOVE", src, dst, b"b"]),
19046 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19047 );
19048 }
19049
19050 #[test]
19052 fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
19053 let mut f = Fixture::striped(8);
19054 let other = apart(&mut f, "src");
19055 let (src, far) = (b"src".as_slice(), other.as_bytes());
19056
19057 for i in 0..150 {
19058 let ele = format!("e:{i}");
19059 f.run(&[b"PFADD", src, ele.as_bytes()]);
19060 }
19061 for i in 150..200 {
19062 let ele = format!("e:{i}");
19063 f.run(&[b"PFADD", far, ele.as_bytes()]);
19064 }
19065 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
19068 assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
19069 assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
19070
19071 let dest = apart(&mut f, &other);
19074 assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
19075 assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
19076 assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
19077 assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
19078 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
19079 }
19080
19081 #[test]
19088 fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
19089 let script: &[&[&[u8]]] = &[
19090 &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
19091 &[b"ZADD", b"z1", b"NX", b"9", b"a"],
19092 &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
19093 &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
19094 &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
19095 &[b"ZINCRBY", b"z1", b"1.5", b"b"],
19096 &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
19097 &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
19098 &[b"ZADD", b"one", b"1", b"m"],
19099 &[b"ZCARD", b"z1"],
19100 &[b"ZCARD", b"gone"],
19101 &[b"ZSCORE", b"z1", b"a"],
19102 &[b"ZSCORE", b"z1", b"zz"],
19103 &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
19104 &[b"ZRANK", b"z1", b"c"],
19105 &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
19106 &[b"ZREVRANK", b"z1", b"c"],
19107 &[b"ZRANK", b"z1", b"gone"],
19108 &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
19109 &[b"ZCOUNT", b"z1", b"(1", b"3"],
19110 &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
19111 &[b"ZRANGE", b"z1", b"0", b"-1"],
19113 &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
19114 &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
19115 &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
19116 &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
19117 &[b"ZREVRANGE", b"z1", b"0", b"-1"],
19118 &[
19119 b"ZRANGEBYSCORE",
19120 b"z1",
19121 b"-inf",
19122 b"+inf",
19123 b"LIMIT",
19124 b"1",
19125 b"1",
19126 ],
19127 &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
19128 &[b"ZSCAN", b"z1", b"0"],
19129 &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
19130 &[b"ZRANDMEMBER", b"one"],
19133 &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
19134 &[b"ZRANDMEMBER", b"gone"],
19135 &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
19137 &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
19138 &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
19139 &[b"EXISTS", b"d0"],
19140 &[b"ZUNION", b"2", b"z1", b"z2"],
19142 &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
19143 &[
19144 b"ZUNION",
19145 b"2",
19146 b"z1",
19147 b"z2",
19148 b"WEIGHTS",
19149 b"2",
19150 b"3",
19151 b"AGGREGATE",
19152 b"MAX",
19153 b"WITHSCORES",
19154 ],
19155 &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
19156 &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
19157 &[b"ZDIFF", b"2", b"gone", b"z1"],
19158 &[b"ZINTERCARD", b"2", b"z1", b"z2"],
19159 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
19160 &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
19161 &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
19162 &[
19163 b"ZINTERSTORE",
19164 b"d2",
19165 b"2",
19166 b"z1",
19167 b"z2",
19168 b"AGGREGATE",
19169 b"MIN",
19170 ],
19171 &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
19172 &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
19173 &[b"ZCARD", b"d3"],
19174 &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
19177 &[b"EXISTS", b"d4"],
19178 &[b"SADD", b"plain", b"a", b"x"],
19181 &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
19182 &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
19183 &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
19185 &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
19186 &[b"ZREM", b"d5", b"x", b"nothere"],
19188 &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
19189 &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
19190 &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
19191 &[b"ZPOPMIN", b"z1"],
19192 &[b"ZPOPMAX", b"z1", b"2"],
19193 &[b"ZPOPMIN", b"gone"],
19194 &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
19195 &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
19196 &[b"SET", b"str", b"v"],
19198 &[b"ZADD", b"str", b"1", b"a"],
19199 &[b"ZSCORE", b"str", b"a"],
19200 &[b"ZADD", b"z1", b"nan", b"a"],
19201 &[b"ZUNION", b"2", b"z1", b"str"],
19202 &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
19203 &[b"EXISTS", b"d6"],
19204 &[b"ZINTERCARD", b"0", b"z1"],
19205 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
19206 &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
19207 &[b"ZMPOP", b"1", b"str", b"MIN"],
19208 &[b"ZPOPMIN", b"z1", b"-1"],
19209 ];
19210
19211 let mut one = Fixture::new();
19212 let mut many = Fixture::striped(8);
19213 for parts in script {
19214 let a = one.run(parts);
19215 let b = many.run(parts);
19216 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19217 }
19218 }
19219
19220 #[test]
19222 fn a_sorted_set_operation_across_stripes_reads_every_input() {
19223 let mut f = Fixture::striped(8);
19224 let second = apart(&mut f, "z1");
19225 let third = apart(&mut f, &second);
19226 let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
19227
19228 f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
19229 f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
19230 assert_eq!(
19233 f.run(&[b"ZUNION", b"2", z1, z2]),
19234 "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
19235 );
19236 assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
19237 assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
19238 assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
19239 assert_eq!(
19240 f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
19241 ":1\r\n"
19242 );
19243
19244 assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
19247 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
19248 assert_eq!(
19249 f.run(&[
19250 b"ZUNIONSTORE",
19251 z3,
19252 b"2",
19253 z1,
19254 z2,
19255 b"WEIGHTS",
19256 b"2",
19257 b"3",
19258 b"AGGREGATE",
19259 b"MAX"
19260 ]),
19261 ":3\r\n"
19262 );
19263 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
19264 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
19265 assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
19266 assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
19267 assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
19268
19269 let popped = format!(
19272 "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
19273 second.len()
19274 );
19275 assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
19276 f.run(&[b"ZADD", z2, b"3", b"b"]);
19277
19278 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
19281 assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
19282 f.run(&[b"SET", z3, b"v"]);
19283 assert_eq!(
19284 f.run(&[b"ZUNION", b"2", z1, z3]),
19285 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19286 );
19287 assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
19288
19289 assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
19293 assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
19294 assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
19295 }
19296
19297 #[test]
19299 fn a_range_store_across_stripes_copies_the_window() {
19300 let mut f = Fixture::striped(8);
19301 let other = apart(&mut f, "src");
19302 let third = apart(&mut f, &other);
19303 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19304
19305 f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
19306 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
19307 assert_eq!(
19308 f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
19309 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
19310 );
19311 assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
19312
19313 assert_eq!(
19316 f.run(&[
19317 b"ZRANGESTORE",
19318 dst,
19319 src,
19320 b"+inf",
19321 b"-inf",
19322 b"BYSCORE",
19323 b"REV",
19324 b"LIMIT",
19325 b"0",
19326 b"2"
19327 ]),
19328 ":2\r\n"
19329 );
19330 assert_eq!(
19331 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19332 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19333 );
19334
19335 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
19338 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19339 f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
19340 f.run(&[b"SET", plain, b"v"]);
19341 assert_eq!(
19342 f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
19343 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19344 );
19345 assert_eq!(
19346 f.run(&[b"ZCARD", dst]),
19347 ":3\r\n",
19348 "and left the destination"
19349 );
19350 }
19351
19352 #[test]
19359 fn the_list_group_answers_the_same_however_many_stripes_there_are() {
19360 let script: &[&[&[u8]]] = &[
19361 &[b"RPUSH", b"l1", b"a", b"b", b"c"],
19362 &[b"LPUSH", b"l1", b"z"],
19363 &[b"RPUSHX", b"l1", b"d"],
19364 &[b"LPUSHX", b"gone", b"x"],
19365 &[b"RPUSHX", b"gone", b"x"],
19366 &[b"LLEN", b"l1"],
19367 &[b"LLEN", b"gone"],
19368 &[b"LRANGE", b"l1", b"0", b"-1"],
19369 &[b"LRANGE", b"l1", b"1", b"2"],
19370 &[b"LRANGE", b"l1", b"5", b"9"],
19371 &[b"LINDEX", b"l1", b"0"],
19372 &[b"LINDEX", b"l1", b"-1"],
19373 &[b"LINDEX", b"l1", b"99"],
19374 &[b"LSET", b"l1", b"0", b"y"],
19375 &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
19376 &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
19377 &[b"LPOS", b"l1", b"b"],
19378 &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
19379 &[b"LPOS", b"l1", b"nothere"],
19380 &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
19381 &[b"LREM", b"l1", b"1", b"aa"],
19382 &[b"LTRIM", b"l1", b"0", b"3"],
19383 &[b"LRANGE", b"l1", b"0", b"-1"],
19384 &[b"LPOP", b"l1"],
19385 &[b"RPOP", b"l1"],
19386 &[b"LPOP", b"l1", b"2"],
19387 &[b"LPOP", b"gone"],
19388 &[b"LPOP", b"gone", b"2"],
19389 &[b"EXISTS", b"l1"],
19390 &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
19393 &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
19394 &[b"RPOPLPUSH", b"src", b"dst"],
19395 &[b"LRANGE", b"dst", b"0", b"-1"],
19396 &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
19397 &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
19398 &[
19399 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
19400 ],
19401 &[
19402 b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
19403 ],
19404 &[b"LRANGE", b"dst", b"0", b"-1"],
19405 &[
19406 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
19407 ],
19408 &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19409 &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19410 &[b"LMPOP", b"1", b"gone", b"LEFT"],
19411 &[b"RPUSH", b"q", b"a", b"b", b"c"],
19414 &[b"BLPOP", b"gone", b"q", b"0"],
19415 &[b"BRPOP", b"q", b"0"],
19416 &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19417 &[b"RPUSH", b"q", b"x", b"y", b"z"],
19418 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19419 &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19420 &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19421 &[b"BLPOP", b"q", b"0"],
19422 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19423 &[b"SET", b"plain", b"v"],
19425 &[b"LPUSH", b"plain", b"a"],
19426 &[b"LLEN", b"plain"],
19427 &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19428 &[b"LRANGE", b"dst", b"0", b"-1"],
19429 &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19430 &[b"LSET", b"gone", b"0", b"v"],
19431 &[b"LSET", b"dst", b"99", b"v"],
19432 &[b"LPOP", b"dst", b"-1"],
19433 &[b"LMPOP", b"0", b"dst", b"LEFT"],
19434 &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19435 ];
19436
19437 let mut one = Fixture::new();
19438 let mut many = Fixture::striped(8);
19439 for parts in script {
19440 let a = one.run(parts);
19441 let b = many.run(parts);
19442 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19443 }
19444 }
19445
19446 #[test]
19448 fn a_list_move_across_stripes_takes_the_elements_with_it() {
19449 let mut f = Fixture::striped(8);
19450 let other = apart(&mut f, "src");
19451 let third = apart(&mut f, &other);
19452 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19453
19454 f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19455 assert_eq!(
19456 f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19457 "$1\r\na\r\n"
19458 );
19459 assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19460 assert_eq!(
19461 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19462 "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19463 "one went on each end of the destination"
19464 );
19465 assert_eq!(
19466 f.run(&[b"LRANGE", src, b"0", b"-1"]),
19467 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19468 );
19469
19470 assert_eq!(
19472 f.run(&[
19473 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19474 ]),
19475 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19476 );
19477 assert_eq!(
19478 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19479 "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19480 );
19481 assert_eq!(
19482 f.run(&[b"EXISTS", src]),
19483 ":0\r\n",
19484 "and the source is gone with its last element"
19485 );
19486
19487 f.run(&[b"RPUSH", src, b"e", b"f"]);
19490 assert_eq!(
19491 f.run(&[
19492 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19493 ]),
19494 "*-1\r\n"
19495 );
19496 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19497 assert_eq!(
19498 f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19499 "$-1\r\n"
19500 );
19501 assert_eq!(
19502 f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19503 "*-1\r\n"
19504 );
19505
19506 f.run(&[b"SET", plain, b"v"]);
19510 assert_eq!(
19511 f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19512 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19513 );
19514 assert_eq!(
19515 f.run(&[b"LLEN", src]),
19516 ":2\r\n",
19517 "and left the source alone"
19518 );
19519 assert_eq!(
19520 f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19521 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19522 );
19523 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19524 }
19525
19526 #[test]
19532 fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19533 let mut f = Fixture::striped(8);
19534 let other = apart(&mut f, "q");
19535 let (q, far) = (b"q".as_slice(), other.as_bytes());
19536
19537 assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19538 assert_eq!(f.server.parked(), 1);
19539 f.run(&[b"RPUSH", far, b"v"]);
19540 let mut out = Out::new(Proto::Resp2);
19541 assert!(f.server.serve_waiter(0, 0, &mut out));
19542 let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19543 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19544 assert_eq!(
19545 f.run(&[b"EXISTS", far]),
19546 ":0\r\n",
19547 "and it took the element with it"
19548 );
19549
19550 f.server.forget_waiters(7);
19553 assert_eq!(
19554 f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19555 Flow::Block
19556 );
19557 f.run(&[b"RPUSH", q, b"w"]);
19558 let mut out = Out::new(Proto::Resp2);
19559 assert!(f.server.serve_waiter(0, 0, &mut out));
19560 assert_eq!(
19561 core::str::from_utf8(out.as_slice()).expect("ascii"),
19562 "$1\r\nw\r\n"
19563 );
19564 assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19565 }
19566
19567 #[test]
19573 fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19574 let script: &[&[&[u8]]] = &[
19575 &[b"XADD", b"s", b"1-1", b"a", b"1"],
19576 &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19577 &[b"XADD", b"s", b"3-1", b"d", b"4"],
19578 &[b"XADD", b"s", b"1-1", b"e", b"5"],
19579 &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19580 &[b"XLEN", b"s"],
19581 &[b"XLEN", b"gone"],
19582 &[b"XRANGE", b"s", b"-", b"+"],
19583 &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19584 &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19585 &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19586 &[b"XREVRANGE", b"s", b"+", b"-"],
19587 &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19588 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19589 &[b"XREAD", b"STREAMS", b"s", b"$"],
19590 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19592 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19593 &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19594 &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19595 &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19596 &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19597 &[
19598 b"XREADGROUP",
19599 b"GROUP",
19600 b"g",
19601 b"c1",
19602 b"COUNT",
19603 b"1",
19604 b"STREAMS",
19605 b"s",
19606 b"0",
19607 ],
19608 &[
19609 b"XREADGROUP",
19610 b"GROUP",
19611 b"nope",
19612 b"c1",
19613 b"STREAMS",
19614 b"s",
19615 b">",
19616 ],
19617 &[b"XPENDING", b"s", b"g"],
19618 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19619 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19620 &[b"XPENDING", b"s", b"nope"],
19621 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19622 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19623 &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19624 &[b"XACK", b"s", b"g", b"1-1"],
19625 &[b"XACK", b"s", b"g", b"1-1"],
19626 &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19627 &[b"XPENDING", b"s", b"g"],
19628 &[b"XINFO", b"STREAM", b"s"],
19629 &[b"XINFO", b"GROUPS", b"s"],
19630 &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19631 &[b"XINFO", b"STREAM", b"gone"],
19632 &[b"XDEL", b"s", b"3-1"],
19634 &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19635 &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19636 &[b"XADD", b"s", b"9-1", b"z", b"9"],
19637 &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19638 &[b"XTRIM", b"s", b"MINID", b"9"],
19639 &[b"XSETID", b"s", b"99-1"],
19640 &[b"XSETID", b"s", b"1-1"],
19641 &[b"XLEN", b"s"],
19642 &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19643 &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19644 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19645 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19646 &[b"SET", b"plain", b"v"],
19648 &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19649 &[b"XLEN", b"plain"],
19650 &[b"XREAD", b"STREAMS", b"plain", b"0"],
19651 &[b"XRANGE", b"s", b"bogus", b"+"],
19652 &[b"XADD", b"s", b"1-1", b"a"],
19653 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19654 &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19655 ];
19656
19657 let mut one = Fixture::new();
19658 let mut many = Fixture::striped(8);
19659 for parts in script {
19660 let a = one.run(parts);
19661 let b = many.run(parts);
19662 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19663 }
19664 }
19665
19666 #[test]
19672 fn a_stream_read_across_stripes_reads_every_key() {
19673 let mut f = Fixture::striped(8);
19674 let other = apart(&mut f, "s1");
19675 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19676
19677 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19678 f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19679 let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19680 assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19681 assert!(got.contains("1-1"), "the first one is in there: {got}");
19682 assert!(got.contains("2-1"), "and so is the second: {got}");
19683
19684 f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19687 let got = f.run(&[
19688 b"XREADGROUP",
19689 b"GROUP",
19690 b"g",
19691 b"c",
19692 b"STREAMS",
19693 s1,
19694 s2,
19695 b">",
19696 b">",
19697 ]);
19698 assert!(got.starts_with("-NOGROUP"), "{got}");
19699 assert_eq!(
19700 f.run(&[b"XPENDING", s1, b"g"]),
19701 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19702 "and read nothing from the key that did have the group"
19703 );
19704
19705 f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19706 let got = f.run(&[
19707 b"XREADGROUP",
19708 b"GROUP",
19709 b"g",
19710 b"c",
19711 b"STREAMS",
19712 s1,
19713 s2,
19714 b">",
19715 b">",
19716 ]);
19717 assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19718 }
19719
19720 #[test]
19722 fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19723 let mut f = Fixture::striped(8);
19724 let other = apart(&mut f, "s1");
19725 let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19726 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19727 f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19728
19729 assert_eq!(
19730 f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19731 .0,
19732 Flow::Block
19733 );
19734 f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19735 let mut out = Out::new(Proto::Resp2);
19736 assert!(f.server.serve_waiter(0, 0, &mut out));
19737 let want = format!(
19738 "*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",
19739 other.len()
19740 );
19741 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19742 }
19743
19744 #[test]
19746 fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19747 let script: &[&[&[u8]]] = &[
19748 &[
19749 b"JSON.SET",
19750 b"d",
19751 b"$",
19752 br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19753 ],
19754 &[b"JSON.SET", b"d", b"$.a", b"2"],
19755 &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19756 &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19757 &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19758 &[b"JSON.GET", b"d"],
19759 &[b"JSON.GET", b"d", b"$.b"],
19760 &[b"JSON.GET", b"gone", b"$"],
19761 &[b"JSON.TYPE", b"d", b"$.b"],
19762 &[b"JSON.TYPE", b"d", b"$.s"],
19763 &[b"JSON.TOGGLE", b"d", b"$.t"],
19764 &[b"JSON.ARRLEN", b"d", b"$.b"],
19765 &[b"JSON.OBJLEN", b"d", b"$"],
19766 &[b"JSON.OBJKEYS", b"d", b"$"],
19767 &[b"JSON.STRLEN", b"d", b"$.s"],
19768 &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19769 &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19770 &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19771 &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19772 &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19773 &[b"JSON.ARRPOP", b"d", b"$.b"],
19774 &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19775 &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19776 &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19777 &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19778 &[b"JSON.RESP", b"d", b"$.b"],
19779 &[b"JSON.DEBUG", b"MEMORY", b"d"],
19780 &[b"JSON.CLEAR", b"d", b"$.b"],
19781 &[b"JSON.DEL", b"d", b"$.m"],
19782 &[b"JSON.FORGET", b"d", b"$.nothere"],
19783 &[
19785 b"JSON.MSET",
19786 b"m1",
19787 b"$",
19788 b"1",
19789 b"m2",
19790 b"$",
19791 b"2",
19792 b"m3",
19793 b"$",
19794 b"3",
19795 ],
19796 &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
19797 &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
19798 &[b"JSON.GET", b"m1", b"$"],
19799 &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
19800 &[b"JSON.GET", b"m2", b"$"],
19801 &[b"SET", b"plain", b"v"],
19803 &[b"JSON.GET", b"plain", b"$"],
19804 &[b"JSON.SET", b"plain", b"$", b"1"],
19805 &[b"JSON.MGET", b"m1", b"plain", b"$"],
19806 &[b"JSON.SET", b"d", b"$.b", b"["],
19807 &[b"JSON.DEL", b"plain"],
19808 ];
19809
19810 let mut one = Fixture::new();
19811 let mut many = Fixture::striped(8);
19812 for parts in script {
19813 let a = one.run(parts);
19814 let b = many.run(parts);
19815 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19816 }
19817 }
19818
19819 #[test]
19826 fn a_json_multi_write_across_stripes_reaches_every_key() {
19827 let mut f = Fixture::striped(8);
19828 let second = apart(&mut f, "m1");
19829 let third = apart(&mut f, &second);
19830 let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
19831
19832 assert_eq!(
19833 f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
19834 "+OK\r\n"
19835 );
19836 assert_eq!(
19837 f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
19838 "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
19839 );
19840
19841 assert_eq!(
19844 f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
19845 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
19846 );
19847 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
19848
19849 assert_eq!(
19853 f.run(&[
19854 b"JSON.MSET",
19855 m1,
19856 b"$",
19857 b"9",
19858 m2,
19859 b"$.deep",
19860 b"9",
19861 m3,
19862 b"$",
19863 b"7"
19864 ]),
19865 "$-1\r\n"
19866 );
19867 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
19868 assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
19869 assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
19870 }
19871
19872 #[test]
19874 fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
19875 let script: &[&[&[u8]]] = &[
19876 &[
19877 b"GEOADD",
19878 b"g",
19879 b"13.361389",
19880 b"38.115556",
19881 b"palermo",
19882 b"15.087269",
19883 b"37.502669",
19884 b"catania",
19885 ],
19886 &[
19887 b"GEOADD",
19888 b"g",
19889 b"NX",
19890 b"13.361389",
19891 b"38.115556",
19892 b"palermo",
19893 ],
19894 &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
19895 &[b"GEOPOS", b"g", b"palermo", b"nothere"],
19896 &[b"GEOHASH", b"g", b"palermo", b"catania"],
19897 &[b"GEODIST", b"g", b"palermo", b"catania"],
19898 &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
19899 &[b"GEODIST", b"g", b"palermo", b"nothere"],
19900 &[
19901 b"GEOSEARCH",
19902 b"g",
19903 b"FROMLONLAT",
19904 b"15",
19905 b"37",
19906 b"BYRADIUS",
19907 b"200",
19908 b"KM",
19909 b"ASC",
19910 b"WITHCOORD",
19911 b"WITHDIST",
19912 b"WITHHASH",
19913 ],
19914 &[
19915 b"GEOSEARCH",
19916 b"g",
19917 b"FROMMEMBER",
19918 b"palermo",
19919 b"BYBOX",
19920 b"400",
19921 b"400",
19922 b"KM",
19923 b"DESC",
19924 ],
19925 &[
19926 b"GEORADIUS",
19927 b"g",
19928 b"15",
19929 b"37",
19930 b"200",
19931 b"KM",
19932 b"COUNT",
19933 b"1",
19934 ],
19935 &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
19936 &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
19937 &[
19938 b"GEOSEARCHSTORE",
19939 b"dst",
19940 b"g",
19941 b"FROMLONLAT",
19942 b"15",
19943 b"37",
19944 b"BYRADIUS",
19945 b"200",
19946 b"KM",
19947 ],
19948 &[b"ZRANGE", b"dst", b"0", b"-1"],
19949 &[
19950 b"GEOSEARCHSTORE",
19951 b"dst",
19952 b"g",
19953 b"FROMLONLAT",
19954 b"15",
19955 b"37",
19956 b"BYRADIUS",
19957 b"1",
19958 b"M",
19959 b"STOREDIST",
19960 ],
19961 &[b"EXISTS", b"dst"],
19962 &[
19963 b"GEORADIUS",
19964 b"g",
19965 b"15",
19966 b"37",
19967 b"200",
19968 b"KM",
19969 b"STORE",
19970 b"dst",
19971 ],
19972 &[b"ZCARD", b"dst"],
19973 &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
19975 &[b"SET", b"plain", b"v"],
19976 &[b"GEOPOS", b"plain", b"a"],
19977 &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
19978 &[
19979 b"GEOSEARCHSTORE",
19980 b"dst",
19981 b"g",
19982 b"FROMLONLAT",
19983 b"15",
19984 b"37",
19985 b"BYRADIUS",
19986 b"200",
19987 b"KM",
19988 b"WITHCOORD",
19989 ],
19990 ];
19991
19992 let mut one = Fixture::new();
19993 let mut many = Fixture::striped(8);
19994 for parts in script {
19995 let a = one.run(parts);
19996 let b = many.run(parts);
19997 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19998 }
19999 }
20000
20001 #[test]
20003 fn a_geo_search_store_across_stripes_writes_what_it_found() {
20004 let mut f = Fixture::striped(8);
20005 let other = apart(&mut f, "g");
20006 let third = apart(&mut f, &other);
20007 let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
20008
20009 f.run(&[
20010 b"GEOADD",
20011 g,
20012 b"13.361389",
20013 b"38.115556",
20014 b"palermo",
20015 b"15.087269",
20016 b"37.502669",
20017 b"catania",
20018 ]);
20019 assert_eq!(
20020 f.run(&[
20021 b"GEOSEARCHSTORE",
20022 dst,
20023 g,
20024 b"FROMLONLAT",
20025 b"15",
20026 b"37",
20027 b"BYRADIUS",
20028 b"200",
20029 b"KM",
20030 b"ASC",
20031 ]),
20032 ":2\r\n"
20033 );
20034 assert_eq!(
20035 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
20036 "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
20037 "the geohash is the score, so the order is not the search order"
20038 );
20039 assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
20040
20041 assert_eq!(
20044 f.run(&[
20045 b"GEOSEARCHSTORE",
20046 dst,
20047 g,
20048 b"FROMMEMBER",
20049 b"palermo",
20050 b"BYRADIUS",
20051 b"200",
20052 b"KM",
20053 b"STOREDIST",
20054 ]),
20055 ":2\r\n"
20056 );
20057 assert_eq!(
20058 f.run(&[b"ZSCORE", dst, b"palermo"]),
20059 "$1\r\n0\r\n",
20060 "the centre is nought away from itself"
20061 );
20062
20063 assert_eq!(
20067 f.run(&[
20068 b"GEOSEARCHSTORE",
20069 dst,
20070 g,
20071 b"FROMLONLAT",
20072 b"0",
20073 b"0",
20074 b"BYRADIUS",
20075 b"1",
20076 b"M",
20077 ]),
20078 ":0\r\n"
20079 );
20080 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
20081 f.run(&[
20082 b"GEOSEARCHSTORE",
20083 dst,
20084 g,
20085 b"FROMLONLAT",
20086 b"15",
20087 b"37",
20088 b"BYRADIUS",
20089 b"200",
20090 b"KM",
20091 ]);
20092 f.run(&[b"SET", plain, b"v"]);
20093 assert_eq!(
20094 f.run(&[
20095 b"GEOSEARCHSTORE",
20096 dst,
20097 plain,
20098 b"FROMLONLAT",
20099 b"15",
20100 b"37",
20101 b"BYRADIUS",
20102 b"200",
20103 b"KM",
20104 ]),
20105 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
20106 );
20107 assert_eq!(
20108 f.run(&[b"ZCARD", dst]),
20109 ":2\r\n",
20110 "and left the destination"
20111 );
20112 }
20113
20114 #[test]
20120 fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
20121 let script: &[&[&[u8]]] = &[
20122 &[
20123 b"TS.CREATE",
20124 b"ts:a",
20125 b"LABELS",
20126 b"sensor",
20127 b"a",
20128 b"room",
20129 b"1",
20130 ],
20131 &[b"TS.CREATE", b"ts:a"],
20132 &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
20133 &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
20134 &[
20135 b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
20136 ],
20137 &[
20138 b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
20139 ],
20140 &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
20141 &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
20142 &[b"TS.GET", b"ts:a"],
20143 &[b"TS.GET", b"gone"],
20144 &[b"TS.RANGE", b"ts:a", b"-", b"+"],
20145 &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
20146 &[
20147 b"TS.RANGE",
20148 b"ts:a",
20149 b"-",
20150 b"+",
20151 b"AGGREGATION",
20152 b"avg",
20153 b"2000",
20154 ],
20155 &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
20156 &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20157 &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20158 &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
20159 &[b"TS.READ", b"ts:a", b"0"],
20160 &[b"TS.READ", b"ts:a", b"+"],
20161 &[b"TS.QUERYINDEX", b"sensor=a"],
20163 &[b"TS.QUERYINDEX", b"room=1"],
20164 &[b"TS.QUERYINDEX", b"room=9"],
20165 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
20166 &[
20167 b"TS.QUERYLABELS",
20168 b"VALUES",
20169 b"sensor",
20170 b"FILTER",
20171 b"room=1",
20172 ],
20173 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
20174 &[
20175 b"TS.MGET",
20176 b"SELECTED_LABELS",
20177 b"sensor",
20178 b"FILTER",
20179 b"sensor=a",
20180 ],
20181 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
20182 &[
20183 b"TS.MREVRANGE",
20184 b"-",
20185 b"+",
20186 b"WITHLABELS",
20187 b"FILTER",
20188 b"sensor=a",
20189 ],
20190 &[
20191 b"TS.MRANGE",
20192 b"-",
20193 b"+",
20194 b"FILTER",
20195 b"room=1",
20196 b"GROUPBY",
20197 b"room",
20198 b"REDUCE",
20199 b"max",
20200 ],
20201 &[b"TS.INFO", b"ts:a"],
20202 &[
20204 b"TS.CREATERULE",
20205 b"ts:a",
20206 b"ts:down",
20207 b"AGGREGATION",
20208 b"avg",
20209 b"1000",
20210 ],
20211 &[b"TS.CREATE", b"ts:down"],
20212 &[
20213 b"TS.CREATERULE",
20214 b"ts:a",
20215 b"ts:down",
20216 b"AGGREGATION",
20217 b"avg",
20218 b"1000",
20219 ],
20220 &[b"TS.ADD", b"ts:a", b"5000", b"4"],
20221 &[b"TS.ADD", b"ts:a", b"6000", b"5"],
20222 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20223 &[b"TS.GET", b"ts:down", b"LATEST"],
20224 &[b"TS.INFO", b"ts:down"],
20225 &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
20226 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20227 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20228 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20229 &[b"TS.DEL", b"ts:a", b"0", b"1000"],
20230 &[b"SET", b"plain", b"v"],
20232 &[b"TS.ADD", b"plain", b"1", b"1"],
20233 &[b"TS.GET", b"plain"],
20234 &[b"TS.READ", b"plain", b"0"],
20235 &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
20236 &[b"TS.RANGE", b"gone", b"-", b"+"],
20237 &[b"TS.INFO", b"gone"],
20238 ];
20239
20240 let mut one = Fixture::new();
20241 let mut many = Fixture::striped(8);
20242 for parts in script {
20243 let a = one.run(parts);
20244 let b = many.run(parts);
20245 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20246 }
20247 }
20248
20249 #[test]
20259 fn a_compaction_rule_across_stripes_reaches_both_ends() {
20260 let mut many = Fixture::striped(8);
20261 let other = apart(&mut many, "src");
20262 let (src, dst) = (b"src".as_slice(), other.as_bytes());
20263 let mut one = Fixture::new();
20264 let mut both = |parts: &[&[u8]]| {
20265 let a = one.run(parts);
20266 let b = many.run(parts);
20267 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20268 a
20269 };
20270
20271 both(&[b"TS.CREATE", src]);
20272 both(&[b"TS.CREATE", dst]);
20273 assert_eq!(
20274 both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
20275 "+OK\r\n"
20276 );
20277 both(&[b"TS.ADD", src, b"1000", b"1"]);
20278 both(&[b"TS.ADD", src, b"1500", b"3"]);
20279 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20282 let open = both(&[b"TS.GET", dst, b"LATEST"]);
20283 assert!(open.contains(":1000"), "the open bucket is folded: {open}");
20284
20285 both(&[b"TS.ADD", src, b"2000", b"5"]);
20288 let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
20289 assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
20290 assert!(got.contains(":1000"), "{got}");
20291
20292 both(&[b"TS.DEL", src, b"1000", b"1999"]);
20294 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20295
20296 assert!(
20299 both(&[b"TS.INFO", dst]).contains("src"),
20300 "the source is named"
20301 );
20302 assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
20303 assert_eq!(
20304 both(&[b"TS.DELETERULE", src, dst]),
20305 "-ERR TSDB: compaction rule does not exist\r\n"
20306 );
20307 }
20308
20309 #[test]
20311 fn a_label_query_across_stripes_finds_every_series() {
20312 let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
20313 let mut many = Fixture::striped(8);
20314 let mut homes: Vec<usize> = names
20315 .iter()
20316 .map(|name| many.server.striped(0).stripe_of(name))
20317 .collect();
20318 homes.sort_unstable();
20319 homes.dedup();
20320 assert!(homes.len() > 1, "the six keys are not all on one stripe");
20321
20322 let mut one = Fixture::new();
20323 let mut both = |parts: &[&[u8]]| {
20324 let a = one.run(parts);
20325 let b = many.run(parts);
20326 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20327 a
20328 };
20329 for name in &names {
20330 both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
20331 both(&[b"TS.ADD", name, b"1000", b"1"]);
20332 }
20333
20334 let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
20335 assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
20336 assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20337 assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20338 assert_eq!(
20339 both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
20340 "*1\r\n$4\r\nroom\r\n"
20341 );
20342 }
20343
20344 #[test]
20351 fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
20352 let script: &[&[&[u8]]] = &[
20353 &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
20354 &[b"HMSET", b"h", b"c", b"3"],
20355 &[b"HSETNX", b"h", b"a", b"9"],
20356 &[b"HSETNX", b"h", b"d", b"4"],
20357 &[b"HGET", b"h", b"a"],
20358 &[b"HGET", b"h", b"nope"],
20359 &[b"HMGET", b"h", b"a", b"nope"],
20360 &[b"HLEN", b"h"],
20361 &[b"HEXISTS", b"h", b"a"],
20362 &[b"HSTRLEN", b"h", b"a"],
20363 &[b"HGETALL", b"h"],
20364 &[b"HKEYS", b"h"],
20365 &[b"HVALS", b"h"],
20366 &[b"HINCRBY", b"h", b"a", b"5"],
20367 &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
20368 &[b"HSCAN", b"h", b"0"],
20369 &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
20370 &[b"HSCAN", b"h", b"0", b"NOVALUES"],
20371 &[b"HDEL", b"h", b"d"],
20372 &[b"HSET", b"one", b"f", b"v"],
20373 &[b"HRANDFIELD", b"one"],
20374 &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
20375 &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
20377 &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
20378 &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
20379 &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20380 &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20381 &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
20382 &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
20383 &[b"HGET", b"h", b"b"],
20384 &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
20386 &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
20387 &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
20388 &[b"HGET", b"h", b"e"],
20389 &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
20391 &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
20392 &[b"HGETALL", b"imp"],
20393 &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
20394 &[b"HIMPORT", b"DISCARD", b"fs"],
20395 &[b"SET", b"plain", b"v"],
20397 &[b"HSET", b"plain", b"a", b"1"],
20398 &[b"HGETALL", b"plain"],
20399 &[b"HGET", b"gone", b"a"],
20400 &[b"HINCRBY", b"h", b"a", b"nan"],
20401 ];
20402
20403 let mut one = Fixture::new();
20404 let mut many = Fixture::striped(8);
20405 one.server.set_clock_ms(1_700_000_000_000);
20409 many.server.set_clock_ms(1_700_000_000_000);
20410 for parts in script {
20411 let a = one.run(parts);
20412 let b = many.run(parts);
20413 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20414 }
20415 }
20416
20417 #[test]
20419 fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20420 let script: &[&[&[u8]]] = &[
20421 &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20422 &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20423 &[b"ARGET", b"a", b"1"],
20424 &[b"ARGET", b"a", b"99"],
20425 &[b"ARMGET", b"a", b"0", b"5", b"99"],
20426 &[b"ARGETRANGE", b"a", b"0", b"7"],
20427 &[b"ARLEN", b"a"],
20428 &[b"ARCOUNT", b"a"],
20429 &[b"ARINSERT", b"a", b"m", b"n"],
20430 &[b"ARSCAN", b"a", b"0", b"20"],
20431 &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20432 &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20433 &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20434 &[b"ARLASTITEMS", b"a", b"2"],
20435 &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20436 &[b"ARNEXT", b"a"],
20437 &[b"ARSEEK", b"a", b"3"],
20438 &[b"AROP", b"a", b"0", b"20", b"USED"],
20439 &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20440 &[b"ARINFO", b"a"],
20441 &[b"ARINFO", b"a", b"FULL"],
20442 &[b"ARDEL", b"a", b"0"],
20443 &[b"ARDELRANGE", b"a", b"1", b"2"],
20444 &[b"ARCOUNT", b"a"],
20445 &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20446 &[b"ARGETRANGE", b"r", b"0", b"9"],
20447 &[b"SET", b"plain", b"v"],
20449 &[b"ARGET", b"plain", b"0"],
20450 &[b"ARSET", b"plain", b"0", b"v"],
20451 &[b"ARGET", b"gone", b"0"],
20452 &[b"ARSET", b"a", b"bad", b"v"],
20453 ];
20454
20455 let mut one = Fixture::new();
20456 let mut many = Fixture::striped(8);
20457 for parts in script {
20458 let a = one.run(parts);
20459 let b = many.run(parts);
20460 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20461 }
20462 }
20463
20464 #[test]
20470 fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20471 let script: &[&[&[u8]]] = &[
20472 &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20473 &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20474 &[b"G.NADD", b"g", b"n3"],
20475 &[b"G.NGET", b"g", b"n1"],
20476 &[b"G.NGET", b"g", b"gone"],
20477 &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20478 &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20479 &[b"G.OUT", b"g", b"n1", b"knows"],
20480 &[b"G.IN", b"g", b"n2", b"knows"],
20481 &[b"G.DEG", b"g", b"n1", b"knows"],
20482 &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20483 &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20484 &[b"G.PATH", b"g", b"n1", b"n3"],
20485 &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20486 &[b"G.NDEL", b"g", b"n3"],
20487 &[b"G.NGET", b"g", b"n3"],
20488 &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20490 &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20491 &[b"VCARD", b"v"],
20492 &[b"VDIM", b"v"],
20493 &[b"VEMB", b"v", b"e1"],
20494 &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20495 &[b"VSIM", b"v", b"ELE", b"e1"],
20496 &[b"VISMEMBER", b"v", b"e1"],
20497 &[b"VISMEMBER", b"v", b"gone"],
20498 &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20499 &[b"VGETATTR", b"v", b"e1"],
20500 &[b"VRANGE", b"v", b"-", b"+"],
20501 &[b"VLINKS", b"v", b"e1"],
20502 &[b"VINFO", b"v"],
20503 &[b"VREM", b"v", b"e2"],
20504 &[b"VCARD", b"v"],
20505 &[b"SET", b"plain", b"v"],
20507 &[b"G.NGET", b"plain", b"n1"],
20508 &[b"VCARD", b"plain"],
20509 &[b"G.NADD", b"gone2", b"n"],
20510 &[b"VEMB", b"gone3", b"e"],
20511 ];
20512
20513 let mut one = Fixture::new();
20514 let mut many = Fixture::striped(8);
20515 for parts in script {
20516 let a = one.run(parts);
20517 let b = many.run(parts);
20518 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20519 }
20520 }
20521
20522 #[test]
20525 fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20526 let script: &[&[&[u8]]] = &[
20527 &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20529 &[b"BF.ADD", b"bf", b"a"],
20530 &[b"BF.ADD", b"bf", b"a"],
20531 &[b"BF.MADD", b"bf", b"b", b"c"],
20532 &[b"BF.EXISTS", b"bf", b"a"],
20533 &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20534 &[b"BF.CARD", b"bf"],
20535 &[b"BF.INFO", b"bf"],
20536 &[b"BF.INFO", b"bf", b"CAPACITY"],
20537 &[b"BF.DEBUG", b"bf"],
20538 &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20539 &[b"BF.EXISTS", b"made", b"x"],
20540 &[b"BF.SCANDUMP", b"bf", b"0"],
20541 &[b"CF.RESERVE", b"cf", b"100"],
20543 &[b"CF.ADD", b"cf", b"a"],
20544 &[b"CF.ADDNX", b"cf", b"a"],
20545 &[b"CF.COUNT", b"cf", b"a"],
20546 &[b"CF.EXISTS", b"cf", b"a"],
20547 &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20548 &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20549 &[b"CF.DEL", b"cf", b"a"],
20550 &[b"CF.COMPACT", b"cf"],
20551 &[b"CF.INFO", b"cf"],
20552 &[b"CF.DEBUG", b"cf"],
20553 &[b"CF.SCANDUMP", b"cf", b"0"],
20554 &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20556 &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20557 &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20558 &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20559 &[b"CMS.INFO", b"cms"],
20560 &[b"TOPK.RESERVE", b"tk", b"3"],
20562 &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20563 &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20564 &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20565 &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20566 &[b"TOPK.LIST", b"tk"],
20567 &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20568 &[b"TOPK.INFO", b"tk"],
20569 &[b"TDIGEST.CREATE", b"td"],
20571 &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20572 &[b"TDIGEST.MIN", b"td"],
20573 &[b"TDIGEST.MAX", b"td"],
20574 &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20575 &[b"TDIGEST.CDF", b"td", b"3"],
20576 &[b"TDIGEST.RANK", b"td", b"3"],
20577 &[b"TDIGEST.REVRANK", b"td", b"3"],
20578 &[b"TDIGEST.BYRANK", b"td", b"0"],
20579 &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20580 &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20581 &[b"TDIGEST.INFO", b"td"],
20582 &[b"TDIGEST.RESET", b"td"],
20583 &[b"TDIGEST.MIN", b"td"],
20584 &[b"SET", b"plain", b"v"],
20586 &[b"BF.ADD", b"plain", b"a"],
20587 &[b"CF.ADD", b"plain", b"a"],
20588 &[b"CMS.QUERY", b"plain", b"a"],
20589 &[b"TOPK.ADD", b"plain", b"a"],
20590 &[b"TDIGEST.ADD", b"plain", b"1"],
20591 &[b"CMS.INFO", b"gone"],
20592 &[b"TOPK.INFO", b"gone"],
20593 &[b"TDIGEST.INFO", b"gone"],
20594 ];
20595
20596 let mut one = Fixture::new();
20597 let mut many = Fixture::striped(8);
20598 for parts in script {
20599 let a = one.run(parts);
20600 let b = many.run(parts);
20601 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20602 }
20603 }
20604
20605 #[test]
20612 fn a_sketch_merge_across_stripes_reads_every_source() {
20613 let mut many = Fixture::striped(8);
20614 let other = apart(&mut many, "s1");
20615 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20616 let mut one = Fixture::new();
20617 let mut both = |parts: &[&[u8]]| {
20618 let a = one.run(parts);
20619 let b = many.run(parts);
20620 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20621 a
20622 };
20623
20624 for key in [b"cd".as_slice(), s1, s2] {
20628 both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20629 }
20630 both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20631 both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20632 assert_eq!(
20633 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20634 "+OK\r\n",
20635 "the merge took both sources"
20636 );
20637 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20638 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20640 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20641 both(&[b"SET", b"plain", b"v"]);
20643 assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20644 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20645
20646 both(&[b"FLUSHALL"]);
20649 both(&[b"TDIGEST.CREATE", b"td"]);
20650 both(&[b"TDIGEST.CREATE", s1]);
20651 both(&[b"TDIGEST.CREATE", s2]);
20652 both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20653 both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20654 assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20655 assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20656 assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20657 }
20658
20659 #[test]
20666 fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20667 let script: &[&[&[u8]]] = &[
20668 &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20669 &[b"SORT", b"l"],
20670 &[b"SORT", b"l", b"DESC"],
20671 &[b"SORT", b"l", b"ALPHA"],
20672 &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20673 &[b"SORT_RO", b"l"],
20674 &[
20677 b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20678 ],
20679 &[b"SORT", b"l", b"BY", b"w_*"],
20680 &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20681 &[b"DEL", b"w_2"],
20682 &[b"SORT", b"l", b"BY", b"w_*"],
20683 &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20686 &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20687 &[b"HSET", b"h_1", b"f", b"9"],
20689 &[b"HSET", b"h_2", b"f", b"8"],
20690 &[b"HSET", b"h_3", b"f", b"7"],
20691 &[b"HSET", b"h_10", b"f", b"6"],
20692 &[b"SORT", b"l", b"BY", b"h_*->f"],
20693 &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20694 &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20696 &[b"LRANGE", b"out", b"0", b"-1"],
20697 &[b"SORT", b"l", b"STORE", b"l"],
20698 &[b"LRANGE", b"l", b"0", b"-1"],
20699 &[b"SORT", b"missing", b"STORE", b"out"],
20702 &[b"EXISTS", b"out"],
20703 &[b"SADD", b"s", b"c", b"a", b"b"],
20706 &[b"SORT", b"s", b"ALPHA"],
20707 &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20708 &[b"LRANGE", b"out", b"0", b"-1"],
20709 &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20710 &[b"SORT", b"z", b"BY", b"nosort"],
20711 &[b"SORT", b"z", b"ALPHA", b"DESC"],
20712 &[b"SET", b"str", b"v"],
20715 &[b"SORT", b"str"],
20716 &[b"RPUSH", b"words", b"one", b"two"],
20717 &[b"SORT", b"words"],
20718 &[b"SORT_RO", b"l", b"STORE", b"out"],
20719 ];
20720
20721 let mut one = Fixture::new();
20722 let mut many = Fixture::striped(8);
20723 for parts in script {
20724 let a = one.run(parts);
20725 let b = many.run(parts);
20726 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20727 }
20728 }
20729
20730 #[test]
20737 fn a_sort_across_stripes_reads_every_pattern_key() {
20738 let mut f = Fixture::striped(8);
20739 let out = apart(&mut f, "l");
20740 let (list, dest) = (b"l".as_slice(), out.as_bytes());
20741
20742 f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20743 f.run(&[
20744 b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20745 ]);
20746 f.run(&[
20747 b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20748 ]);
20749
20750 let db = f.server.striped(0);
20753 let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20754 .iter()
20755 .map(|k| db.stripe_of(k.as_slice()))
20756 .collect();
20757 assert!(
20758 weights.iter().any(|s| *s != weights[0]),
20759 "the four weight keys all landed on one stripe, so this proves nothing"
20760 );
20761
20762 assert_eq!(
20763 f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
20764 "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
20765 "the order came off the weights and the answer off the data keys"
20766 );
20767 assert_eq!(
20768 f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20769 ":4\r\n"
20770 );
20771 assert_eq!(
20772 f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20773 "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20774 "the destination is on a stripe of its own and got the whole answer"
20775 );
20776 }
20777
20778 #[test]
20786 fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20787 let mut f = Fixture::striped(8);
20788 let other = apart(&mut f, "h");
20789 let (first, second) = (b"h".as_slice(), other.as_bytes());
20790
20791 assert_eq!(
20792 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
20793 "+OK\r\n"
20794 );
20795 assert_eq!(
20796 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
20797 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
20798 "the read comes off one stripe and has to answer for all of them"
20799 );
20800 for key in [first, second] {
20801 f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
20802 assert_eq!(
20803 f.run(&[b"OBJECT", b"ENCODING", key]),
20804 "$8\r\nlistpack\r\n",
20805 "two fields is still under the ladder"
20806 );
20807 f.run(&[b"HSET", key, b"c", b"3"]);
20808 assert_eq!(
20809 f.run(&[b"OBJECT", b"ENCODING", key]),
20810 "$9\r\nhashtable\r\n",
20811 "three fields is over it, on whichever stripe the key is on"
20812 );
20813 }
20814
20815 assert_eq!(
20818 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
20819 "+OK\r\n"
20820 );
20821 let db = f.server.striped(0);
20822 assert!(
20823 (0..db.width()).all(|i| db.hold_stripe(i).policy().name() == "allkeys-lru"),
20824 "a stripe kept the old policy"
20825 );
20826 }
20827
20828 fn held(f: &Fixture, name: &[u8]) -> (usize, u32) {
20834 let search = f.server.search.lock();
20835 let index = search.named(name).expect("the index is there");
20836 (index.held.docs.len(), index.held.docs.last())
20837 }
20838
20839 #[test]
20842 fn a_hash_that_is_written_reaches_the_index_that_follows_it() {
20843 let mut f = Fixture::new();
20844 f.run(&[
20845 b"FT.CREATE",
20846 b"ix",
20847 b"PREFIX",
20848 b"1",
20849 b"p:",
20850 b"SCHEMA",
20851 b"t",
20852 b"TEXT",
20853 ]);
20854 f.run(&[b"HSET", b"p:1", b"t", b"running dogs"]);
20855 assert_eq!(held(&f, b"ix"), (1, 1));
20856 f.run(&[b"HSET", b"other:1", b"t", b"running dogs"]);
20857 assert_eq!(held(&f, b"ix"), (1, 1));
20858
20859 f.run(&[b"HSET", b"p:1", b"u", b"beta"]);
20862 f.run(&[b"HDEL", b"p:1", b"u"]);
20863 assert_eq!(held(&f, b"ix"), (1, 3));
20864 let search = f.server.search.lock();
20865 let index = search.named(b"ix").expect("there");
20866 assert_eq!(index.held.docs.id(b"p:1"), Some(3));
20867 }
20868
20869 #[test]
20872 fn a_fresh_index_reads_the_keys_that_were_already_there() {
20873 let mut f = Fixture::new();
20874 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20875 f.run(&[b"SET", b"p:str", b"not a hash"]);
20876 f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
20877 f.run(&[
20878 b"FT.CREATE",
20879 b"ix",
20880 b"PREFIX",
20881 b"1",
20882 b"p:",
20883 b"SCHEMA",
20884 b"t",
20885 b"TEXT",
20886 ]);
20887
20888 assert_eq!(held(&f, b"ix"), (1, 1));
20889 let search = f.server.search.lock();
20890 let index = search.named(b"ix").expect("there");
20891 assert_eq!(index.trouble.whole().failures(), 0);
20892 }
20893
20894 #[test]
20897 fn an_index_that_skipped_the_scan_fills_up_on_the_next_write() {
20898 let mut f = Fixture::new();
20899 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20900 f.run(&[
20901 b"FT.CREATE",
20902 b"ix",
20903 b"PREFIX",
20904 b"1",
20905 b"p:",
20906 b"SKIPINITIALSCAN",
20907 b"SCHEMA",
20908 b"t",
20909 b"TEXT",
20910 ]);
20911 assert_eq!(held(&f, b"ix"), (0, 0));
20912 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20913 assert_eq!(held(&f, b"ix"), (1, 1));
20914 }
20915
20916 #[test]
20924 fn only_a_real_change_gives_the_document_a_new_number() {
20925 let mut f = Fixture::new();
20926 f.run(&[
20927 b"FT.CREATE",
20928 b"ix",
20929 b"PREFIX",
20930 b"1",
20931 b"p:",
20932 b"SCHEMA",
20933 b"t",
20934 b"TEXT",
20935 ]);
20936 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20937 assert_eq!(held(&f, b"ix"), (1, 1));
20938
20939 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20940 assert_eq!(held(&f, b"ix"), (1, 2), "the same value still rewrites");
20941
20942 for quiet in [
20943 vec![b"HSETNX".as_slice(), b"p:1", b"t", b"other"],
20944 vec![b"HDEL".as_slice(), b"p:1", b"nosuch"],
20945 vec![b"HGET".as_slice(), b"p:1", b"t"],
20946 vec![b"HGETALL".as_slice(), b"p:1"],
20947 vec![b"HEXPIRE".as_slice(), b"p:1", b"100", b"FIELDS", b"1", b"t"],
20948 vec![b"HPERSIST".as_slice(), b"p:1", b"FIELDS", b"1", b"t"],
20949 vec![
20950 b"HGETEX".as_slice(),
20951 b"p:1",
20952 b"EX",
20953 b"100",
20954 b"FIELDS",
20955 b"1",
20956 b"t",
20957 ],
20958 vec![b"HGETDEL".as_slice(), b"p:1", b"FIELDS", b"1", b"nosuch"],
20959 ] {
20960 f.run(&quiet);
20961 assert_eq!(held(&f, b"ix"), (1, 2), "{:?} moved the document", quiet[0]);
20962 }
20963
20964 f.run(&[b"HSET", b"p:2", b"n", b"1"]);
20966 f.run(&[b"HINCRBY", b"p:2", b"n", b"1"]);
20967 assert_eq!(held(&f, b"ix"), (2, 4));
20968 f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"n"]);
20974 assert_eq!(held(&f, b"ix"), (1, 5));
20975 }
20976
20977 #[test]
20983 fn a_key_emptied_by_a_deadline_spends_a_number_and_one_emptied_by_hdel_does_not() {
20984 fn refused(f: &Fixture, name: &[u8]) -> u64 {
20986 let search = f.server.search.lock();
20987 let index = search.named(name).expect("the index is there");
20988 index.trouble.whole().failures()
20989 }
20990
20991 let mut f = Fixture::new();
20992 f.run(&[
20993 b"FT.CREATE",
20994 b"ix",
20995 b"PREFIX",
20996 b"1",
20997 b"p:",
20998 b"SCHEMA",
20999 b"t",
21000 b"TEXT",
21001 ]);
21002 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21003 assert_eq!(held(&f, b"ix"), (1, 1));
21004 f.run(&[b"HDEL", b"p:1", b"t"]);
21005 assert_eq!(
21006 held(&f, b"ix"),
21007 (0, 1),
21008 "HDEL of the last field spends none"
21009 );
21010 assert_eq!(refused(&f, b"ix"), 1, "and is counted as a refusal");
21011
21012 f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21013 assert_eq!(held(&f, b"ix"), (1, 2));
21014 f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"t"]);
21015 assert_eq!(held(&f, b"ix"), (0, 3), "a deadline spends one");
21016 assert_eq!(refused(&f, b"ix"), 1, "and is counted as nothing");
21017
21018 f.run(&[b"HSET", b"p:3", b"t", b"alpha"]);
21019 assert_eq!(held(&f, b"ix"), (1, 4));
21020 f.run(&[b"HGETDEL", b"p:3", b"FIELDS", b"1", b"t"]);
21021 assert_eq!(held(&f, b"ix"), (0, 5), "and so does HGETDEL");
21022
21023 f.run(&[b"HSET", b"p:4", b"t", b"alpha", b"u", b"beta"]);
21026 assert_eq!(held(&f, b"ix"), (1, 6));
21027 f.run(&[b"HEXPIRE", b"p:4", b"0", b"FIELDS", b"2", b"t", b"u"]);
21028 assert_eq!(held(&f, b"ix"), (0, 7));
21029 assert_eq!(refused(&f, b"ix"), 1);
21030 }
21031
21032 #[test]
21036 fn a_field_written_already_past_its_deadline_moves_the_number_twice() {
21037 let mut f = Fixture::new();
21038 f.run(&[
21039 b"FT.CREATE",
21040 b"ix",
21041 b"PREFIX",
21042 b"1",
21043 b"p:",
21044 b"SCHEMA",
21045 b"t",
21046 b"TEXT",
21047 b"u",
21048 b"TEXT",
21049 ]);
21050 f.run(&[b"HSET", b"p:1", b"u", b"keepme"]);
21051 assert_eq!(held(&f, b"ix"), (1, 1));
21052 f.run(&[
21053 b"HSETEX", b"p:1", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21054 ]);
21055 assert_eq!(
21056 held(&f, b"ix"),
21057 (1, 3),
21058 "the key lived and the field did not"
21059 );
21060
21061 f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21063 assert_eq!(held(&f, b"ix"), (2, 4));
21064 f.run(&[
21065 b"HSETEX", b"p:2", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21066 ]);
21067 assert_eq!(held(&f, b"ix"), (1, 6));
21068 }
21069
21070 #[test]
21073 fn a_hash_that_will_not_read_is_counted_where_ft_info_reports_it() {
21074 let mut f = Fixture::new();
21075 f.run(&[
21076 b"FT.CREATE",
21077 b"ix",
21078 b"PREFIX",
21079 b"1",
21080 b"p:",
21081 b"SCHEMA",
21082 b"n",
21083 b"NUMERIC",
21084 ]);
21085 f.run(&[b"HSET", b"p:1", b"n", b"notanumber"]);
21086 assert_eq!(held(&f, b"ix"), (0, 0));
21087
21088 let reply = f.run(&[b"FT.INFO", b"ix"]);
21089 assert!(
21090 reply.contains("SEARCH_NUMERIC_VALUE_INVALID Invalid numeric value: 'notanumber'"),
21091 "{reply}"
21092 );
21093 assert!(reply.contains("hash_indexing_failures"), "{reply}");
21094 }
21095
21096 #[test]
21099 fn an_index_can_only_be_made_on_database_zero() {
21100 let mut f = Fixture::new();
21101 f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]);
21102 f.run(&[b"SELECT", b"1"]);
21103 let refused = "-Cannot create index on db != 0\r\n";
21104 assert_eq!(
21105 f.run(&[b"FT.CREATE", b"jx", b"SCHEMA", b"t", b"TEXT"]),
21106 refused
21107 );
21108 assert_eq!(
21110 f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21111 refused
21112 );
21113 assert_eq!(
21115 f.run(&[b"FT.CREATE", b"zz", b"BOGUS", b"SCHEMA", b"t", b"TEXT"]),
21116 refused
21117 );
21118 assert_eq!(
21120 f.run(&[b"FT._CREATEIFNX", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21121 "+OK\r\n"
21122 );
21123 assert_eq!(f.server.search.lock().len(), 1);
21124 }
21125
21126 #[test]
21132 fn the_scan_is_one_database_and_the_following_is_all_of_them() {
21133 let mut f = Fixture::new();
21134 f.run(&[b"SELECT", b"1"]);
21135 f.run(&[b"HSET", b"p:9", b"t", b"on one"]);
21136 f.run(&[b"SELECT", b"0"]);
21137 f.run(&[b"HSET", b"p:0", b"t", b"on zero"]);
21138 f.run(&[
21139 b"FT.CREATE",
21140 b"ix",
21141 b"PREFIX",
21142 b"1",
21143 b"p:",
21144 b"SCHEMA",
21145 b"t",
21146 b"TEXT",
21147 ]);
21148 assert_eq!(held(&f, b"ix"), (1, 1), "the scan read database zero only");
21149
21150 f.run(&[b"SELECT", b"1"]);
21151 f.run(&[b"HSET", b"p:8", b"t", b"later"]);
21152 assert_eq!(
21153 held(&f, b"ix"),
21154 (2, 2),
21155 "and then it follows every database"
21156 );
21157 }
21158}