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: AtomicUsize,
411 dirty: u64,
426 conn_bytes: AtomicUsize,
433 maxmemory: AtomicU64,
441 store: Lock<Option<Box<StoreSource>>>,
458 maxstore: AtomicU64,
480 used: AtomicUsize,
497 evict_db: AtomicUsize,
507 expire_db: usize,
514 expire_ms: u64,
517 waiters: Lock<Waiters>,
525 parked: AtomicUsize,
538 peers: Lock<migrate::Peers>,
550 locals: Box<[Local]>,
556 claimed: AtomicUsize,
558 dir: PathBuf,
566 backup: Lock<backup::State>,
576 sealed: AtomicBool,
586 search: Lock<Registry>,
601 stopping: AtomicBool,
610}
611
612impl Server {
613 #[must_use]
615 pub fn new() -> Server {
616 let clock = Clock::system();
617 Server {
618 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
619 width: 1,
620 clock,
621 started_ms: clock.now_ms(),
622 next_db: AtomicUsize::new(0),
623 dirty: ALL_DATABASES,
624 conn_bytes: AtomicUsize::new(0),
625 maxmemory: AtomicU64::new(0),
626 store: Lock::new(None),
627 maxstore: AtomicU64::new(NO_MAXSTORE),
628 used: AtomicUsize::new(0),
629 evict_db: AtomicUsize::new(0),
630 expire_db: 0,
631 expire_ms: 0,
632 waiters: Lock::default(),
633 parked: AtomicUsize::new(0),
634 peers: Lock::default(),
635 locals: one_thread(),
636 claimed: AtomicUsize::new(0),
637 dir: working_dir(),
638 backup: Lock::default(),
639 sealed: AtomicBool::new(false),
640 search: Lock::new(Registry::new()),
641 stopping: AtomicBool::new(false),
642 }
643 }
644
645 #[must_use]
656 pub fn with_width(width: usize) -> Server {
657 let clock = Clock::system();
658 let mut server = Server::new();
659 server.dbs = (0..DATABASES)
660 .map(|_| Db::with_clock(clock, width))
661 .collect();
662 server.width = server.dbs[0].width();
663 server
664 }
665
666 #[must_use]
668 pub fn with_clock(clock: Clock) -> Server {
669 Server {
670 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
671 width: 1,
672 clock,
673 started_ms: clock.now_ms(),
674 next_db: AtomicUsize::new(0),
675 dirty: ALL_DATABASES,
676 conn_bytes: AtomicUsize::new(0),
677 maxmemory: AtomicU64::new(0),
678 store: Lock::new(None),
679 maxstore: AtomicU64::new(NO_MAXSTORE),
680 used: AtomicUsize::new(0),
681 evict_db: AtomicUsize::new(0),
682 expire_db: 0,
683 expire_ms: 0,
684 waiters: Lock::default(),
685 parked: AtomicUsize::new(0),
686 peers: Lock::default(),
687 locals: one_thread(),
688 claimed: AtomicUsize::new(0),
689 dir: working_dir(),
690 backup: Lock::default(),
691 sealed: AtomicBool::new(false),
692 search: Lock::new(Registry::new()),
693 stopping: AtomicBool::new(false),
694 }
695 }
696
697 pub fn striped(&self, i: usize) -> &Db {
721 self.mine().mark(1u64 << i);
722 &self.dbs[i]
723 }
724
725 fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
732 self.dbs
733 .iter()
734 .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
735 }
736
737 const fn slots(&self) -> usize {
743 DATABASES * self.width
744 }
745
746 const fn slot_db(&self, i: usize) -> usize {
748 i / self.width
749 }
750
751 fn slot_mut(&mut self, i: usize) -> &mut Keyspace {
753 let (db, stripe) = (i / self.width, i % self.width);
754 self.dbs[db].stripe_mut(stripe)
755 }
756
757 fn slot(&self, i: usize) -> Held<'_, Keyspace> {
759 let (db, stripe) = (i / self.width, i % self.width);
760 self.dbs[db].hold_stripe(stripe)
761 }
762
763 #[must_use]
765 pub fn dir(&self) -> &Path {
766 &self.dir
767 }
768
769 pub fn set_dir(&mut self, dir: PathBuf) {
776 self.dir = dir;
777 }
778
779 pub fn backup_expire(&self) {
785 backup::expire(self);
786 }
787
788 pub fn stop(&self) {
794 self.stopping.store(true, Release);
795 }
796
797 #[must_use]
803 pub fn stopping(&self) -> bool {
804 self.stopping.load(Acquire)
805 }
806
807 #[must_use]
814 pub fn striped_ref(&self, i: usize) -> &Db {
815 &self.dbs[i]
816 }
817
818 fn settings(&self) -> Held<'_, Keyspace> {
826 self.dbs[0].hold_stripe(0)
827 }
828
829 pub fn refresh_clock(&mut self) {
835 self.clock.refresh();
836 let now = self.clock.now_ms();
837 for db in &mut self.dbs {
838 db.set_clock_ms(now);
839 }
840 }
841
842 pub fn advance_clock_ms(&mut self, ms: u64) {
848 let now = self.clock.now_ms() + ms;
849 self.set_clock_ms(now);
850 }
851
852 pub fn set_clock_ms(&mut self, ms: u64) {
860 self.clock.set(ms);
861 for db in &mut self.dbs {
862 db.set_clock_ms(ms);
863 }
864 }
865
866 #[must_use]
868 pub fn uptime_secs(&self) -> u64 {
869 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
870 }
871
872 #[must_use]
880 pub fn memory_bytes(&self) -> usize {
881 self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes()
882 }
883
884 #[must_use]
890 pub fn dataset_bytes(&self) -> usize {
891 self.keyspaces()
892 .map(|db| db.map().arena().live_bytes() as usize)
893 .sum()
894 }
895
896 #[must_use]
898 pub fn arena_bytes(&self) -> usize {
899 self.keyspaces()
900 .map(|db| db.map().arena().reserved_bytes() as usize)
901 .sum()
902 }
903
904 #[must_use]
906 pub fn index_bytes(&self) -> usize {
907 self.keyspaces()
908 .map(|db| db.map().index().memory_bytes())
909 .sum()
910 }
911
912 #[must_use]
919 pub fn compaction(&self) -> yo_kv::Compaction {
920 self.keyspaces().map(|db| db.map().compaction()).fold(
921 yo_kv::Compaction::default(),
922 |a, b| yo_kv::Compaction {
923 walked: a.walked + b.walked,
924 moved: a.moved + b.moved,
925 bytes: a.bytes + b.bytes,
926 },
927 )
928 }
929
930 #[must_use]
932 pub fn segment_count(&self) -> usize {
933 self.keyspaces()
934 .map(|db| db.map().arena().resident_segments())
935 .sum()
936 }
937
938 #[must_use]
940 pub fn conn_bytes(&self) -> usize {
941 self.conn_bytes.load(Relaxed)
942 }
943
944 pub fn note_conn_bytes(&mut self, delta: isize) {
952 self.conn_bytes
958 .store(self.conn_bytes().saturating_add_signed(delta), Relaxed);
959 }
960
961 #[must_use]
963 pub fn expired_keys(&self) -> u64 {
964 self.keyspaces().map(|db| db.expired_keys()).sum()
965 }
966
967 #[must_use]
969 pub fn evicted_keys(&self) -> u64 {
970 self.keyspaces().map(|db| db.evicted_keys()).sum()
971 }
972
973 pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
979 (0..table::count())
980 .map(|at| (table::name_at(at), self.command_stat(at)))
981 .filter(|(_, row)| row.seen())
982 }
983
984 fn command_stat(&self, at: usize) -> CommandStat {
986 let mut sum = CommandStat::default();
987 for thread in &self.locals {
988 let row = &thread.cmdstats.0[at];
989 sum.calls += row.calls.get();
990 sum.rejected += row.rejected.get();
991 sum.failed += row.failed.get();
992 }
993 sum
994 }
995
996 pub fn counted(&self) -> &Stats {
1005 &self.mine().stats
1006 }
1007
1008 fn mine(&self) -> &Local {
1010 let mut slot = SLOT.get();
1011 if slot == usize::MAX {
1012 slot = self.claimed.fetch_add(1, Relaxed);
1013 SLOT.set(slot);
1014 }
1015 &self.locals[slot % self.locals.len()]
1016 }
1017
1018 #[must_use]
1020 pub fn totals(&self) -> Totals {
1021 let mut sum = Totals::default();
1022 for thread in &self.locals {
1023 sum.clients += thread.stats.clients.get();
1024 sum.connections += thread.stats.connections.get();
1025 sum.commands += thread.stats.commands.get();
1026 }
1027 sum
1028 }
1029
1030 pub fn reset_stats(&self) {
1037 for thread in &self.locals {
1038 thread.stats.connections.zero();
1039 thread.stats.commands.zero();
1040 }
1041 }
1042
1043 pub fn set_threads(&mut self, threads: usize) {
1050 self.locals = slots(threads);
1051 self.claimed = AtomicUsize::new(0);
1052 }
1053
1054 #[must_use]
1056 pub fn maxmemory(&self) -> u64 {
1057 self.maxmemory.load(Relaxed)
1058 }
1059
1060 pub fn set_maxmemory(&self, bytes: u64) {
1074 self.maxmemory.store(bytes, Relaxed);
1075 for db in &self.dbs {
1076 db.track_memory(bytes != 0);
1077 }
1078 self.used.store(self.settled_memory(), Relaxed);
1079 }
1080
1081 pub fn set_store_source(
1091 &mut self,
1092 source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
1093 ) {
1094 *self.store.lock() = Some(Box::new(source));
1095 }
1096
1097 #[must_use]
1099 pub fn has_store_source(&self) -> bool {
1100 self.store.lock().is_some()
1101 }
1102
1103 fn attach_store(&self, at: usize) {
1110 if self.slot(at).store_bytes().is_some() {
1111 return;
1112 }
1113 let mut source = self.store.lock();
1117 let Some(source) = source.as_mut() else {
1118 return;
1119 };
1120 if let Some(blocks) = source(at) {
1121 self.slot(at).attach(blocks);
1122 }
1123 }
1124
1125 #[must_use]
1127 pub fn maxstore(&self) -> Option<u64> {
1128 match self.maxstore.load(Relaxed) {
1129 NO_MAXSTORE => None,
1130 bytes => Some(bytes),
1131 }
1132 }
1133
1134 pub fn set_maxstore(&self, bytes: Option<u64>) {
1140 self.maxstore.store(bytes.unwrap_or(NO_MAXSTORE), Relaxed);
1141 }
1142
1143 #[must_use]
1149 pub fn store_bytes(&self) -> u64 {
1150 self.keyspaces().filter_map(|db| db.store_bytes()).sum()
1151 }
1152
1153 #[must_use]
1168 pub fn cold_stats(&self) -> yo_kv::tier::Stats {
1169 let mut total = yo_kv::tier::Stats::default();
1170 for db in self.keyspaces() {
1171 let Some(tier) = db.tier() else { continue };
1172 let s = tier.stats();
1173 total.demoted += s.demoted;
1174 total.promoted += s.promoted;
1175 total.faults += s.faults;
1176 total.served += s.served;
1177 total.bytes_out += s.bytes_out;
1178 total.bytes_in += s.bytes_in;
1179 }
1180 total
1181 }
1182
1183 #[must_use]
1190 pub fn regime(&self) -> &'static str {
1191 if (0..self.slots()).any(|at| self.migrates(at)) {
1192 "migrate"
1193 } else {
1194 "evict"
1195 }
1196 }
1197
1198 fn migrates(&self, at: usize) -> bool {
1210 let cap = self.maxstore();
1211 if cap == Some(0) {
1212 return false;
1213 }
1214 let bytes = self.slot(at).store_bytes();
1218 match bytes {
1219 Some(held) => cap.is_none_or(|cap| held < cap),
1220 None => self.store.lock().is_some(),
1224 }
1225 }
1226
1227 pub fn refresh_memory(&self) {
1232 if self.maxmemory() != 0 {
1233 self.used.store(self.settled_memory(), Relaxed);
1234 }
1235 }
1236
1237 fn settled_memory(&self) -> usize {
1244 self.keyspaces()
1245 .map(|mut db| db.settled_memory_bytes())
1246 .sum::<usize>()
1247 + self.conn_bytes()
1248 }
1249
1250 pub fn make_room(&self) -> bool {
1284 let limit = self.maxmemory();
1285 if limit == 0 || self.used.load(Relaxed) as u64 <= limit {
1286 return true;
1287 }
1288 let mut used = self.settled_memory();
1293 self.used.store(used, Relaxed);
1294 let mut budget = EVICT_BUDGET;
1295 while used as u64 > limit {
1296 let over = used - limit as usize;
1297 if !self.relieve_step(over) {
1298 return false;
1299 }
1300 self.compact_hard_step();
1301 used = self.settled_memory();
1302 self.used.store(used, Relaxed);
1303 budget -= 1;
1304 if budget == 0 {
1305 break;
1306 }
1307 }
1308 true
1309 }
1310
1311 fn relieve_step(&self, over: usize) -> bool {
1328 let from = self.evict_db.load(Relaxed);
1329 for turn in 0..self.slots() {
1330 let i = (from + turn) % self.slots();
1331 let used = !self.slot(i).is_empty();
1334 let gave = if used && self.migrates(i) {
1335 self.attach_store(i);
1336 self.slot(i)
1341 .relieve(over)
1342 .is_ok_and(yo_kv::tier::Relief::made_room)
1343 } else {
1344 self.slot(i).evict_one()
1345 };
1346 if gave {
1347 self.evict_db.store((i + 1) % self.slots(), Relaxed);
1348 self.mine().mark(1u64 << self.slot_db(i));
1349 return true;
1350 }
1351 }
1352 false
1353 }
1354
1355 pub fn expire_slice(&mut self, budget: usize) -> usize {
1370 let now = self.clock.now_ms();
1371 if now == self.expire_ms {
1372 return 0;
1373 }
1374 self.expire_ms = now;
1375 self.expire_step(budget)
1376 }
1377
1378 pub fn expire_step(&mut self, budget: usize) -> usize {
1394 let mut spent = 0;
1395 for turn in 0..self.slots() {
1396 if spent >= budget {
1397 break;
1398 }
1399 let i = (self.expire_db + turn) % self.slots();
1400 let c = self.slot_mut(i).expire_cycle(budget - spent);
1401 spent += c.examined;
1402 if c.expired > 0 {
1403 self.expire_db = (i + 1) % self.slots();
1404 self.dirty |= 1u64 << self.slot_db(i);
1405 }
1406 }
1407 spent
1408 }
1409
1410 fn compact_hard_step(&self) -> Option<usize> {
1416 let from = self.next_db.load(Relaxed);
1417 for turn in 0..self.slots() {
1418 let i = (from + turn) % self.slots();
1419 if let Some(moved) = self.slot(i).compact_hard() {
1420 self.next_db.store((i + 1) % self.slots(), Relaxed);
1421 return Some(moved);
1422 }
1423 }
1424 None
1425 }
1426
1427 fn collect_marks(&mut self) {
1436 let mut marked = 0;
1437 for thread in &self.locals {
1438 marked |= thread.dirty.swap(0, Relaxed);
1439 }
1440 self.dirty |= marked;
1441 }
1442
1443 pub fn compact_step(&mut self) -> Option<usize> {
1456 self.collect_marks();
1457 let from = self.next_db.load(Relaxed);
1458 for turn in 0..self.slots() {
1459 let i = (from + turn) % self.slots();
1460 let at = self.slot_db(i);
1464 if self.dirty & (1 << at) == 0 {
1465 continue;
1466 }
1467 if let Some(moved) = self.slot_mut(i).compact_step() {
1468 self.next_db.store((i + 1) % self.slots(), Relaxed);
1469 return Some(moved);
1470 }
1471 if i % self.width == self.width - 1 {
1475 self.dirty &= !(1u64 << at);
1476 }
1477 }
1478 None
1479 }
1480}
1481
1482impl Default for Server {
1483 fn default() -> Server {
1484 Server::new()
1485 }
1486}
1487
1488pub struct Session {
1490 db: usize,
1491 id: u64,
1492 name: Vec<u8>,
1493 sets: himport::Fieldsets,
1499}
1500
1501impl Session {
1502 #[must_use]
1504 pub fn new(id: u64) -> Session {
1505 Session {
1506 db: 0,
1507 id,
1508 name: Vec::new(),
1509 sets: himport::Fieldsets::default(),
1510 }
1511 }
1512
1513 #[must_use]
1515 pub const fn id(&self) -> u64 {
1516 self.id
1517 }
1518
1519 #[must_use]
1521 pub const fn db(&self) -> usize {
1522 self.db
1523 }
1524
1525 #[must_use]
1527 pub fn name(&self) -> &[u8] {
1528 &self.name
1529 }
1530
1531 pub fn reset(&mut self) {
1536 self.db = 0;
1537 self.name.clear();
1538 self.sets.clear();
1542 }
1543
1544 fn set_name(&mut self, name: &[u8]) {
1546 yo_alloc::allow(|| {
1547 self.name.clear();
1548 self.name.extend_from_slice(name);
1549 });
1550 }
1551}
1552
1553pub fn execute(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1558 if args.is_empty() {
1561 return Flow::Continue;
1562 }
1563 resolved(server, session, lookup(args.name()), args, out)
1564}
1565
1566pub fn resolved(
1578 server: &Server,
1579 session: &mut Session,
1580 spec: Option<&'static Spec>,
1581 args: Args<'_>,
1582 out: &mut Out,
1583) -> Flow {
1584 if args.is_empty() {
1585 return Flow::Continue;
1586 }
1587 server.mine().stats.commands.bump();
1588
1589 let Some(spec) = spec else {
1590 write_error(out, &args::unknown_command(args));
1591 return Flow::Continue;
1592 };
1593 if !arity_ok(spec, args.len()) {
1594 server.mine().cmdstats.at(spec).rejected.bump();
1595 write_error(out, &args::wrong_arity(spec.name));
1596 return Flow::Continue;
1597 }
1598
1599 if server.maxmemory() != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1609 server.mine().cmdstats.at(spec).rejected.bump();
1610 out.error_line(b"OOM ", OOM);
1611 return Flow::Continue;
1612 }
1613
1614 server.mine().mark(match spec.group {
1621 "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1622 | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1623 1u64 << session.db
1624 }
1625 _ => ALL_DATABASES,
1626 });
1627
1628 let mark = out.len();
1629 let done = if spec.flags.contains(&"blocking") {
1636 blocking::execute(server, session, spec, args, out)
1637 } else {
1638 match spec.group {
1639 "string" => {
1640 let db = session.db;
1641 strings::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1642 }
1643 "bitmap" => {
1647 let db = session.db;
1648 bits::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1649 }
1650 "hyperloglog" => {
1653 let db = session.db;
1654 hll::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1655 }
1656 "set" => {
1657 let db = session.db;
1658 sets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1659 }
1660 "hash" if spec.name == "himport" => {
1665 let db = session.db;
1666 himport::execute(&server.dbs[db], &mut session.sets, args, out)
1667 .map(|()| Flow::Continue)
1668 }
1669 "hash" => {
1674 let db = session.db;
1675 let changed = hashes::execute(&server.dbs[db], spec, args, out);
1676 changed.map(|changed| {
1677 indexing::changed(server, db, args.get(1), changed);
1678 Flow::Continue
1679 })
1680 }
1681 "list" => {
1682 let db = session.db;
1683 lists::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1684 }
1685 "zset" => {
1686 let db = session.db;
1687 zsets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1688 }
1689 "geo" => {
1693 let db = session.db;
1694 geo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1695 }
1696 "array" => {
1697 let db = session.db;
1698 arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1699 }
1700 "graph" => {
1701 let db = session.db;
1702 graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1703 }
1704 "json" => {
1708 let db = session.db;
1709 json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1710 }
1711 "vector" => {
1712 let db = session.db;
1713 vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1714 }
1715 "bloom" => {
1716 let db = session.db;
1717 bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1718 }
1719 "cuckoo" => {
1720 let db = session.db;
1721 cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1722 }
1723 "cms" => {
1724 let db = session.db;
1725 cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1726 }
1727 "topk" => {
1728 let db = session.db;
1729 topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1730 }
1731 "tdigest" => {
1732 let db = session.db;
1733 tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1734 }
1735 "ts" => {
1736 let db = session.db;
1737 ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1738 }
1739 "stream" => {
1744 let db = session.db;
1745 let now = server.now_ms();
1746 streams::execute(&server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1747 }
1748 "keyspace" if spec.name == "migrate" => {
1752 migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1753 }
1754 "keyspace" => {
1760 let mut touched = indexing::Touched::new(server);
1761 let done =
1762 keyspace::execute(&server.dbs, session.db, spec, args, out, &mut touched);
1763 done.map(|()| {
1764 indexing::touched(server, &touched);
1765 Flow::Continue
1766 })
1767 }
1768 "search" if spec.name == "FT.SEARCH" => {
1775 search::find(server, session.db, args, out).map(|()| Flow::Continue)
1780 }
1781 "search" if spec.name == "FT.AGGREGATE" => {
1782 search::roll(server, session.db, args, out).map(|()| Flow::Continue)
1783 }
1784 "search" => {
1785 let db = session.db;
1786 let made = search::execute(&mut server.search.lock(), db, spec, args, out);
1787 made.map(|made| {
1788 if let Some(name) = made {
1789 indexing::scan(server, db, name);
1790 }
1791 Flow::Continue
1792 })
1793 }
1794 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1795 _ => server::execute(server, session, spec, args, out),
1796 }
1797 };
1798 let flow = match done {
1799 Ok(flow) => flow,
1800 Err(e) => {
1801 out.truncate(mark);
1802 write_error(out, &e);
1803 Flow::Continue
1804 }
1805 };
1806
1807 let row = server.mine().cmdstats.at(spec);
1818 row.calls.bump();
1819 if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1820 row.failed.bump();
1821 }
1822 flow
1823}
1824
1825fn write_error(out: &mut Out, e: &Error) {
1835 let prefix: &[u8] = match e.code() {
1836 Code::WrongType => b"WRONGTYPE ",
1837 Code::Corrupt => b"INVALIDOBJ ",
1841 _ => b"ERR ",
1842 };
1843 out.error_line(prefix, e.message().as_bytes());
1844}
1845
1846#[cfg(test)]
1847mod tests {
1848 use super::*;
1849 use crate::proto::{Limits, Proto};
1850 use crate::request::Argv;
1851
1852 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1857 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1858 for p in parts {
1859 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1860 wire.extend_from_slice(p);
1861 wire.extend_from_slice(b"\r\n");
1862 }
1863 wire
1864 }
1865
1866 struct Fixture {
1868 server: Server,
1869 session: Session,
1870 argv: Argv,
1871 out: Out,
1872 }
1873
1874 impl Fixture {
1875 fn new() -> Fixture {
1876 Fixture::on(Server::new())
1877 }
1878
1879 fn striped(width: usize) -> Fixture {
1881 Fixture::on(Server::with_width(width))
1882 }
1883
1884 fn on(server: Server) -> Fixture {
1885 Fixture {
1886 server,
1887 session: Session::new(7),
1888 argv: Argv::new(),
1889 out: Out::new(Proto::Resp2),
1890 }
1891 }
1892
1893 fn run(&mut self, parts: &[&[u8]]) -> String {
1895 self.flow(parts).1
1896 }
1897
1898 fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1904 let wire = encode(parts);
1905 self.argv.decode(&wire, &Limits::default()).unwrap();
1906 self.out.clear();
1907 execute(
1908 &self.server,
1909 &mut self.session,
1910 Args::new(&self.argv, &wire),
1911 &mut self.out,
1912 );
1913 self.out.as_slice().to_vec()
1914 }
1915
1916 fn advance(&mut self, ms: u64) {
1918 self.server.advance_clock_ms(ms);
1919 }
1920
1921 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1923 let wire = encode(parts);
1924 self.argv.decode(&wire, &Limits::default()).unwrap();
1925 self.out.clear();
1926 let flow = execute(
1927 &self.server,
1928 &mut self.session,
1929 Args::new(&self.argv, &wire),
1930 &mut self.out,
1931 );
1932 (
1933 flow,
1934 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1935 )
1936 }
1937 }
1938
1939 #[test]
1943 fn rewriting_the_same_keys_does_not_grow_the_server() {
1944 let mut f = Fixture::new();
1945 let val = vec![b'v'; 1024];
1946 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1947
1948 for k in &keys {
1949 f.run(&[b"SET", k, &val]);
1950 }
1951 f.server.compact_step();
1952 let after_first = f.server.memory_bytes();
1953
1954 for _ in 0..500 {
1959 for k in &keys {
1960 f.run(&[b"SET", k, &val]);
1961 }
1962 f.server.compact_step();
1963 }
1964
1965 assert!(
1966 f.server.memory_bytes() <= after_first * 2,
1967 "held {} after five hundred passes against {after_first} after one",
1968 f.server.memory_bytes()
1969 );
1970 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1971 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1972 }
1973
1974 #[test]
1988 fn a_database_nobody_started_on_is_still_collected() {
1989 let mut f = Fixture::new();
1990 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1991 let val = vec![b'v'; 1024];
1992 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1993
1994 for k in &keys {
1995 f.run(&[b"SET", k, &val]);
1996 }
1997 while f.server.compact_step().is_some() {}
1998 assert_eq!(
1999 f.server.dirty & (1 << 9),
2000 0,
2001 "database nine was drained and should not be asked again until it is written to"
2002 );
2003 let after_first = f.server.memory_bytes();
2004
2005 for _ in 0..500 {
2006 for k in &keys {
2007 f.run(&[b"SET", k, &val]);
2008 }
2009 f.server.compact_step();
2010 }
2011
2012 assert!(
2013 f.server.memory_bytes() <= after_first * 2,
2014 "held {} after five hundred passes against {after_first} after one",
2015 f.server.memory_bytes()
2016 );
2017 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
2018 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
2019 f.run(&[b"SELECT", b"0"]);
2021 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2022 }
2023
2024 #[test]
2025 fn a_command_goes_from_bytes_to_bytes() {
2026 let mut f = Fixture::new();
2027 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2028 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
2029 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2030 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
2031 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
2033 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
2034 }
2035
2036 #[test]
2037 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
2038 let mut f = Fixture::new();
2039 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
2040 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
2043 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
2044 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
2045 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
2047 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2048 }
2049
2050 #[test]
2051 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
2052 let mut f = Fixture::new();
2053 f.run(&[b"SET", b"k", b"v"]);
2054 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
2057 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
2058 }
2059
2060 #[test]
2061 fn touch_counts_the_way_exists_counts() {
2062 let mut f = Fixture::new();
2063 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2064 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
2065 assert_eq!(
2066 f.run(&[b"TOUCH", b"a", b"a"]),
2067 ":2\r\n",
2068 "twice counts twice"
2069 );
2070 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
2071 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
2072 }
2073
2074 #[test]
2075 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
2076 let mut f = Fixture::new();
2077 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2078 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
2079
2080 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
2081 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2082 assert_eq!(
2083 f.run(&[b"TTL", b"b"]),
2084 ":100\r\n",
2085 "the source's and not b's"
2086 );
2087 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
2088 }
2089
2090 #[test]
2091 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
2092 let mut f = Fixture::new();
2093 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
2094 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
2097 }
2098
2099 #[test]
2100 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
2101 let mut f = Fixture::new();
2102 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
2103
2104 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
2105 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2106 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
2109 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
2110 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
2111 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
2112 }
2113
2114 #[test]
2115 fn renaming_a_set_does_not_touch_a_member() {
2116 let mut f = Fixture::new();
2117 for i in 0..300 {
2118 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
2119 }
2120 let before = f.server.memory_bytes();
2121
2122 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
2123 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
2124 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
2125 assert!(
2126 f.server.memory_bytes().abs_diff(before) < 256,
2127 "the members were copied: {} against {before}",
2128 f.server.memory_bytes()
2129 );
2130 }
2131
2132 #[test]
2133 fn a_copy_is_a_second_value_and_not_a_second_name() {
2134 let mut f = Fixture::new();
2135 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
2136
2137 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
2138 f.run(&[b"SADD", b"t", b"m3"]);
2139 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
2140 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
2141 }
2142
2143 #[test]
2153 fn every_type_can_be_copied() {
2154 let mut f = Fixture::new();
2155 f.run(&[b"SET", b"str", b"v1"]);
2156 f.run(&[b"SADD", b"set", b"m1"]);
2157 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2158 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
2159 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
2160
2161 for name in [
2162 &b"str"[..],
2163 &b"set"[..],
2164 &b"hash"[..],
2165 &b"list"[..],
2166 &b"zset"[..],
2167 ] {
2168 let dst = [name, b":copy"].concat();
2169 assert_eq!(
2170 f.run(&[b"COPY", name, &dst]),
2171 ":1\r\n",
2172 "copying {}",
2173 String::from_utf8_lossy(name)
2174 );
2175 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
2176 }
2177
2178 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
2179 let mut want = String::from("*2\r\n");
2180 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
2181 want
2182 });
2183 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
2184
2185 f.run(&[b"RPUSH", b"list:copy", b"c"]);
2187 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
2188 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
2189 }
2190
2191 #[test]
2192 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
2193 let mut f = Fixture::new();
2194 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2195 f.run(&[b"SET", b"b", b"v2"]);
2196
2197 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
2198 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2199 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
2200 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2201 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
2202 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
2203 }
2204
2205 #[test]
2206 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
2207 let mut f = Fixture::new();
2208 f.run(&[b"SET", b"a", b"v1"]);
2209
2210 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
2213 f.run(&[b"SELECT", b"1"]);
2214 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
2215 assert_eq!(
2216 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
2217 ":0\r\n",
2218 "taken"
2219 );
2220 assert_eq!(
2221 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
2222 ":1\r\n"
2223 );
2224 }
2225
2226 #[test]
2227 fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
2228 let mut f = Fixture::new();
2229 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2230 assert_eq!(
2231 f.run(&[b"SORT", b"l"]),
2232 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2233 );
2234 assert_eq!(
2236 f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
2237 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2238 );
2239 assert_eq!(
2240 f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
2241 "*1\r\n$1\r\n2\r\n"
2242 );
2243 }
2244
2245 #[test]
2246 fn sort_reads_a_key_per_element_for_by_and_for_get() {
2247 let mut f = Fixture::new();
2248 f.run(&[b"RPUSH", b"l", b"a", b"b"]);
2249 f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
2250 assert_eq!(
2253 f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
2254 "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
2255 );
2256 }
2257
2258 #[test]
2259 fn sort_store_writes_a_list_and_answers_its_length() {
2260 let mut f = Fixture::new();
2261 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2262 assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
2263 assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
2264 assert_eq!(
2265 f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
2266 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2267 );
2268 assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
2271 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
2272 }
2273
2274 #[test]
2275 fn sort_ro_does_not_know_the_word_store() {
2276 let mut f = Fixture::new();
2277 f.run(&[b"RPUSH", b"l", b"2", b"1"]);
2278 assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
2279 assert_eq!(
2280 f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
2281 "-ERR syntax error\r\n"
2282 );
2283 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2284 }
2285
2286 #[test]
2287 fn sort_refuses_what_it_cannot_sort() {
2288 let mut f = Fixture::new();
2289 assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
2290 f.run(&[b"SET", b"s", b"x"]);
2291 assert_eq!(
2292 f.run(&[b"SORT", b"s"]),
2293 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
2294 );
2295 f.run(&[b"RPUSH", b"words", b"one", b"two"]);
2296 assert_eq!(
2297 f.run(&[b"SORT", b"words"]),
2298 "-ERR One or more scores can't be converted into double\r\n"
2299 );
2300 assert_eq!(
2301 f.run(&[b"SORT", b"words", b"ALPHA"]),
2302 "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
2303 );
2304 assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
2305 }
2306
2307 #[test]
2308 fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
2309 let mut f = Fixture::new();
2310 assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
2311 assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
2312 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2313 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2314 assert_eq!(
2315 f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
2316 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2317 );
2318 assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
2321 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2322 }
2323
2324 #[test]
2325 fn move_answers_zero_when_either_end_says_no() {
2326 let mut f = Fixture::new();
2327 assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
2328 assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
2329 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2330 assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
2331 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2332 assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
2335 assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
2336 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2337 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
2338 }
2339
2340 #[test]
2341 fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
2342 let mut f = Fixture::new();
2343 assert_eq!(
2344 f.run(&[b"MOVE", b"a", b"0"]),
2345 "-ERR source and destination objects are the same\r\n"
2346 );
2347 assert_eq!(
2348 f.run(&[b"MOVE", b"a", b"99"]),
2349 "-ERR DB index is out of range\r\n"
2350 );
2351 assert_eq!(
2352 f.run(&[b"MOVE", b"a", b"-1"]),
2353 "-ERR DB index is out of range\r\n"
2354 );
2355 assert_eq!(
2356 f.run(&[b"MOVE", b"a", b"x"]),
2357 "-ERR value is not an integer or out of range\r\n"
2358 );
2359 }
2360
2361 #[test]
2362 fn swapdb_swaps_what_two_connections_would_see() {
2363 let mut f = Fixture::new();
2364 assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
2365 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2366 assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
2367 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2368
2369 assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
2370 assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
2372 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2373 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2374 assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
2376 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2377 }
2378
2379 #[test]
2384 fn swapdb_swaps_every_stripe_of_a_wide_database() {
2385 let mut f = Fixture::striped(4);
2386 for i in 0..16u32 {
2387 let key = format!("k{i}");
2388 assert_eq!(f.run(&[b"SET", key.as_bytes(), b"zero"]), "+OK\r\n");
2389 }
2390 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2391 assert_eq!(f.run(&[b"SET", b"only", b"one"]), "+OK\r\n");
2392 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2393
2394 assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
2395 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2396 assert_eq!(f.run(&[b"GET", b"only"]), "$3\r\none\r\n");
2397 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2398 assert_eq!(f.run(&[b"DBSIZE"]), ":16\r\n");
2399 for i in 0..16u32 {
2400 let key = format!("k{i}");
2401 assert_eq!(f.run(&[b"GET", key.as_bytes()]), "$4\r\nzero\r\n");
2402 }
2403 }
2404
2405 #[test]
2406 fn swapdb_says_which_index_it_could_not_read() {
2407 let mut f = Fixture::new();
2408 assert_eq!(
2409 f.run(&[b"SWAPDB", b"x", b"1"]),
2410 "-ERR invalid first DB index\r\n"
2411 );
2412 assert_eq!(
2413 f.run(&[b"SWAPDB", b"0", b"y"]),
2414 "-ERR invalid second DB index\r\n"
2415 );
2416 assert_eq!(
2420 f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2421 "-ERR invalid first DB index\r\n"
2422 );
2423 assert_eq!(
2424 f.run(&[b"SWAPDB", b"0", b"99"]),
2425 "-ERR DB index is out of range\r\n"
2426 );
2427 assert_eq!(
2428 f.run(&[b"SWAPDB", b"-1", b"0"]),
2429 "-ERR DB index is out of range\r\n"
2430 );
2431 }
2432
2433 #[test]
2434 fn wait_answers_zero_replicas_without_waiting() {
2435 let mut f = Fixture::new();
2436 assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2437 assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2438 assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2441 assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2444 assert_eq!(
2445 f.run(&[b"WAIT", b"x", b"0"]),
2446 "-ERR value is not an integer or out of range\r\n"
2447 );
2448 assert_eq!(
2449 f.run(&[b"WAIT", b"0", b"-1"]),
2450 "-ERR timeout is negative\r\n"
2451 );
2452 assert_eq!(
2453 f.run(&[b"WAIT", b"0", b"1.5"]),
2454 "-ERR timeout is not an integer or out of range\r\n"
2455 );
2456 }
2457
2458 #[test]
2459 fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2460 let mut f = Fixture::new();
2461 assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2462 assert_eq!(
2463 f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2464 "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2465 );
2466 assert_eq!(
2467 f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2468 "-ERR value is out of range, value must between 0 and 1\r\n"
2469 );
2470 assert_eq!(
2471 f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2472 "-ERR value is out of range, must be positive\r\n"
2473 );
2474 assert_eq!(
2477 f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2478 "-ERR timeout is negative\r\n"
2479 );
2480 }
2481
2482 fn payload(reply: &[u8]) -> Vec<u8> {
2486 let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2487 reply[head + 2..reply.len() - 2].to_vec()
2488 }
2489
2490 #[test]
2491 fn a_value_survives_a_dump_and_a_restore() {
2492 let mut f = Fixture::new();
2493 f.run(&[b"SET", b"s", b"hello"]);
2494 f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2495 f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2496 f.run(&[b"SADD", b"u", b"x", b"y"]);
2497 f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2498 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2499
2500 for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2501 let mut copy = key.to_vec();
2502 copy.push(b'2');
2503 let bytes = payload(&f.raw(&[b"DUMP", key]));
2504 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
2505 assert_eq!(f.run(&[b"TYPE", ©]), f.run(&[b"TYPE", key]));
2506 }
2507
2508 assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2509 assert_eq!(
2510 f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2511 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2512 );
2513 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2514 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2515 assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2516 assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2517 assert_eq!(
2520 f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2521 f.run(&[b"OBJECT", b"ENCODING", b"t"])
2522 );
2523 }
2524
2525 #[test]
2526 fn a_dumped_hash_keeps_its_field_deadlines() {
2527 let mut f = Fixture::new();
2528 f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2529 assert_eq!(
2530 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2531 "*1\r\n:1\r\n"
2532 );
2533 let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2534 assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2535 assert_eq!(
2536 f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2537 "*2\r\n:-1\r\n:100\r\n"
2538 );
2539 }
2540
2541 #[test]
2542 fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2543 let mut f = Fixture::new();
2544 f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2545 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2546 assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2547 assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2548 assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2549 assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2550 assert_eq!(
2553 f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2554 "+OK\r\n"
2555 );
2556 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2557 }
2558
2559 #[test]
2560 fn dump_answers_nothing_for_a_key_that_is_not_there() {
2561 let mut f = Fixture::new();
2562 assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2563 f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2564 f.advance(50);
2565 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2566 }
2567
2568 #[test]
2569 fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2570 let mut f = Fixture::new();
2571 f.run(&[b"SET", b"a", b"first"]);
2572 f.run(&[b"SET", b"b", b"second"]);
2573 let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2574 assert_eq!(
2575 f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2576 "-BUSYKEY Target key name already exists.\r\n"
2577 );
2578 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2579 assert_eq!(
2580 f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2581 "+OK\r\n"
2582 );
2583 assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2584 }
2585
2586 #[test]
2590 fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2591 let mut f = Fixture::new();
2592 f.run(&[b"SET", b"a", b"v"]);
2593 assert_eq!(
2594 f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2595 "-BUSYKEY Target key name already exists.\r\n"
2596 );
2597 assert_eq!(
2600 f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2601 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2602 );
2603 }
2604
2605 #[test]
2606 fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2607 let mut f = Fixture::new();
2608 f.run(&[b"SET", b"a", b"hello"]);
2609 let good = payload(&f.raw(&[b"DUMP", b"a"]));
2610
2611 let mut flipped = good.clone();
2612 flipped[2] ^= 0x40;
2613 assert_eq!(
2614 f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2615 "-ERR DUMP payload version or checksum are wrong\r\n"
2616 );
2617 assert_eq!(
2618 f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2619 "-ERR DUMP payload version or checksum are wrong\r\n"
2620 );
2621 let mut truncated = good[..1].to_vec();
2625 truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2626 let crc = yo_common::crc::crc64(0, &truncated);
2627 truncated.extend_from_slice(&crc.to_le_bytes());
2628 assert_eq!(
2629 f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2630 "-ERR Bad data format\r\n"
2631 );
2632 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2633 }
2634
2635 #[test]
2636 fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2637 let mut f = Fixture::new();
2638 f.run(&[b"SET", b"a", b"v"]);
2639 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2640 assert_eq!(
2641 f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2642 "-ERR Invalid TTL value, must be >= 0\r\n"
2643 );
2644 assert_eq!(
2645 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2646 "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2647 );
2648 assert_eq!(
2649 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2650 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2651 );
2652 assert_eq!(
2654 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2655 "+OK\r\n"
2656 );
2657 assert_eq!(
2658 f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2659 "+OK\r\n"
2660 );
2661 }
2662
2663 #[test]
2667 fn restore_takes_idletime_or_freq_and_not_both() {
2668 let mut f = Fixture::new();
2669 f.run(&[b"SET", b"a", b"v"]);
2670 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2671 assert_eq!(
2672 f.run(&[
2673 b"RESTORE",
2674 b"b",
2675 b"0",
2676 &bytes,
2677 b"IDLETIME",
2678 b"1",
2679 b"FREQ",
2680 b"2"
2681 ]),
2682 "-ERR syntax error\r\n"
2683 );
2684 assert_eq!(
2685 f.run(&[
2686 b"RESTORE",
2687 b"b",
2688 b"0",
2689 &bytes,
2690 b"FREQ",
2691 b"2",
2692 b"IDLETIME",
2693 b"1"
2694 ]),
2695 "-ERR syntax error\r\n"
2696 );
2697 assert_eq!(
2698 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2699 "-ERR syntax error\r\n"
2700 );
2701 assert_eq!(
2702 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2703 "-ERR syntax error\r\n"
2704 );
2705 }
2706
2707 #[test]
2708 fn copy_checks_its_options_before_it_looks_for_anything() {
2709 let mut f = Fixture::new();
2710 assert_eq!(
2713 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2714 "-ERR DB index is out of range\r\n"
2715 );
2716 assert_eq!(
2717 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2718 "-ERR DB index is out of range\r\n"
2719 );
2720 assert_eq!(
2721 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2722 "-ERR value is not an integer or out of range\r\n"
2723 );
2724 assert_eq!(
2725 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2726 "-ERR syntax error\r\n"
2727 );
2728 assert_eq!(
2729 f.run(&[b"COPY", b"a", b"a"]),
2730 "-ERR source and destination objects are the same\r\n"
2731 );
2732 assert_eq!(
2734 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2735 ":0\r\n"
2736 );
2737 }
2738
2739 #[test]
2740 fn time_is_two_bulk_strings_and_moves() {
2741 let mut f = Fixture::new();
2742 let first = f.run(&[b"TIME"]);
2743 assert!(first.starts_with("*2\r\n$"), "got {first}");
2744 let parts: Vec<&str> = first.split("\r\n").collect();
2745 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2746 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2747 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2748 assert!((0..1_000_000).contains(µs), "got {micros}");
2749 assert_ne!(first, f.run(&[b"TIME"]));
2753 }
2754
2755 #[test]
2756 fn a_keyspace_scan_walks_every_key_once() {
2757 let mut f = Fixture::new();
2758 for i in 0..500 {
2759 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2760 }
2761
2762 let mut seen: Vec<String> = Vec::new();
2763 let mut cursor = "0".to_owned();
2764 let mut calls = 0;
2765 loop {
2766 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2767 seen.extend(keys);
2768 cursor = next;
2769 calls += 1;
2770 assert!(calls < 10_000, "the cursor is not advancing");
2771 if cursor == "0" {
2772 break;
2773 }
2774 }
2775
2776 seen.sort();
2777 seen.dedup();
2778 assert_eq!(seen.len(), 500, "every key once and only once");
2779 assert!(calls > 1, "500 keys came back in one batch");
2782 }
2783
2784 #[test]
2785 fn a_scan_narrows_by_pattern_and_by_type() {
2786 let mut f = Fixture::new();
2787 f.run(&[b"SET", b"str", b"v"]);
2788 f.run(&[b"SADD", b"members", b"a"]);
2789 f.run(&[b"HSET", b"fields", b"f", b"v"]);
2790
2791 let all = |f: &mut Fixture, args: &[&[u8]]| {
2792 let mut out: Vec<String> = Vec::new();
2793 let mut cursor = "0".to_owned();
2794 loop {
2795 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2796 line.extend_from_slice(args);
2797 let (next, keys) = scan_reply(&f.run(&line));
2798 out.extend(keys);
2799 cursor = next;
2800 if cursor == "0" {
2801 break;
2802 }
2803 }
2804 out.sort();
2805 out
2806 };
2807
2808 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2809 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2810 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2811 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2813 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2815 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2816 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2818 }
2819
2820 #[test]
2821 fn a_scan_says_what_is_wrong_with_it() {
2822 let mut f = Fixture::new();
2823 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2824 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2825 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2826 assert_eq!(
2827 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2828 "-ERR syntax error\r\n"
2829 );
2830 assert_eq!(
2831 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2832 "-ERR value is not an integer or out of range\r\n"
2833 );
2834 assert_eq!(
2835 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2836 "-ERR syntax error\r\n"
2837 );
2838 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2843 }
2844
2845 #[test]
2846 fn keys_and_randomkey_look_at_the_whole_database() {
2847 let mut f = Fixture::new();
2848 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2849 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2850
2851 for name in ["one", "two", "three"] {
2852 f.run(&[b"SET", name.as_bytes(), b"v"]);
2853 }
2854 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2855 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2856 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2857
2858 for _ in 0..50 {
2859 let got = f.run(&[b"RANDOMKEY"]);
2860 assert!(
2861 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2862 "got {got}"
2863 );
2864 }
2865 }
2866
2867 #[test]
2868 fn a_walk_does_not_answer_keys_that_have_expired() {
2869 let mut f = Fixture::new();
2870 f.run(&[b"SET", b"alive", b"v"]);
2871 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2872 f.server.advance_clock_ms(2);
2873 assert_eq!(
2874 f.run(&[b"DBSIZE"]),
2875 ":2\r\n",
2876 "nothing has collected it yet"
2877 );
2878
2879 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2880 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2881 assert_eq!(keys, ["alive"]);
2882 for _ in 0..20 {
2883 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2884 }
2885 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2888 }
2889
2890 #[test]
2891 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2892 let mut f = Fixture::new();
2893 f.run(&[b"SET", b"k", b"v"]);
2894 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2895 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2896
2897 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2898 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2899 let ms = int(&f.run(&[b"PTTL", b"k"]));
2900 assert!((99_000..=100_000).contains(&ms), "got {ms}");
2901
2902 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2904 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2905 assert_eq!(at, (at_ms + 500) / 1000);
2906 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2907
2908 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2909 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2910 assert_eq!(
2911 f.run(&[b"PERSIST", b"k"]),
2912 ":0\r\n",
2913 "nothing to take off the second time"
2914 );
2915 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2916 assert_eq!(
2917 f.run(&[b"GET", b"k"]),
2918 "$1\r\nv\r\n",
2919 "and the value went through all of that untouched"
2920 );
2921 }
2922
2923 #[test]
2924 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2925 let mut f = Fixture::new();
2926 f.run(&[b"SET", b"str", b"v"]);
2927 f.run(&[b"SADD", b"set", b"a", b"b"]);
2928 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2929
2930 for key in [b"str".as_slice(), b"set", b"hash"] {
2931 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2932 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2933 }
2934 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2937 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2938 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2939 }
2940
2941 #[test]
2942 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2943 let mut f = Fixture::new();
2944 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2945 f.run(&[b"SET", key, b"v"]);
2946 }
2947 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2951 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2952 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2953 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2954 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2955 assert_eq!(
2956 f.run(&[b"EXPIRE", b"a", b"100"]),
2957 ":0\r\n",
2958 "and the key really went, so there is nothing to put a deadline on"
2959 );
2960 }
2961
2962 #[test]
2963 fn the_four_conditions_decide_whether_the_deadline_moves() {
2964 let mut f = Fixture::new();
2965 f.run(&[b"SET", b"k", b"v"]);
2966
2967 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2968 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2969 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2970 assert_eq!(
2971 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2972 ":1\r\n",
2973 "no deadline reads as infinitely far away, so LT passes where GT fails"
2974 );
2975
2976 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2977 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2978 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2979 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2980 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2981 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2982
2983 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2986 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2987 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2988 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2989 }
2990
2991 #[test]
2992 fn the_conditions_are_a_set_and_not_a_keyword() {
2993 let mut f = Fixture::new();
2994 f.run(&[b"SET", b"k", b"v"]);
2995
2996 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2997 assert_eq!(
2998 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2999 ":0\r\n",
3000 "the same keyword twice means it once, and NX now has a deadline to fail on"
3001 );
3002
3003 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
3006 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
3007 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
3008 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
3009 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
3010 f.run(&[b"PERSIST", b"k"]);
3011 assert_eq!(
3012 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
3013 ":0\r\n",
3014 "where LT on its own would have taken it"
3015 );
3016 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
3017 }
3018
3019 #[test]
3020 fn a_key_is_gone_once_its_moment_passes() {
3021 let mut f = Fixture::new();
3022 f.run(&[b"SET", b"k", b"v"]);
3023 f.run(&[b"EXPIRE", b"k", b"100"]);
3024
3025 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
3026 f.server.set_clock_ms(at as u64 + 1);
3027 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3028 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
3029 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
3030 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3031 }
3032
3033 #[test]
3034 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
3035 let mut f = Fixture::new();
3036 f.run(&[b"SET", b"k", b"v"]);
3037 for (bad, want) in [
3038 (
3039 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
3040 "-ERR value is not an integer or out of range\r\n",
3041 ),
3042 (
3043 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
3044 "-ERR Unsupported option MAYBE\r\n",
3045 ),
3046 (
3047 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
3048 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
3049 ),
3050 (
3051 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
3052 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
3053 ),
3054 (
3055 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
3056 "-ERR GT and LT options at the same time are not compatible\r\n",
3057 ),
3058 (
3061 &[b"EXPIRE", b"k", b"9223372036854775807"],
3062 "-ERR invalid expire time in 'expire' command\r\n",
3063 ),
3064 (
3065 &[b"EXPIREAT", b"k", b"9223372036854775807"],
3066 "-ERR invalid expire time in 'expireat' command\r\n",
3067 ),
3068 (
3069 &[b"PEXPIRE", b"k", b"9223372036854775807"],
3070 "-ERR invalid expire time in 'pexpire' command\r\n",
3071 ),
3072 ] {
3073 assert_eq!(f.run(bad), want, "for {bad:?}");
3074 }
3075 assert_eq!(
3076 f.run(&[b"TTL", b"k"]),
3077 ":-1\r\n",
3078 "and none of those put a deadline on anything"
3079 );
3080
3081 assert_eq!(
3085 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
3086 ":1\r\n"
3087 );
3088 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
3089 }
3090
3091 #[test]
3092 fn flushing_empties_this_database_or_every_one_of_them() {
3093 let mut f = Fixture::new();
3094 f.run(&[b"SELECT", b"0"]);
3095 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3096 f.run(&[b"SELECT", b"1"]);
3097 f.run(&[b"SET", b"c", b"3"]);
3098 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3099 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
3102 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3103 f.run(&[b"SELECT", b"0"]);
3105 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
3106 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
3107 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3108 f.run(&[b"SELECT", b"1"]);
3109 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3110 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
3113 assert_eq!(
3114 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
3115 "-ERR syntax error\r\n"
3116 );
3117 }
3118
3119 #[test]
3120 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
3121 let mut f = Fixture::new();
3122 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
3123 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
3124 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
3125 assert_eq!(
3128 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
3129 "*2\r\n:0\r\n:0\r\n"
3130 );
3131 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
3132 assert_eq!(
3133 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
3134 "*0\r\n"
3135 );
3136 assert_eq!(
3137 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
3138 "-ERR Library not found\r\n"
3139 );
3140
3141 assert_eq!(
3144 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
3145 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
3146 );
3147 assert_eq!(
3148 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
3149 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
3150 );
3151 assert_eq!(
3154 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
3155 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
3156 );
3157 assert_eq!(
3158 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
3159 "-ERR Unknown argument bogus\r\n"
3160 );
3161 assert_eq!(
3162 f.run(&[b"SCRIPT", b"EXISTS"]),
3163 "-ERR wrong number of arguments for 'script|exists' command\r\n"
3164 );
3165
3166 assert_eq!(
3169 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
3170 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
3171 );
3172 assert_eq!(
3173 f.run(&[b"FUNCTION", b"STATS"]),
3174 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
3175 );
3176 }
3177
3178 #[test]
3179 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
3180 let mut f = Fixture::new();
3181 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
3182 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
3183 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
3184 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
3187 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
3188 f.run(&[b"SET", b"k", b"hello"]);
3191 assert_eq!(
3192 f.run(&[b"INCR", b"k"]),
3193 "-ERR value is not an integer or out of range\r\n"
3194 );
3195 assert_eq!(
3196 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
3197 "-ERR increment would produce NaN or Infinity\r\n"
3198 );
3199 }
3200
3201 #[test]
3206 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
3207 let mut f = Fixture::new();
3208 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
3209 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
3212 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
3213 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
3214 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
3215 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
3216 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
3217 assert_eq!(
3218 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
3219 "*2\r\n:1\r\n:0\r\n",
3220 "a refused increment reports the value it left alone and applied nothing"
3221 );
3222 assert_eq!(
3223 f.run(&[
3224 b"INCREX",
3225 b"n",
3226 b"BYINT",
3227 b"5",
3228 b"UBOUND",
3229 b"3",
3230 b"SATURATE"
3231 ]),
3232 "*2\r\n:3\r\n:2\r\n"
3233 );
3234 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
3235 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
3236 }
3237
3238 #[test]
3239 fn the_same_answers_come_out_in_resp3_spelling() {
3240 let mut f = Fixture::new();
3241 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
3242 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
3243 assert_eq!(
3246 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
3247 "*2\r\n,1.5\r\n,1.5\r\n"
3248 );
3249 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
3250 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
3253 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
3254 }
3255
3256 #[test]
3257 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
3258 let mut f = Fixture::new();
3259 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
3260 assert_eq!(flow, Flow::Continue);
3261 assert_eq!(
3262 reply,
3263 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
3264 );
3265 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
3268 assert_eq!(reply.matches("\r\n").count(), 1);
3269 }
3270
3271 #[test]
3272 fn arity_is_checked_before_the_command_is() {
3273 let mut f = Fixture::new();
3274 assert_eq!(
3275 f.run(&[b"GET"]),
3276 "-ERR wrong number of arguments for 'get' command\r\n"
3277 );
3278 assert_eq!(
3279 f.run(&[b"MSET", b"k"]),
3280 "-ERR wrong number of arguments for 'mset' command\r\n"
3281 );
3282 assert_eq!(
3286 f.run(&[b"PING", b"a", b"b"]),
3287 "-ERR wrong number of arguments for 'ping' command\r\n"
3288 );
3289 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
3290 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
3291 assert_eq!(
3293 f.run(&[b"DELEX", b"k", b"IFEQ"]),
3294 "-ERR wrong number of arguments for 'delex' command\r\n"
3295 );
3296 }
3297
3298 #[test]
3302 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
3303 let mut f = Fixture::new();
3304 let syntax = "-ERR syntax error\r\n";
3305 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
3306 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
3307 assert_eq!(
3308 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
3309 syntax
3310 );
3311 assert_eq!(
3312 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
3313 syntax
3314 );
3315 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
3316 assert_eq!(
3318 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
3319 "+OK\r\n"
3320 );
3321 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
3322 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
3323 assert_eq!(
3325 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
3326 syntax
3327 );
3328 assert_eq!(
3329 f.run(&[b"INCREX", b"n", b"ENX"]),
3330 "-ERR ENX flag requires an expiration\r\n"
3331 );
3332 assert_eq!(
3333 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
3334 "-ERR UBOUND is not an integer or out of range\r\n"
3335 );
3336 assert_eq!(
3337 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
3338 "-ERR LBOUND can't be greater than UBOUND\r\n"
3339 );
3340 assert_eq!(
3341 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
3342 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
3343 );
3344 }
3345
3346 #[test]
3350 fn the_expiry_rules_are_redis_own() {
3351 let mut f = Fixture::new();
3352 let bad = "-ERR invalid expire time in 'set' command\r\n";
3353 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
3354 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
3355 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
3356 assert_eq!(
3357 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
3358 bad
3359 );
3360 assert_eq!(
3361 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
3362 "-ERR value is not an integer or out of range\r\n"
3363 );
3364 assert_eq!(
3365 f.run(&[b"SETEX", b"k", b"0", b"v"]),
3366 "-ERR invalid expire time in 'setex' command\r\n"
3367 );
3368 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
3369 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
3370 assert_eq!(
3371 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
3372 "-ERR syntax error\r\n",
3373 "the option list is still checked before the key is looked up"
3374 );
3375 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3377 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
3378 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3379 }
3380
3381 #[test]
3382 fn mset_takes_its_pairs_from_the_read_buffer() {
3383 let mut f = Fixture::new();
3384 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
3385 assert_eq!(
3386 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
3387 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
3388 );
3389 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
3390 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
3391 assert_eq!(
3392 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
3393 "-ERR wrong number of key-value pairs\r\n"
3394 );
3395 assert_eq!(
3396 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
3397 "-ERR invalid numkeys value\r\n"
3398 );
3399 assert_eq!(
3400 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
3401 "-ERR invalid numkeys value\r\n"
3402 );
3403 }
3404
3405 #[test]
3406 fn lcs_answers_the_length_the_string_and_the_runs() {
3407 let mut f = Fixture::new();
3408 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
3409 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
3410 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
3411 assert_eq!(
3412 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
3413 "*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"
3414 );
3415 assert_eq!(
3418 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3419 "$6\r\nmytext\r\n"
3420 );
3421 }
3422
3423 #[test]
3424 fn select_moves_the_connection_and_the_databases_stay_apart() {
3425 let mut f = Fixture::new();
3426 f.run(&[b"SET", b"k", b"zero"]);
3427 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3428 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3429 f.run(&[b"SET", b"k", b"four"]);
3430 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3431 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3432 assert_eq!(
3433 f.run(&[b"SELECT", b"99"]),
3434 "-ERR DB index is out of range\r\n"
3435 );
3436 assert_eq!(
3437 f.run(&[b"SELECT", b"-1"]),
3438 "-ERR DB index is out of range\r\n"
3439 );
3440 assert_eq!(
3441 f.run(&[b"SELECT", b"abc"]),
3442 "-ERR value is not an integer or out of range\r\n"
3443 );
3444 f.run(&[b"SELECT", b"4"]);
3446 f.run(&[b"RESET"]);
3447 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3448 }
3449
3450 #[test]
3451 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3452 let mut f = Fixture::new();
3453 let reply = f.run(&[b"HELLO"]);
3454 assert!(reply.starts_with("*14\r\n"), "{reply}");
3455 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3456 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3457 assert!(
3458 reply.contains(":7\r\n"),
3459 "the connection id is in there: {reply}"
3460 );
3461 assert_eq!(
3462 f.run(&[b"HELLO", b"4"]),
3463 "-NOPROTO unsupported protocol version\r\n"
3464 );
3465 assert_eq!(
3466 f.run(&[b"HELLO", b"abc"]),
3467 "-ERR Protocol version is not an integer or out of range\r\n"
3468 );
3469 assert_eq!(
3470 f.run(&[b"HELLO", b"3", b"SETNAME"]),
3471 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3472 );
3473 assert!(
3474 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3475 .starts_with("%7\r\n")
3476 );
3477 assert_eq!(f.session.name(), b"bob");
3478 f.run(&[b"RESET"]);
3479 assert_eq!(f.session.name(), b"");
3480 }
3481
3482 #[test]
3483 fn command_describes_this_server_in_the_shape_a_driver_reads() {
3484 let mut f = Fixture::new();
3485 let count = format!(":{}\r\n", COMMANDS.len());
3486 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3487 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3488 assert_eq!(
3489 info,
3490 "*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\
3491 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3492 );
3493 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3495 assert_eq!(
3496 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3497 "*1\r\n$8\r\ngetrange\r\n"
3498 );
3499 assert_eq!(
3500 f.run(&[b"COMMAND", b"NOPE"]),
3501 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3502 );
3503 }
3504
3505 #[test]
3509 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3510 let mut f = Fixture::new();
3511 assert_eq!(
3512 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3513 "*1\r\n$1\r\nk\r\n"
3514 );
3515 assert_eq!(
3516 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3517 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3518 );
3519 assert_eq!(
3520 f.run(&[
3521 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3522 ]),
3523 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3524 );
3525 assert_eq!(
3526 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3527 "-ERR The command has no key arguments\r\n"
3528 );
3529 assert_eq!(
3530 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3531 "-ERR Invalid number of arguments specified for command\r\n"
3532 );
3533 }
3534
3535 #[test]
3536 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3537 let mut f = Fixture::new();
3538 assert_eq!(
3539 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3540 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3541 );
3542 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3545 assert!(both.starts_with("*6\r\n"), "{both}");
3546 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3547 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3548 assert_eq!(
3549 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3550 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3551 );
3552 assert_eq!(
3553 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3554 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3555 );
3556 assert_eq!(
3557 f.run(&[b"CONFIG", b"GET"]),
3558 "-ERR wrong number of arguments for 'config|get' command\r\n"
3559 );
3560 assert_eq!(
3564 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3565 "-ERR wrong number of arguments for 'config|set' command\r\n"
3566 );
3567 assert_eq!(
3568 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3569 "-ERR syntax error\r\n"
3570 );
3571 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3572 assert_eq!(
3573 f.run(&[b"CONFIG", b"REWRITE"]),
3574 "-ERR The server is running without a config file\r\n"
3575 );
3576 }
3577
3578 #[test]
3579 fn the_eviction_policy_reads_back_what_was_written_to_it() {
3580 let mut f = Fixture::new();
3581 assert_eq!(
3582 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3583 "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3584 );
3585 assert_eq!(
3586 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3587 "+OK\r\n",
3588 "the name is matched without regard to case, like every other one"
3589 );
3590 assert_eq!(
3591 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3592 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3593 );
3594 assert!(
3596 f.run(&[b"INFO", b"memory"])
3597 .contains("maxmemory_policy:allkeys-lfu"),
3598 "INFO and CONFIG disagree about the policy"
3599 );
3600 assert_eq!(
3604 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3605 "-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"
3606 );
3607 assert_eq!(
3610 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3611 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3612 );
3613 f.run(&[
3614 b"CONFIG",
3615 b"SET",
3616 b"hash-max-listpack-entries",
3617 b"7",
3618 b"maxmemory-policy",
3619 b"nonsense",
3620 ]);
3621 assert_eq!(
3622 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3623 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3624 );
3625 }
3626
3627 #[test]
3628 fn the_three_eviction_numbers_read_back_too() {
3629 let mut f = Fixture::new();
3630 for (name, default, set) in [
3631 ("maxmemory-samples", "5", "12"),
3632 ("lfu-log-factor", "10", "3"),
3633 ("lfu-decay-time", "1", "60"),
3634 ] {
3635 let get = || {
3636 format!(
3637 "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3638 name.len(),
3639 default.len()
3640 )
3641 };
3642 assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3643 assert_eq!(
3644 f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3645 "+OK\r\n"
3646 );
3647 assert_eq!(
3648 f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3649 format!(
3650 "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3651 name.len(),
3652 set.len()
3653 )
3654 );
3655 assert_eq!(
3658 f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3659 format!(
3660 "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3661 )
3662 );
3663 }
3664 }
3665
3666 #[test]
3667 fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3668 let mut f = Fixture::new();
3669 assert_eq!(
3670 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3671 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3672 "no limit is the default"
3673 );
3674 for (typed, bytes) in [
3677 (&b"1024"[..], "1024"),
3678 (b"1k", "1000"),
3679 (b"1kb", "1024"),
3680 (b"1M", "1000000"),
3681 (b"1Mb", "1048576"),
3682 (b"1gb", "1073741824"),
3683 (b"100mb", "104857600"),
3684 ] {
3685 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3686 assert_eq!(
3687 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3688 format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3689 "set {}",
3690 String::from_utf8_lossy(typed)
3691 );
3692 }
3693 assert!(
3694 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3695 "the report agrees with the setting"
3696 );
3697
3698 for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3701 assert_eq!(
3702 f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3703 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3704 "refused {}",
3705 String::from_utf8_lossy(bad)
3706 );
3707 }
3708 assert!(
3709 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3710 "and the refusal left the old one alone"
3711 );
3712 }
3713
3714 #[test]
3715 fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3716 let mut f = Fixture::new();
3717 f.run(&[b"SET", b"here", b"already"]);
3718 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3722 assert_eq!(
3723 f.run(&[b"SET", b"k", b"v"]),
3724 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3725 );
3726 assert_eq!(
3727 f.run(&[b"LPUSH", b"l", b"v"]),
3728 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3729 );
3730 assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3732 assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3733 assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3734
3735 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3737 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3738 }
3739
3740 #[test]
3741 fn an_allkeys_policy_makes_room_instead_of_refusing() {
3742 let mut f = Fixture::new();
3743 let val = vec![b'v'; 256];
3744 for i in 0..24000u32 {
3745 let k = format!("key:{i:08}");
3746 f.run(&[b"SET", k.as_bytes(), &val]);
3747 }
3748 let full = f.server.memory_bytes();
3749 assert!(
3750 full > 3 * 1024 * 1024,
3751 "the arena is several segments: {full}"
3752 );
3753
3754 let limit = full - 2 * 1024 * 1024;
3758 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3759 f.run(&[
3760 b"CONFIG",
3761 b"SET",
3762 b"maxmemory",
3763 limit.to_string().as_bytes(),
3764 ]);
3765
3766 for i in 0..2000u32 {
3770 let k = format!("new:{i:08}");
3771 assert_eq!(
3772 f.run(&[b"SET", k.as_bytes(), &val]),
3773 "+OK\r\n",
3774 "write {i} was refused"
3775 );
3776 f.server.refresh_memory();
3777 if f.server.memory_bytes() <= limit {
3778 break;
3779 }
3780 }
3781 assert!(
3782 f.server.memory_bytes() <= limit,
3783 "it never got under: {} against {limit}",
3784 f.server.memory_bytes()
3785 );
3786 let info = f.run(&[b"INFO", b"stats"]);
3787 assert!(!info.contains("evicted_keys:0"), "{info}");
3788 assert!(
3789 f.run(&[b"DBSIZE"]) != ":0\r\n",
3790 "and it did not empty the database to get there"
3791 );
3792 }
3793
3794 #[test]
3795 fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3796 let mut f = Fixture::new();
3803 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3804 let big = vec![b'v'; 200];
3805
3806 for i in 0..400u32 {
3807 let n = i.to_string();
3808 let n = n.as_bytes();
3809 f.run(&[b"SADD", b"s", n]);
3810 f.run(&[b"SADD", b"s2", &big]);
3811 f.run(&[b"HSET", b"h", n, &big]);
3812 f.run(&[b"RPUSH", b"l", &big]);
3813 f.run(&[b"ZADD", b"z", n, n]);
3814 f.run(&[b"ARSET", b"a", n, &big]);
3815 if i % 7 == 0 {
3816 f.run(&[b"SREM", b"s", n]);
3817 f.run(&[b"HDEL", b"h", n]);
3818 f.run(&[b"LPOP", b"l"]);
3819 f.run(&[b"ZREM", b"z", n]);
3820 f.run(&[b"ARDEL", b"a", n]);
3821 }
3822 if i % 53 == 0 {
3823 f.run(&[b"DEL", b"s2"]);
3826 }
3827 assert_eq!(
3828 f.server.settled_memory(),
3829 f.server.memory_bytes(),
3830 "after round {i}"
3831 );
3832 }
3833
3834 assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3837 assert!(
3838 f.server.memory_bytes() > 512 * 1024,
3839 "{}",
3840 f.server.memory_bytes()
3841 );
3842
3843 f.run(&[b"FLUSHALL"]);
3845 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3846 }
3847
3848 #[test]
3849 fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3850 let mut f = Fixture::new();
3855 for i in 0..200u32 {
3856 let n = i.to_string();
3857 f.run(&[b"SADD", b"s", n.as_bytes()]);
3858 f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3859 }
3860 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3861 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3862
3863 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3864 for i in 200..400u32 {
3865 let n = i.to_string();
3866 f.run(&[b"SADD", b"s", n.as_bytes()]);
3867 }
3868 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3869 assert_eq!(
3870 f.server.settled_memory(),
3871 f.server.memory_bytes(),
3872 "the writes it was not watching are in the number it started from"
3873 );
3874 }
3875
3876 #[test]
3877 fn evicted_keys_and_expired_keys_are_different_numbers() {
3878 let mut f = Fixture::new();
3879 f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3882 f.server.advance_clock_ms(20);
3883 f.run(&[b"GET", b"gone"]);
3884 let info = f.run(&[b"INFO", b"stats"]);
3885 assert!(info.contains("expired_keys:1"), "{info}");
3886 assert!(info.contains("evicted_keys:0"), "{info}");
3887 }
3888
3889 #[test]
3890 fn the_object_subcommands_follow_the_policy() {
3891 let mut f = Fixture::new();
3892 f.run(&[b"SET", b"s", b"v"]);
3893 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3897 assert!(
3898 f.run(&[b"OBJECT", b"FREQ", b"s"])
3899 .starts_with("-ERR An LFU maxmemory policy is not selected"),
3900 );
3901
3902 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3903 assert!(
3904 f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3905 .starts_with("-ERR An LFU maxmemory policy is selected"),
3906 );
3907 assert!(
3912 f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3913 "FREQ should answer under an LFU policy"
3914 );
3915 }
3916
3917 #[test]
3918 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3919 let mut f = Fixture::new();
3920 f.run(&[b"SET", b"s", b"hello"]);
3921 f.run(&[b"SET", b"n", b"123"]);
3922 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3923 f.run(&[b"SADD", b"ss", b"a", b"b"]);
3924 f.run(&[b"HSET", b"h", b"f", b"v"]);
3925 for (key, want) in [
3926 (b"s".as_slice(), "embstr"),
3927 (b"n", "int"),
3928 (b"si", "intset"),
3929 (b"ss", "listpack"),
3930 (b"h", "listpack"),
3931 ] {
3932 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3933 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3934 }
3935
3936 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3939 assert_eq!(
3940 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3941 "$10\r\nlistpackex\r\n"
3942 );
3943
3944 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3945 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3946 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3947 }
3948
3949 #[test]
3950 fn object_answers_nil_for_a_key_that_is_not_there() {
3951 let mut f = Fixture::new();
3952 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3953 assert_eq!(
3954 f.run(&[b"OBJECT", sub, b"nokey"]),
3955 "$-1\r\n",
3956 "a nil and not an error, which is what 8.10.1 does"
3957 );
3958 }
3959 f.run(&[b"SET", b"s", b"v"]);
3962 assert!(
3963 f.run(&[b"OBJECT", b"FREQ", b"s"])
3964 .starts_with("-ERR An LFU maxmemory policy is not"),
3965 );
3966 assert_eq!(
3967 f.run(&[b"OBJECT", b"NOPE", b"s"]),
3968 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3969 );
3970 assert_eq!(
3971 f.run(&[b"OBJECT", b"ENCODING"]),
3972 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3973 );
3974 assert_eq!(
3975 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3976 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3977 );
3978 assert_eq!(
3979 f.run(&[b"OBJECT"]),
3980 "-ERR wrong number of arguments for 'object' command\r\n"
3981 );
3982 }
3983
3984 #[test]
3985 fn config_moves_the_ladder_and_object_encoding_agrees() {
3986 let mut f = Fixture::new();
3987 assert_eq!(
3988 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3989 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3990 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3991 );
3992 assert_eq!(
3995 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3996 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3997 );
3998 assert!(
3999 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
4000 .starts_with("*8\r\n")
4001 );
4002 assert!(
4003 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
4004 .starts_with("*6\r\n")
4005 );
4006
4007 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
4008 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
4009
4010 assert_eq!(
4011 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
4012 "+OK\r\n",
4013 "written under the old name and read back under the new one"
4014 );
4015 assert_eq!(
4016 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
4017 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
4018 );
4019 assert_eq!(
4020 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
4021 "$8\r\nlistpack\r\n",
4022 "the hash that already exists is left exactly where it was"
4023 );
4024 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
4025 assert_eq!(
4026 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
4027 "$9\r\nhashtable\r\n",
4028 "and the next one built goes straight to a table"
4029 );
4030
4031 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
4033 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
4034 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
4035 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
4036 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
4037 assert_eq!(
4038 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
4039 "$9\r\nhashtable\r\n"
4040 );
4041 }
4042
4043 #[test]
4044 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
4045 let mut f = Fixture::new();
4046 assert_eq!(
4047 f.run(&[
4048 b"CONFIG",
4049 b"SET",
4050 b"hash-max-listpack-entries",
4051 b"7",
4052 b"set-max-listpack-entries",
4053 b"abc"
4054 ]),
4055 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
4056 );
4057 assert_eq!(
4058 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
4059 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
4060 "the pair in front of the bad one did not go in"
4061 );
4062 assert_eq!(
4065 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
4066 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
4067 );
4068 assert_eq!(
4069 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
4070 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
4071 );
4072 assert_eq!(
4075 f.run(&[
4076 b"CONFIG",
4077 b"SET",
4078 b"set-max-intset-entries",
4079 b"99999999999999999999"
4080 ]),
4081 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
4082 );
4083 assert_eq!(
4084 f.run(&[
4085 b"CONFIG",
4086 b"SET",
4087 b"set-max-intset-entries",
4088 b"9223372036854775807"
4089 ]),
4090 "+OK\r\n"
4091 );
4092 }
4093
4094 #[test]
4095 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
4096 let mut f = Fixture::new();
4097 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
4098 f.run(&[b"SELECT", b"3"]);
4099 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4100 assert_eq!(
4101 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
4102 "$9\r\nhashtable\r\n",
4103 "these are one server wide number in Redis, whatever a Keyspace carries"
4104 );
4105 }
4106
4107 #[test]
4108 fn info_reports_the_numbers_it_can_stand_behind() {
4109 let mut f = Fixture::new();
4110 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
4111 let all = f.run(&[b"INFO"]);
4112 assert!(all.contains("redis_version:8.8.0"), "{all}");
4113 assert!(
4114 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
4115 "{all}"
4116 );
4117 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
4118 assert!(all.contains("role:master"), "{all}");
4119 let clients = f.run(&[b"INFO", b"clients"]);
4121 assert!(clients.contains("connected_clients:0"), "{clients}");
4122 assert!(!clients.contains("redis_version"), "{clients}");
4123 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
4124 }
4125
4126 #[test]
4133 fn commandstats_is_asked_for_and_replication_is_not() {
4134 let mut f = Fixture::new();
4135 for arg in ["", "all", "default", "everything"] {
4136 let info = if arg.is_empty() {
4137 f.run(&[b"INFO"])
4138 } else {
4139 f.run(&[b"INFO", arg.as_bytes()])
4140 };
4141 assert!(info.contains("redis_version"), "{arg}: {info}");
4142 assert!(info.contains("used_cpu_user"), "{arg}: {info}");
4143 assert!(info.contains("used_memory"), "{arg}: {info}");
4144 assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
4145 let asked = arg == "all" || arg == "everything";
4146 assert_eq!(
4147 info.contains("rejected_calls"),
4148 asked,
4149 "{arg} should{} carry the command counters: {info}",
4150 if asked { "" } else { " not" }
4151 );
4152 }
4153
4154 let cpu = f.run(&[b"INFO", b"cpu"]);
4155 assert!(cpu.contains("used_cpu_user"), "{cpu}");
4156 assert!(!cpu.contains("used_memory"), "{cpu}");
4157
4158 let stats = f.run(&[b"INFO", b"commandSTATS"]);
4161 assert!(!stats.contains("used_memory"), "{stats}");
4162 assert!(stats.contains("rejected_calls"), "{stats}");
4163
4164 let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
4166 assert!(pair.contains("used_cpu_user"), "{pair}");
4167 assert!(!pair.contains("master_repl_offset"), "{pair}");
4168
4169 let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
4170 assert!(with_all.contains("used_memory"), "{with_all}");
4171 assert!(with_all.contains("master_repl_offset"), "{with_all}");
4172 assert!(with_all.contains("rejected_calls"), "{with_all}");
4173 assert_eq!(
4175 with_all.matches("used_cpu_user_children").count(),
4176 1,
4177 "{with_all}"
4178 );
4179
4180 let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
4181 assert!(with_default.contains("used_memory"), "{with_default}");
4182 assert!(
4183 with_default.contains("master_repl_offset"),
4184 "{with_default}"
4185 );
4186 assert!(!with_default.contains("rejected_calls"), "{with_default}");
4187 assert_eq!(
4188 with_default.matches("used_cpu_user_children").count(),
4189 1,
4190 "{with_default}"
4191 );
4192 }
4193
4194 #[test]
4203 fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
4204 let mut f = Fixture::new();
4205 let info = f.run(&[b"INFO", b"memory"]);
4206 for field in [
4207 "total_system_memory:",
4208 "mem_cgroup_limit:",
4209 "mem_limit:",
4210 "mem_budget:",
4211 ] {
4212 assert!(info.contains(field), "no {field} in {info}");
4213 }
4214
4215 let field = |name: &str| -> u64 {
4216 info.lines()
4217 .find_map(|l| l.strip_prefix(name))
4218 .unwrap_or_else(|| panic!("no {name} in {info}"))
4219 .trim()
4220 .parse()
4221 .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
4222 };
4223 let limit = field("mem_limit:");
4224 assert_eq!(field("mem_budget:"), limit / 4, "{info}");
4225 if limit != 0 {
4228 let host = field("total_system_memory:");
4229 let cgroup = field("mem_cgroup_limit:");
4230 assert!(
4231 limit == host || limit == cgroup,
4232 "the limit came from neither number: {info}"
4233 );
4234 }
4235 }
4236
4237 #[test]
4246 fn a_command_counts_what_it_did_separately_from_what_it_refused() {
4247 let mut f = Fixture::new();
4248 f.run(&[b"SET", b"k", b"v"]);
4249 f.run(&[b"SET", b"k", b"w"]);
4250 f.run(&[b"LPUSH", b"k", b"x"]);
4252 f.run(&[b"LPUSH", b"k"]);
4254
4255 let stats = f.run(&[b"INFO", b"commandstats"]);
4256 assert!(
4257 stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
4258 "{stats}"
4259 );
4260 assert!(
4261 stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
4262 "{stats}"
4263 );
4264 assert!(
4265 !stats.contains("cmdstat_zadd"),
4266 "a command nobody has sent has no row: {stats}"
4267 );
4268 }
4269
4270 #[test]
4274 fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
4275 let mut f = Fixture::new();
4276 for i in 0..3_000u32 {
4277 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4278 }
4279 for i in 0..1_000u32 {
4280 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4281 }
4282 assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
4283 f.advance(100);
4284 assert_eq!(
4285 f.run(&[b"DBSIZE"]),
4286 ":4000\r\n",
4287 "DBSIZE counts records and nothing has read past the dead ones yet"
4288 );
4289
4290 let mut spent = 0;
4292 for _ in 0..2_000 {
4293 spent += f.server.expire_step(4096);
4294 if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
4295 break;
4296 }
4297 }
4298 assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
4299 assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
4300 for i in 0..1_000u32 {
4301 assert_eq!(
4302 f.run(&[b"GET", format!("k{i}").as_bytes()]),
4303 "$1\r\nv\r\n",
4304 "it took a key that had no deadline"
4305 );
4306 }
4307 }
4308
4309 #[test]
4310 fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
4311 let mut f = Fixture::new();
4312 for i in 0..2_000u32 {
4313 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4314 }
4315 assert_eq!(f.server.expire_step(4096), 0);
4316 f.run(&[b"SELECT", b"3"]);
4318 f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
4319 f.advance(100);
4320 for _ in 0..64 {
4321 f.server.expire_step(4096);
4322 }
4323 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4324 f.run(&[b"SELECT", b"0"]);
4325 assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
4326 assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
4327 }
4328
4329 #[test]
4332 fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
4333 let mut f = Fixture::new();
4334 for i in 0..500u32 {
4335 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4336 }
4337 f.advance(100);
4338 let at = f.server.striped(0).now_ms();
4339 f.server.set_clock_ms(at);
4340 assert!(f.server.expire_slice(8) > 0, "the first one works");
4344 for _ in 0..1_000 {
4345 assert_eq!(
4346 f.server.expire_slice(8),
4347 0,
4348 "the millisecond has not moved and neither should this"
4349 );
4350 }
4351 assert!(
4352 f.server.striped(0).expires() > 400,
4353 "there is plenty left to take"
4354 );
4355 f.server.set_clock_ms(at + 1);
4356 assert!(f.server.expire_slice(8) > 0, "and then it goes again");
4357 }
4358
4359 #[test]
4362 fn info_keyspace_counts_the_keys_that_have_a_deadline() {
4363 let mut f = Fixture::new();
4364 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
4365 assert!(
4366 f.run(&[b"INFO", b"keyspace"])
4367 .contains("db0:keys=3,expires=0"),
4368 "none of them has one yet"
4369 );
4370 f.run(&[b"EXPIRE", b"a", b"1000"]);
4371 f.run(&[b"EXPIRE", b"b", b"1000"]);
4372 let two = f.run(&[b"INFO", b"keyspace"]);
4373 assert!(two.contains("db0:keys=3,expires=2"), "{two}");
4374 f.run(&[b"PERSIST", b"a"]);
4375 f.run(&[b"DEL", b"b"]);
4376 let none = f.run(&[b"INFO", b"keyspace"]);
4377 assert!(none.contains("db0:keys=2,expires=0"), "{none}");
4378
4379 f.run(&[b"SELECT", b"1"]);
4381 f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
4382 let both = f.run(&[b"INFO", b"keyspace"]);
4383 assert!(both.contains("db0:keys=2,expires=0"), "{both}");
4384 assert!(both.contains("db1:keys=1,expires=1"), "{both}");
4385 }
4386
4387 #[cfg(unix)]
4388 #[test]
4389 fn info_cpu_reports_processor_time_that_was_really_measured() {
4390 let mut f = Fixture::new();
4391 let cpu = f.run(&[b"INFO", b"cpu"]);
4392 assert!(cpu.contains("# CPU"), "{cpu}");
4393 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
4395 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
4396 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
4397 assert!(!cpu.contains("redis_version"), "{cpu}");
4398
4399 let before = used_cpu_user(&cpu);
4403 let mut n = 0u64;
4404 let mut rounds = 0;
4405 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
4406 for i in 0..1_000_000u64 {
4407 n = n.wrapping_add(i.wrapping_mul(i));
4408 }
4409 rounds += 1;
4410 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
4414 }
4415 }
4416
4417 #[cfg(unix)]
4419 fn used_cpu_user(info: &str) -> f64 {
4420 info.lines()
4421 .find_map(|l| l.strip_prefix("used_cpu_user:"))
4422 .expect("no used_cpu_user in the reply")
4423 .trim()
4424 .parse()
4425 .expect("used_cpu_user is not a number")
4426 }
4427
4428 #[test]
4434 fn a_command_that_fails_leaves_nothing_half_written() {
4435 let mut f = Fixture::new();
4436 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4437 assert_eq!(reply, "-ERR offset is out of range\r\n");
4438 assert!(!reply.contains(':'), "no integer went out in front of it");
4439 }
4440
4441 #[test]
4442 fn quit_answers_first_and_closes_after() {
4443 let mut f = Fixture::new();
4444 let (flow, reply) = f.flow(&[b"QUIT"]);
4445 assert_eq!(reply, "+OK\r\n");
4446 assert_eq!(flow, Flow::Close);
4447 }
4448
4449 #[test]
4456 fn shutdown_writes_nothing_and_sets_the_flag() {
4457 let mut f = Fixture::new();
4458 assert!(!f.server.stopping(), "nobody has asked yet");
4459
4460 let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4461 assert_eq!(reply, "");
4462 assert_eq!(flow, Flow::Close);
4463 assert!(f.server.stopping());
4464 }
4465
4466 #[test]
4474 fn shutdown_takes_the_flags_redis_takes() {
4475 for flags in [
4476 &[b"NOSAVE".as_slice()][..],
4477 &[b"SAVE"],
4478 &[b"NOW"],
4479 &[b"FORCE"],
4480 &[b"nosave"],
4481 &[b"NOW", b"NOW"],
4482 &[b"SAVE", b"SAVE"],
4483 &[b"NOSAVE", b"NOW", b"FORCE"],
4484 ] {
4485 let mut f = Fixture::new();
4486 let mut parts = vec![b"SHUTDOWN".as_slice()];
4487 parts.extend_from_slice(flags);
4488 let (flow, reply) = f.flow(&parts);
4489 assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4490 assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4491 assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4492 }
4493
4494 for flags in [
4495 &[b"BOGUS".as_slice()][..],
4496 &[b"SAVE", b"NOSAVE"],
4497 &[b"NOSAVE", b"SAVE"],
4498 &[b"ABORT", b"NOW"],
4499 &[b"NOSAVE", b"ABORT"],
4500 &[b"NOW", b"FORCE", b"ABORT"],
4501 ] {
4502 let mut f = Fixture::new();
4503 let mut parts = vec![b"SHUTDOWN".as_slice()];
4504 parts.extend_from_slice(flags);
4505 assert_eq!(
4506 f.run(&parts),
4507 "-ERR syntax error\r\n",
4508 "SHUTDOWN {flags:?} was accepted"
4509 );
4510 assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4511 }
4512 }
4513
4514 #[test]
4522 fn shutdown_abort_never_has_anything_to_abort() {
4523 let mut f = Fixture::new();
4524 for parts in [
4525 &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4526 &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4527 ] {
4528 assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4529 assert!(!f.server.stopping(), "an abort stopped the server");
4530 }
4531 }
4532
4533 struct Backups {
4541 f: Fixture,
4542 dir: PathBuf,
4543 }
4544
4545 impl Backups {
4546 fn new(name: &str) -> Backups {
4547 let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4548 let _ = std::fs::remove_dir_all(&dir);
4549 std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4550 let mut f = Fixture::new();
4551 f.server.set_dir(dir.clone());
4552 Backups { f, dir }
4553 }
4554
4555 fn run(&mut self, parts: &[&[u8]]) -> String {
4556 self.f.run(parts)
4557 }
4558
4559 fn files(&self) -> Vec<String> {
4561 let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4562 Ok(entries) => entries
4563 .filter_map(|e| e.ok())
4564 .map(|e| e.file_name().to_string_lossy().into_owned())
4565 .collect(),
4566 Err(_) => Vec::new(),
4567 };
4568 names.sort();
4569 names
4570 }
4571
4572 fn read(&self, name: &str) -> Vec<u8> {
4573 std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4574 }
4575 }
4576
4577 impl Drop for Backups {
4578 fn drop(&mut self) {
4579 let _ = std::fs::remove_dir_all(&self.dir);
4580 }
4581 }
4582
4583 #[test]
4586 fn backup_walks_the_states_the_reference_walks() {
4587 let mut b = Backups::new("states");
4588 let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4589
4590 assert!(status(&mut b).contains("idle"));
4591 assert!(b.files().is_empty(), "an idle server has written a backup");
4592
4593 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4594 assert!(status(&mut b).contains("incrementing"));
4595 assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4596
4597 assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4598 assert!(status(&mut b).contains("sealed"));
4599 assert_eq!(
4600 b.files(),
4601 [
4602 "appendonly.aof.1.base.rdb",
4603 "appendonly.aof.1.incr.aof",
4604 "appendonly.aof.manifest",
4605 ]
4606 );
4607
4608 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4609 assert!(status(&mut b).contains("idle"));
4610 assert!(b.files().is_empty(), "cleanup left something behind");
4611 }
4612
4613 #[test]
4615 fn backup_refuses_the_moves_the_reference_refuses() {
4616 let mut b = Backups::new("refusals");
4617
4618 assert_eq!(
4619 b.run(&[b"BACKUP", b"SEAL"]),
4620 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4621 );
4622 assert_eq!(
4623 b.run(&[b"BACKUP", b"ABORT"]),
4624 "-ERR No backup in progress\r\n"
4625 );
4626 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4629
4630 b.run(&[b"BACKUP", b"START"]);
4631 assert_eq!(
4632 b.run(&[b"BACKUP", b"START"]),
4633 "-ERR A backup is already in progress, ABORT it first\r\n"
4634 );
4635 assert_eq!(
4636 b.run(&[b"BACKUP", b"CLEANUP"]),
4637 "-ERR Backup is in progress\r\n"
4638 );
4639
4640 b.run(&[b"BACKUP", b"SEAL"]);
4641 assert_eq!(
4642 b.run(&[b"BACKUP", b"START"]),
4643 "-ERR A sealed backup exists, CLEANUP it first\r\n"
4644 );
4645 assert_eq!(
4646 b.run(&[b"BACKUP", b"SEAL"]),
4647 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4648 );
4649 assert_eq!(
4650 b.run(&[b"BACKUP", b"ABORT"]),
4651 "-ERR No backup in progress\r\n"
4652 );
4653 }
4654
4655 #[test]
4662 fn backup_abort_removes_the_file_and_says_who_did_it() {
4663 let mut b = Backups::new("abort");
4664 b.run(&[b"BACKUP", b"START"]);
4665 assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4666
4667 let status = b.run(&[b"BACKUP", b"STATUS"]);
4668 assert!(status.contains("failed"), "{status}");
4669 assert!(status.contains("aborted by user"), "{status}");
4670 assert!(b.files().is_empty(), "abort left the base file behind");
4671 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4672
4673 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4675 assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4676 let status = b.run(&[b"BACKUP", b"STATUS"]);
4677 assert!(status.contains("incrementing"), "{status}");
4678 assert!(!status.contains("aborted"), "the old error was kept");
4679 }
4680
4681 #[test]
4683 fn backup_list_names_the_files_that_are_pinned_so_far() {
4684 let mut b = Backups::new("list");
4685 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4686
4687 b.run(&[b"BACKUP", b"START"]);
4688 let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4689 let base = base.to_string_lossy().into_owned();
4690 assert_eq!(
4691 b.run(&[b"BACKUP", b"LIST"]),
4692 format!("*1\r\n${}\r\n{base}\r\n", base.len())
4693 );
4694
4695 b.run(&[b"BACKUP", b"SEAL"]);
4696 let listed = b.run(&[b"BACKUP", b"LIST"]);
4697 assert!(listed.starts_with("*3\r\n"), "{listed}");
4698 let names: Vec<&str> = listed
4701 .lines()
4702 .filter(|l| l.starts_with('/') || l.contains(":\\"))
4703 .collect();
4704 assert_eq!(names.len(), 3, "{listed}");
4705 assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4706 assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4707 assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4708 }
4709
4710 #[test]
4718 fn a_backup_holds_the_dataset_as_it_was_at_start() {
4719 let mut b = Backups::new("contents");
4720 b.run(&[b"SET", b"bk", b"v1"]);
4721 b.run(&[b"BACKUP", b"START"]);
4722 b.run(&[b"SET", b"bk", b"v2"]);
4723 b.run(&[b"BACKUP", b"SEAL"]);
4724
4725 let base = b.read("appendonly.aof.1.base.rdb");
4726 assert!(base.starts_with(b"REDIS"), "not an RDB file");
4727 assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4728 assert!(
4729 !base.windows(2).any(|w| w == b"v2"),
4730 "the base file moved on after START"
4731 );
4732 let at = base
4737 .windows(8)
4738 .position(|w| w == b"aof-base")
4739 .expect("no aof-base aux field");
4740 assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4741
4742 assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4743 assert_eq!(
4744 String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4745 "file appendonly.aof.1.base.rdb seq 1 type b\n\
4746 file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4747 );
4748 }
4749
4750 #[test]
4753 fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4754 let mut b = Backups::new("status");
4755 b.f.server.set_clock_ms(1_700_000_000_000);
4756
4757 assert_eq!(
4758 b.run(&[b"BACKUP", b"STATUS"]),
4759 "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4760 $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4761 );
4762
4763 b.f.out = Out::new(Proto::Resp3);
4764 b.run(&[b"BACKUP", b"START"]);
4765 assert_eq!(
4766 b.run(&[b"BACKUP", b"STATUS"]),
4767 "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4768 $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4769 );
4770
4771 b.run(&[b"BACKUP", b"SEAL"]);
4772 let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4773 assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4774 }
4775
4776 #[test]
4779 fn a_sealed_backup_is_swept_away_after_the_timeout() {
4780 let mut b = Backups::new("ttl");
4781 b.f.server.set_clock_ms(1_000_000);
4782 assert_eq!(
4783 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4784 "+OK\r\n"
4785 );
4786 b.run(&[b"BACKUP", b"START"]);
4787 b.run(&[b"BACKUP", b"SEAL"]);
4788
4789 b.f.server.set_clock_ms(1_000_000 + 59_000);
4791 b.f.server.backup_expire();
4792 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4793 assert_eq!(b.files().len(), 3);
4794
4795 b.f.server.set_clock_ms(1_000_000 + 60_000);
4796 b.f.server.backup_expire();
4797 let status = b.run(&[b"BACKUP", b"STATUS"]);
4798 assert!(status.contains("idle"), "{status}");
4799 assert!(b.files().is_empty(), "the timeout left the files behind");
4800
4801 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4803 b.run(&[b"BACKUP", b"START"]);
4804 b.run(&[b"BACKUP", b"SEAL"]);
4805 b.f.server.set_clock_ms(9_000_000_000);
4806 b.f.server.backup_expire();
4807 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4808 }
4809
4810 #[test]
4813 fn the_backup_settings_behave_the_way_the_reference_does() {
4814 let mut b = Backups::new("config");
4815 let dir = b.dir.to_string_lossy().into_owned();
4816
4817 assert_eq!(
4818 b.run(&[b"CONFIG", b"GET", b"dir"]),
4819 format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4820 );
4821 assert_eq!(
4822 b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4823 "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4824 );
4825 assert_eq!(
4826 b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4827 "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4828 );
4829
4830 assert_eq!(
4833 b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4834 "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4835 );
4836 assert_eq!(
4837 b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4838 "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4839 );
4840 assert!(
4841 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4842 .contains("argument couldn't be parsed into an integer")
4843 );
4844 assert!(
4845 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4846 .contains("argument must be between 0 and 9223372036854775807 inclusive")
4847 );
4848 }
4849
4850 #[test]
4852 fn backup_help_is_the_text_the_reference_sends() {
4853 let mut f = Fixture::new();
4854 let help = f.run(&[b"BACKUP", b"HELP"]);
4855 assert!(help.starts_with("*17\r\n"), "{help}");
4856 assert!(
4857 help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4858 );
4859 assert!(help.contains("+ Start a new backup into the configured 'backupdirname'.\r\n"));
4860 assert!(help.contains("+ Freeze the current backup (BASE + INCR + manifest).\r\n"));
4861 assert!(help.contains("+ Return this help.\r\n+HELP\r\n+ Print this help.\r\n"));
4862 }
4863
4864 #[test]
4872 fn backup_refuses_what_it_cannot_read() {
4873 let mut f = Fixture::new();
4874 assert_eq!(
4875 f.run(&[b"BACKUP"]),
4876 "-ERR wrong number of arguments for 'backup' command\r\n"
4877 );
4878 assert_eq!(
4879 f.run(&[b"BACKUP", b"START", b"x"]),
4880 "-ERR wrong number of arguments for 'backup' command\r\n"
4881 );
4882 assert_eq!(
4883 f.run(&[b"BACKUP", b"NOPE"]),
4884 "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4885 );
4886 }
4887
4888 #[test]
4889 fn the_command_counter_counts_every_command_including_the_bad_ones() {
4890 let mut f = Fixture::new();
4891 f.run(&[b"PING"]);
4892 f.run(&[b"NOPE"]);
4893 f.run(&[b"GET"]);
4894 assert_eq!(f.server.totals().commands, 3);
4895 }
4896
4897 #[test]
4898 fn what_a_thread_marked_is_taken_by_the_maintenance_turn() {
4899 let mut server = Server::new();
4900 server.set_threads(2);
4901 server.dirty = 0;
4904 server.locals[1].mark(1 << 9);
4905 server.collect_marks();
4906 assert_ne!(server.dirty & (1 << 9), 0);
4907 assert_eq!(server.locals[1].dirty.load(Relaxed), 0);
4909 }
4910
4911 #[test]
4912 fn what_two_threads_counted_is_added_up_when_info_asks() {
4913 let mut server = Server::new();
4914 server.set_threads(2);
4915 let ping = lookup(b"PING").expect("PING is a command");
4919 for (at, calls) in [(0, 2), (1, 3)] {
4920 let counters = &server.locals[at];
4921 for _ in 0..calls {
4922 counters.stats.commands.bump();
4923 counters.cmdstats.at(ping).calls.bump();
4924 }
4925 counters.stats.opened();
4926 }
4927 assert_eq!(server.totals().commands, 5);
4928 assert_eq!(server.totals().clients, 2);
4929 assert_eq!(server.totals().connections, 2);
4930 let rows: Vec<_> = server.command_stats().collect();
4931 assert_eq!(rows.len(), 1);
4932 assert_eq!(rows[0].0, "ping");
4933 assert_eq!(rows[0].1.calls, 5);
4934 server.reset_stats();
4937 assert_eq!(server.totals().commands, 0);
4938 assert_eq!(server.totals().connections, 0);
4939 assert_eq!(server.totals().clients, 2);
4940 }
4941
4942 #[test]
4943 fn the_parked_count_says_what_the_waiter_list_says() {
4944 let mut f = Fixture::new();
4945 assert_eq!(f.server.parked(), 0);
4946 for client in 1..=3u64 {
4947 f.session = Session::new(client);
4948 assert_eq!(f.flow(&[b"BLPOP", b"q", b"0"]).0, Flow::Block);
4949 }
4950 assert_eq!(f.server.parked(), 3);
4951 assert_eq!(f.server.waiters().len(), 3);
4952
4953 f.server.drop_waiter(1);
4957 assert_eq!(f.server.parked(), f.server.waiters().len());
4958 f.server.forget_waiters(1);
4959 assert_eq!(f.server.parked(), f.server.waiters().len());
4960 f.run(&[b"RPUSH", b"q", b"v"]);
4961 let mut out = Out::new(Proto::Resp2);
4962 assert!(f.server.serve_waiter(0, 0, &mut out));
4963 f.server.drop_waiter(0);
4964 assert_eq!(f.server.parked(), 0);
4965 assert!(f.server.waiters().is_empty());
4966 }
4967
4968 #[test]
4969 fn a_set_goes_from_bytes_to_bytes() {
4970 let mut f = Fixture::new();
4971 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4972 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4973 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4974 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4975 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4976 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4977 assert_eq!(
4978 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4979 "*3\r\n:1\r\n:0\r\n:1\r\n"
4980 );
4981 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4982 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4983 }
4984
4985 #[test]
4986 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4987 let mut f = Fixture::new();
4988 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4989 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4990 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4991 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4992 assert_eq!(
4993 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4994 "*2\r\n:0\r\n:0\r\n"
4995 );
4996 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4997 }
4998
4999 #[test]
5000 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
5001 let mut f = Fixture::new();
5005 f.run(&[b"SADD", b"s", b"one"]);
5006 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
5007
5008 f.run(&[b"HELLO", b"3"]);
5009 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
5010 }
5011
5012 #[test]
5013 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
5014 let mut f = Fixture::new();
5017 f.run(&[b"SADD", b"s", b"42"]);
5018 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
5019 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
5020 assert_eq!(
5021 f.run(&[b"SISMEMBER", b"s", b"042"]),
5022 ":0\r\n",
5023 "the member is the bytes and not the number they parse to"
5024 );
5025 }
5026
5027 #[test]
5028 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
5029 let mut f = Fixture::new();
5030 f.run(&[b"SET", b"str", b"v"]);
5031 f.run(&[b"SADD", b"set", b"a"]);
5032
5033 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5034 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
5035 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
5036 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
5037 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
5038 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
5039 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
5040 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
5041 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
5042
5043 assert_eq!(
5046 f.run(&[b"MGET", b"str", b"set", b"nope"]),
5047 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
5048 );
5049 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
5051 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
5052 }
5053
5054 #[test]
5055 fn a_wrongtype_leaves_nothing_half_written() {
5056 let mut f = Fixture::new();
5060 f.run(&[b"SET", b"k", b"v"]);
5061 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
5062 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
5063 assert!(!reply.contains('*'), "an array header went out in front");
5064 }
5065
5066 #[test]
5067 fn emptying_a_set_takes_the_key_with_it() {
5068 let mut f = Fixture::new();
5069 f.run(&[b"SADD", b"s", b"a", b"b"]);
5070 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
5071 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
5072 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
5073 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
5074 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5075 }
5076
5077 fn split_scan(reply: &str) -> (String, Vec<String>) {
5083 let mut lines = reply.split("\r\n");
5084 assert_eq!(lines.next(), Some("*2"), "got {reply}");
5085 lines.next().expect("the cursor header");
5086 let cursor = lines.next().expect("the cursor").to_owned();
5087 let header = lines.next().expect("the member header");
5088 let n: usize = header[1..].parse().expect("a member count");
5089 let mut members = Vec::with_capacity(n);
5090 for _ in 0..n {
5091 lines.next().expect("a member header");
5092 members.push(lines.next().expect("a member").to_owned());
5093 }
5094 (cursor, members)
5095 }
5096
5097 #[test]
5098 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
5099 let mut f = Fixture::new();
5100 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
5101
5102 let one = f.run(&[b"SPOP", b"s"]);
5103 assert!(
5104 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
5105 "got {one}"
5106 );
5107 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
5108
5109 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
5111 assert!(rest.starts_with("*3\r\n"), "got {rest}");
5112 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
5113 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
5115 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
5116 }
5117
5118 #[test]
5119 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
5120 let mut f = Fixture::new();
5125 f.run(&[b"HELLO", b"3"]);
5126 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
5127
5128 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
5129 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
5131
5132 f.run(&[b"SADD", b"one", b"z"]);
5136 assert_eq!(
5137 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
5138 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
5139 );
5140 }
5141
5142 #[test]
5143 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
5144 let mut f = Fixture::new();
5145 f.run(&[b"SADD", b"s", b"only"]);
5146 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5147 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5148 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
5149
5150 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
5151 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
5154 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
5155 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
5157 }
5158
5159 #[test]
5160 fn a_pop_count_that_is_not_a_positive_number_says_so() {
5161 let mut f = Fixture::new();
5162 f.run(&[b"SADD", b"s", b"a"]);
5163 let bad = "-ERR value is out of range, must be positive\r\n";
5164 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
5165 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
5166 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
5167 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
5169 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
5170 }
5171
5172 #[test]
5173 fn a_scan_walks_a_set_of_any_size_exactly_once() {
5174 let mut f = Fixture::new();
5175 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
5176 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
5177 .into_iter()
5178 .chain(members.iter().map(Vec::as_slice))
5179 .collect();
5180 f.run(&args);
5181
5182 let mut seen = Vec::new();
5183 let mut cursor = "0".to_owned();
5184 loop {
5185 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
5186 let (next, got) = split_scan(&reply);
5187 seen.extend(got);
5188 cursor = next;
5189 if cursor == "0" {
5190 break;
5191 }
5192 }
5193 seen.sort();
5194 seen.dedup();
5195 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
5196
5197 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
5200 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
5201 assert_eq!(cursor, "0");
5202 assert_eq!(got.len(), 3);
5203 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
5205 }
5206
5207 #[test]
5208 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
5209 let mut f = Fixture::new();
5210 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
5211
5212 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
5213 let mut got = got;
5214 got.sort();
5215 assert_eq!(got, ["aa", "ab"]);
5216
5217 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
5220 let mut got = got;
5221 got.sort();
5222 assert_eq!(got, ["12", "13"]);
5223
5224 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
5225 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
5226 assert_eq!(
5227 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
5228 "-ERR syntax error\r\n"
5229 );
5230 assert_eq!(
5233 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
5234 "-ERR syntax error\r\n"
5235 );
5236 }
5237
5238 #[test]
5239 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
5240 let mut f = Fixture::new();
5241 f.run(&[b"SADD", b"src", b"a", b"b"]);
5242 f.run(&[b"SADD", b"dst", b"c"]);
5243
5244 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
5245 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
5246 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
5247 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
5249 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
5250
5251 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
5254 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
5255 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
5256 }
5257
5258 #[test]
5259 fn moving_checks_the_types_in_the_order_redis_checks_them() {
5260 let mut f = Fixture::new();
5264 f.run(&[b"SET", b"str", b"v"]);
5265 f.run(&[b"SADD", b"set", b"a"]);
5266
5267 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5268 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
5269 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
5270 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
5271 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
5272 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
5273 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
5274 assert_eq!(
5275 f.run(&[b"SISMEMBER", b"set", b"a"]),
5276 ":1\r\n",
5277 "and none of that moved anything"
5278 );
5279 }
5280
5281 #[test]
5282 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5283 let mut f = Fixture::new();
5286 f.run(&[b"SADD", b"s", b"a"]);
5287 for bad in [
5288 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
5289 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
5290 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
5291 ] {
5292 let reply = f.run(bad);
5293 assert!(reply.starts_with("-ERR"), "got {reply}");
5294 assert!(!reply.contains('*'), "an array header went out in front");
5295 }
5296 }
5297
5298 #[test]
5299 fn a_hash_writes_reads_and_deletes_its_fields() {
5300 let mut f = Fixture::new();
5301 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
5302 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
5303 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5304 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
5305 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
5306 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
5307 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
5308 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
5309 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
5310 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
5311
5312 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
5315
5316 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
5317 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
5318 assert_eq!(
5319 f.run(&[b"EXISTS", b"h"]),
5320 ":0\r\n",
5321 "and losing the last field lost the key"
5322 );
5323 }
5324
5325 #[test]
5326 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
5327 let mut f = Fixture::new();
5328 f.run(&[b"HSET", b"h", b"a", b"1"]);
5329 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
5330 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
5331 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
5332 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
5333 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
5334
5335 f.run(&[b"HELLO", b"3"]);
5336 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
5337 assert_eq!(
5338 f.run(&[b"HGETALL", b"nokey"]),
5339 "%0\r\n",
5340 "a missing key is the empty hash and never a nil"
5341 );
5342 assert_eq!(
5343 f.run(&[b"HKEYS", b"h"]),
5344 "*1\r\n$1\r\na\r\n",
5345 "and the two that answer one side stay arrays"
5346 );
5347 }
5348
5349 #[test]
5350 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
5351 let mut f = Fixture::new();
5352 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
5353 assert_eq!(
5354 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
5355 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
5356 "the reply is positional, so b is a nil and not a gap"
5357 );
5358 assert_eq!(
5359 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
5360 "*2\r\n$-1\r\n$-1\r\n",
5361 "and a missing key is all nils rather than an empty array"
5362 );
5363
5364 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
5365 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
5366 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5367 }
5368
5369 #[test]
5370 fn a_hash_counts_up_and_says_so_when_it_cannot() {
5371 let mut f = Fixture::new();
5372 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
5373 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
5374 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
5375 assert_eq!(
5376 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
5377 "$4\r\n10.5\r\n",
5378 "a bulk string and not a double, on both protocols"
5379 );
5380
5381 f.run(&[b"HSET", b"h", b"s", b"words"]);
5382 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
5383 assert!(
5384 bad.starts_with("-ERR hash value is not an integer"),
5385 "{bad}"
5386 );
5387 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
5388 assert!(
5389 bad.starts_with("-ERR value is not an integer"),
5390 "a bad argument is not yet a hash value, {bad}"
5391 );
5392 assert_eq!(
5393 f.run(&[b"HGET", b"h", b"s"]),
5394 "$5\r\nwords\r\n",
5395 "and neither of them wrote anything"
5396 );
5397 }
5398
5399 #[test]
5400 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
5401 let mut f = Fixture::new();
5402 for i in 0..500 {
5403 let field = format!("field-{i}");
5404 let value = format!("value-{i}");
5405 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
5406 }
5407
5408 let mut seen: Vec<String> = Vec::new();
5409 let mut cursor = "0".to_owned();
5410 loop {
5411 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
5412 let (next, items) = scan_reply(&reply);
5413 assert_eq!(items.len() % 2, 0, "a pair went out half written");
5414 for pair in items.chunks(2) {
5415 assert_eq!(
5416 pair[0].strip_prefix("field-"),
5417 pair[1].strip_prefix("value-"),
5418 "a field came back with someone else's value"
5419 );
5420 seen.push(pair[0].clone());
5421 }
5422 cursor = next;
5423 if cursor == "0" {
5424 break;
5425 }
5426 }
5427 seen.sort();
5428 seen.dedup();
5429 assert_eq!(seen.len(), 500, "every field once and only once");
5430
5431 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
5432 assert!(
5433 items.iter().all(|s| s.starts_with("field-")),
5434 "NOVALUES still sent the values"
5435 );
5436
5437 let (_, one) = scan_reply(&f.run(&[
5438 b"HSCAN",
5439 b"h",
5440 b"0",
5441 b"MATCH",
5442 b"field-499",
5443 b"COUNT",
5444 b"1000",
5445 ]));
5446 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
5447 }
5448
5449 #[test]
5450 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
5451 let mut f = Fixture::new();
5452 f.run(&[b"HSET", b"h", b"a", b"1"]);
5453 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
5454 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
5455 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
5456 assert_eq!(
5457 f.run(&[b"HRANDFIELD", b"h", b"3"]),
5458 "*1\r\n$1\r\na\r\n",
5459 "a positive count is capped at the size of the hash"
5460 );
5461 assert_eq!(
5462 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
5463 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
5464 "and a negative one repeats itself"
5465 );
5466 assert_eq!(
5467 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5468 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5469 "flat on RESP2"
5470 );
5471
5472 f.run(&[b"HELLO", b"3"]);
5473 assert_eq!(
5474 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5475 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5476 "and nested on RESP3, but still an array and never a map"
5477 );
5478 }
5479
5480 #[test]
5481 fn every_hash_command_says_wrongtype_and_writes_nothing() {
5482 let mut f = Fixture::new();
5483 f.run(&[b"SET", b"str", b"v"]);
5484 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5485
5486 for cmd in [
5487 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
5488 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
5489 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5490 &[b"HGET".as_slice(), b"str", b"f"][..],
5491 &[b"HMGET".as_slice(), b"str", b"f"][..],
5492 &[b"HDEL".as_slice(), b"str", b"f"][..],
5493 &[b"HLEN".as_slice(), b"str"][..],
5494 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5495 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5496 &[b"HGETALL".as_slice(), b"str"][..],
5497 &[b"HKEYS".as_slice(), b"str"][..],
5498 &[b"HVALS".as_slice(), b"str"][..],
5499 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5500 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5501 &[b"HRANDFIELD".as_slice(), b"str"][..],
5502 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5503 &[b"HSCAN".as_slice(), b"str", b"0"][..],
5504 ] {
5505 let reply = f.run(cmd);
5506 assert_eq!(reply, wrong, "{:?}", cmd[0]);
5507 }
5508 assert_eq!(
5509 f.run(&[b"GET", b"str"]),
5510 "$1\r\nv\r\n",
5511 "and none of them touched the value"
5512 );
5513 }
5514
5515 #[test]
5516 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5517 let mut f = Fixture::new();
5518 f.run(&[b"HSET", b"h", b"f", b"v"]);
5519 for bad in [
5520 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5521 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5522 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5523 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5524 ] {
5525 let reply = f.run(bad);
5526 assert!(reply.starts_with("-ERR"), "got {reply}");
5527 assert!(!reply.contains('*'), "an array header went out in front");
5528 }
5529 }
5530
5531 #[test]
5532 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5533 let mut f = Fixture::new();
5534 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5535 assert_eq!(
5536 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5537 "*1\r\n:1\r\n"
5538 );
5539 assert_eq!(
5540 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5541 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5542 "one answer per field, and the two sentinels are TTL's own"
5543 );
5544
5545 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5548 assert!((99_000..=100_000).contains(&ms), "got {ms}");
5549 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5550 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5551 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5552 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5553
5554 assert_eq!(
5555 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5556 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5557 "one for the deadline taken off, and it does not say what it was"
5558 );
5559 assert_eq!(
5560 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5561 "*1\r\n:-1\r\n"
5562 );
5563 assert_eq!(
5564 f.run(&[b"HGET", b"h", b"a"]),
5565 "$1\r\n1\r\n",
5566 "and the field is still there with the value it had"
5567 );
5568 }
5569
5570 #[test]
5571 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5572 let mut f = Fixture::new();
5573 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5574 assert_eq!(
5575 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5576 "*1\r\n:2\r\n",
5577 "two, and not one, because nothing was stored"
5578 );
5579 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5580 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5581
5582 assert_eq!(
5583 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5584 "*1\r\n:2\r\n"
5585 );
5586 assert_eq!(
5587 f.run(&[b"EXISTS", b"h"]),
5588 ":0\r\n",
5589 "and the last field going took the key with it"
5590 );
5591
5592 f.run(&[b"HSET", b"h", b"a", b"1"]);
5595 assert_eq!(
5596 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5597 "*1\r\n:2\r\n"
5598 );
5599 }
5600
5601 #[test]
5602 fn a_field_is_gone_once_its_moment_passes() {
5603 let mut f = Fixture::new();
5604 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5605 assert_eq!(
5606 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5607 "*1\r\n:1\r\n"
5608 );
5609 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5610
5611 f.server.advance_clock_ms(60);
5615 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5616 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5617 assert_eq!(
5618 f.run(&[b"HGETALL", b"h"]),
5619 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5620 "and the walks do not hand back a field that has expired"
5621 );
5622 }
5623
5624 #[test]
5625 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5626 let mut f = Fixture::new();
5627 for cmd in [
5628 &[
5629 b"HEXPIRE".as_slice(),
5630 b"nokey",
5631 b"100",
5632 b"FIELDS",
5633 b"2",
5634 b"a",
5635 b"b",
5636 ][..],
5637 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5638 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5639 &[
5640 b"HEXPIRETIME".as_slice(),
5641 b"nokey",
5642 b"FIELDS",
5643 b"2",
5644 b"a",
5645 b"b",
5646 ][..],
5647 &[
5648 b"HPERSIST".as_slice(),
5649 b"nokey",
5650 b"FIELDS",
5651 b"2",
5652 b"a",
5653 b"b",
5654 ][..],
5655 ] {
5656 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5657 }
5658 }
5659
5660 #[test]
5661 fn writing_a_field_clears_the_deadline_that_was_on_it() {
5662 let mut f = Fixture::new();
5663 f.run(&[b"HSET", b"h", b"a", b"1"]);
5664 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5665 f.run(&[b"HSET", b"h", b"a", b"2"]);
5666 assert_eq!(
5667 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5668 "*1\r\n:-1\r\n",
5669 "Redis has done this since 7.4, and it is why HGETEX exists"
5670 );
5671 }
5672
5673 #[test]
5674 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5675 let mut f = Fixture::new();
5676 f.run(&[b"HSET", b"h", b"a", b"1"]);
5677 assert_eq!(
5678 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5679 "*1\r\n:0\r\n",
5680 "XX on a field with no deadline changes nothing"
5681 );
5682 assert_eq!(
5683 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5684 "*1\r\n:1\r\n"
5685 );
5686 assert_eq!(
5687 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5688 "*1\r\n:0\r\n",
5689 "and NX will not move one that is already there"
5690 );
5691 assert_eq!(
5692 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5693 "*1\r\n:0\r\n"
5694 );
5695 assert_eq!(
5696 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5697 "*1\r\n:1\r\n"
5698 );
5699 assert_eq!(
5700 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5701 "*1\r\n:1\r\n"
5702 );
5703 assert_eq!(
5704 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5705 "*1\r\n:50\r\n"
5706 );
5707 }
5708
5709 #[test]
5710 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5711 let mut f = Fixture::new();
5712 f.run(&[b"HSET", b"h", b"a", b"1"]);
5713 for (bad, want) in [
5714 (
5715 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5716 "-ERR invalid expire time, must be >= 0",
5717 ),
5718 (
5719 &[
5720 b"HEXPIRE".as_slice(),
5721 b"h",
5722 b"9999999999999999",
5723 b"FIELDS",
5724 b"1",
5725 b"a",
5726 ][..],
5727 "-ERR invalid expire time in 'hexpire' command",
5728 ),
5729 (
5730 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5731 "-ERR wrong number of arguments for 'hexpire' command",
5732 ),
5733 (
5734 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5735 "-ERR Parameter `numFields` should be greater than 0",
5736 ),
5737 (
5738 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5739 "-ERR wrong number of arguments",
5740 ),
5741 (
5742 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5743 "-ERR wrong number of arguments",
5744 ),
5745 ] {
5746 let reply = f.run(bad);
5747 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5748 assert!(!reply.contains('*'), "an array header went out in front");
5749 }
5750 assert_eq!(
5751 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5752 "*1\r\n:-1\r\n",
5753 "and not one of them put a deadline on anything"
5754 );
5755 }
5756
5757 #[test]
5758 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5759 let mut f = Fixture::new();
5760 f.run(&[b"SET", b"str", b"v"]);
5761 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5762
5763 for cmd in [
5764 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5765 &[
5766 b"HPEXPIRE".as_slice(),
5767 b"str",
5768 b"100",
5769 b"FIELDS",
5770 b"1",
5771 b"f",
5772 ][..],
5773 &[
5774 b"HEXPIREAT".as_slice(),
5775 b"str",
5776 b"9999999999",
5777 b"FIELDS",
5778 b"1",
5779 b"f",
5780 ][..],
5781 &[
5782 b"HPEXPIREAT".as_slice(),
5783 b"str",
5784 b"9999999999999",
5785 b"FIELDS",
5786 b"1",
5787 b"f",
5788 ][..],
5789 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5790 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5791 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5792 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5793 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5794 ] {
5795 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5796 }
5797 assert_eq!(
5798 f.run(&[b"GET", b"str"]),
5799 "$1\r\nv\r\n",
5800 "and none of them touched the value"
5801 );
5802 }
5803
5804 #[test]
5805 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5806 let mut f = Fixture::new();
5807 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5808 assert_eq!(
5809 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5810 "*2\r\n$1\r\n1\r\n$-1\r\n",
5811 "positional, so the field that was not there is a nil in its place"
5812 );
5813 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5814 assert_eq!(
5815 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5816 "*1\r\n$-1\r\n"
5817 );
5818 assert_eq!(
5819 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5820 "*1\r\n$1\r\n2\r\n"
5821 );
5822 assert_eq!(
5823 f.run(&[b"EXISTS", b"h"]),
5824 ":0\r\n",
5825 "and the last field took the key"
5826 );
5827 }
5828
5829 #[test]
5830 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5831 let mut f = Fixture::new();
5832 f.run(&[b"HSET", b"h", b"a", b"1"]);
5833 assert_eq!(
5834 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5835 "*1\r\n$1\r\n1\r\n"
5836 );
5837 assert_eq!(
5838 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5839 "*1\r\n:-1\r\n",
5840 "no option means leave it alone, which is the one place this is not GETEX"
5841 );
5842
5843 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
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 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5849 assert_eq!(
5850 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5851 "*1\r\n:100\r\n",
5852 "and a plain read really does leave it alone"
5853 );
5854 assert_eq!(
5855 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5856 "*1\r\n$1\r\n1\r\n"
5857 );
5858 assert_eq!(
5859 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5860 "*1\r\n:-1\r\n"
5861 );
5862
5863 assert_eq!(
5864 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5865 "*1\r\n$1\r\n1\r\n",
5866 "the value goes out before the deadline that has already gone is applied"
5867 );
5868 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5869 assert_eq!(
5870 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5871 "*1\r\n$-1\r\n"
5872 );
5873 }
5874
5875 #[test]
5876 fn hsetex_writes_all_of_it_or_none_of_it() {
5877 let mut f = Fixture::new();
5878 assert_eq!(
5879 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5880 ":1\r\n"
5881 );
5882 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5883 assert_eq!(
5884 f.run(&[
5885 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5886 ]),
5887 ":0\r\n",
5888 "FNX wants every field named to be missing"
5889 );
5890 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5891 assert_eq!(
5892 f.run(&[b"HEXISTS", b"h", b"new"]),
5893 ":0\r\n",
5894 "and none of the list was written"
5895 );
5896 assert_eq!(
5897 f.run(&[
5898 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5899 ]),
5900 ":0\r\n",
5901 "and FXX wants every one of them to be there"
5902 );
5903 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5904 assert_eq!(
5905 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5906 ":1\r\n"
5907 );
5908 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5909
5910 assert_eq!(
5911 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5912 ":0\r\n"
5913 );
5914 assert_eq!(
5915 f.run(&[b"EXISTS", b"gone"]),
5916 ":0\r\n",
5917 "a key with no fields cannot meet FXX and is not created trying"
5918 );
5919 }
5920
5921 #[test]
5922 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5923 let mut f = Fixture::new();
5924 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5925 assert_eq!(
5926 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5927 "*1\r\n:100\r\n"
5928 );
5929
5930 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5931 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5932 assert_eq!(
5933 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5934 "*1\r\n:100\r\n",
5935 "KEEPTTL put back what the write cleared"
5936 );
5937
5938 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5939 assert_eq!(
5940 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5941 "*1\r\n:-1\r\n",
5942 "and without it a write clears the deadline the way HSET does"
5943 );
5944
5945 assert_eq!(
5948 f.run(&[
5949 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5950 ]),
5951 ":1\r\n"
5952 );
5953 assert_eq!(
5954 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5955 "*1\r\n:100\r\n"
5956 );
5957
5958 assert_eq!(
5959 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5960 ":1\r\n",
5961 "written, and not the separate code the HEXPIRE family has for this"
5962 );
5963 assert_eq!(
5964 f.run(&[b"EXISTS", b"h"]),
5965 ":0\r\n",
5966 "and storing it and then removing it emptied the hash"
5967 );
5968 }
5969
5970 #[test]
5971 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5972 let mut f = Fixture::new();
5973 f.run(&[b"HSET", b"h", b"a", b"1"]);
5974 for (bad, want) in [
5975 (
5977 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5978 "-ERR Number of fields must be a positive integer",
5979 ),
5980 (
5981 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5982 "-ERR The `numfields` parameter must match the number of arguments",
5983 ),
5984 (
5985 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5986 "-ERR Mandatory argument FIELDS is missing or not at the right position",
5987 ),
5988 (
5990 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5991 "-ERR invalid number of fields",
5992 ),
5993 (
5994 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5995 "-ERR wrong number of arguments",
5996 ),
5997 (
5998 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5999 "-ERR unknown argument: FIELD",
6000 ),
6001 (
6002 &[
6003 b"HGETEX".as_slice(),
6004 b"h",
6005 b"KEEPTTL",
6006 b"FIELDS",
6007 b"1",
6008 b"a",
6009 ][..],
6010 "-ERR unknown argument: KEEPTTL",
6011 ),
6012 (
6013 &[
6014 b"HGETEX".as_slice(),
6015 b"h",
6016 b"EX",
6017 b"100",
6018 b"PERSIST",
6019 b"FIELDS",
6020 b"1",
6021 b"a",
6022 ][..],
6023 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
6024 ),
6025 (
6026 &[
6027 b"HSETEX".as_slice(),
6028 b"h",
6029 b"EX",
6030 b"1",
6031 b"KEEPTTL",
6032 b"FIELDS",
6033 b"1",
6034 b"a",
6035 b"1",
6036 ][..],
6037 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
6038 ),
6039 (
6040 &[
6041 b"HSETEX".as_slice(),
6042 b"h",
6043 b"FNX",
6044 b"FXX",
6045 b"FIELDS",
6046 b"1",
6047 b"a",
6048 b"1",
6049 ][..],
6050 "-ERR Only one of FXX or FNX arguments can be specified",
6051 ),
6052 (
6053 &[
6054 b"HSETEX".as_slice(),
6055 b"h",
6056 b"FIELDS",
6057 b"2",
6058 b"a",
6059 b"1",
6060 b"b",
6061 ][..],
6062 "-ERR wrong number of arguments",
6063 ),
6064 (
6065 &[
6066 b"HGETEX".as_slice(),
6067 b"h",
6068 b"EX",
6069 b"-1",
6070 b"FIELDS",
6071 b"1",
6072 b"a",
6073 ][..],
6074 "-ERR invalid expire time, must be >= 0",
6075 ),
6076 (
6077 &[
6078 b"HGETEX".as_slice(),
6079 b"h",
6080 b"PXAT",
6081 b"99999999999999",
6082 b"FIELDS",
6083 b"1",
6084 b"a",
6085 ][..],
6086 "-ERR invalid expire time in 'hgetex' command",
6087 ),
6088 (
6089 &[
6090 b"HSETEX".as_slice(),
6091 b"h",
6092 b"EX",
6093 b"abc",
6094 b"FIELDS",
6095 b"1",
6096 b"a",
6097 b"1",
6098 ][..],
6099 "-ERR value is not an integer or out of range",
6100 ),
6101 ] {
6102 let reply = f.run(bad);
6103 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
6104 assert!(!reply.contains('*'), "an array header went out in front");
6105 }
6106 assert_eq!(
6107 f.run(&[b"HGET", b"h", b"a"]),
6108 "$1\r\n1\r\n",
6109 "and not one of them wrote anything"
6110 );
6111 assert_eq!(
6112 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
6113 "*1\r\n:-1\r\n"
6114 );
6115 }
6116
6117 #[test]
6118 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
6119 let mut f = Fixture::new();
6120 f.run(&[b"SET", b"str", b"v"]);
6121 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6122 for cmd in [
6123 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6124 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6125 &[
6126 b"HGETEX".as_slice(),
6127 b"str",
6128 b"EX",
6129 b"100",
6130 b"FIELDS",
6131 b"1",
6132 b"f",
6133 ][..],
6134 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
6135 ] {
6136 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
6137 }
6138 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
6139 }
6140
6141 #[test]
6149 fn himport_writes_declared_values_into_sorted_fields() {
6150 let mut f = Fixture::new();
6151 assert_eq!(
6152 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
6153 "+OK\r\n"
6154 );
6155 assert_eq!(
6156 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
6157 "+OK\r\n"
6158 );
6159 assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
6160 assert_eq!(
6161 f.run(&[b"HGETALL", b"k"]),
6162 bulks(&["a", "3", "b", "1", "aa", "2"])
6163 );
6164 }
6165
6166 #[test]
6169 fn himport_set_replaces_the_whole_key() {
6170 let mut f = Fixture::new();
6171 f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
6172 f.run(&[b"EXPIRE", b"k", b"100"]);
6173 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6174 assert_eq!(
6175 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6176 "+OK\r\n"
6177 );
6178 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6179 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
6180 }
6181
6182 #[test]
6185 fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
6186 let mut f = Fixture::new();
6187 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
6188 f.run(&[b"SELECT", b"1"]);
6189 assert_eq!(
6190 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6191 "+OK\r\n"
6192 );
6193 f.run(&[b"SELECT", b"0"]);
6194 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
6195 assert_eq!(
6196 f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
6197 "-ERR no such fieldset\r\n"
6198 );
6199 }
6200
6201 #[test]
6208 fn himport_complains_in_the_order_a_real_server_does() {
6209 let mut f = Fixture::new();
6210 f.run(&[b"SET", b"str", b"v"]);
6211 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6212 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6213 assert_eq!(
6214 f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
6215 wrong,
6216 "the type beats a missing fieldset"
6217 );
6218 assert_eq!(
6219 f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
6220 wrong,
6221 "and it beats a value count that does not fit"
6222 );
6223 assert_eq!(
6224 f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
6225 "-ERR no such fieldset\r\n"
6226 );
6227 for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
6229 let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
6230 line.extend_from_slice(values);
6231 assert_eq!(
6232 f.run(&line),
6233 "-ERR value count does not match fieldset field count\r\n",
6234 "{} values into two fields",
6235 values.len()
6236 );
6237 }
6238 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6239 }
6240
6241 #[test]
6243 fn himport_checks_each_subcommand_count_under_its_own_name() {
6244 let mut f = Fixture::new();
6245 assert_eq!(
6246 f.run(&[b"HIMPORT"]),
6247 "-ERR wrong number of arguments for 'himport' command\r\n"
6248 );
6249 for (rest, name) in [
6250 (&["PREPARE"][..], "prepare"),
6251 (&["PREPARE", "fs"][..], "prepare"),
6252 (&["SET"][..], "set"),
6253 (&["SET", "k"][..], "set"),
6254 (&["SET", "k", "fs"][..], "set"),
6255 (&["DISCARD"][..], "discard"),
6256 (&["DISCARD", "a", "b"][..], "discard"),
6257 (&["DISCARDALL", "x"][..], "discardall"),
6258 ] {
6259 let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
6260 line.extend(rest.iter().map(|a| a.as_bytes()));
6261 assert_eq!(
6262 f.run(&line),
6263 format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
6264 "HIMPORT {}",
6265 rest.join(" ")
6266 );
6267 }
6268 assert_eq!(
6269 f.run(&[b"HIMPORT", b"NOPE", b"x"]),
6270 "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
6271 );
6272 }
6273
6274 #[test]
6277 fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
6278 let mut f = Fixture::new();
6279 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6280 assert_eq!(
6281 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
6282 "-ERR duplicate field name in fieldset\r\n"
6283 );
6284 assert_eq!(
6285 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6286 "+OK\r\n"
6287 );
6288 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6289 }
6290
6291 #[test]
6294 fn himport_prepare_replaces_and_the_discards_count() {
6295 let mut f = Fixture::new();
6296 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6297 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
6298 assert_eq!(
6299 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6300 "+OK\r\n"
6301 );
6302 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
6303
6304 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
6305 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
6306 f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
6307 f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
6308 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
6309 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
6310 }
6311
6312 fn int(reply: &str) -> i64 {
6318 let body = reply
6319 .strip_prefix(':')
6320 .and_then(|s| s.strip_suffix("\r\n"))
6321 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
6322 body.parse().expect("an integer")
6323 }
6324
6325 fn int_reply(reply: &str) -> i64 {
6326 let body = reply
6327 .strip_prefix("*1\r\n:")
6328 .and_then(|s| s.strip_suffix("\r\n"))
6329 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
6330 body.parse().expect("an integer")
6331 }
6332
6333 fn scan_reply(reply: &str) -> (String, Vec<String>) {
6335 let mut lines = reply.split("\r\n");
6336 assert_eq!(lines.next(), Some("*2"), "got {reply}");
6337 lines.next().expect("the cursor header");
6338 let cursor = lines.next().expect("a cursor").to_owned();
6339 let header = lines.next().expect("an item count");
6340 let n: usize = header[1..].parse().expect("a count");
6341 let mut items = Vec::with_capacity(n);
6342 for _ in 0..n {
6343 lines.next().expect("an item header");
6344 items.push(lines.next().expect("an item").to_owned());
6345 }
6346 (cursor, items)
6347 }
6348
6349 fn sorted(reply: &str) -> Vec<String> {
6352 let mut lines = reply.split("\r\n");
6353 let header = lines.next().expect("a header");
6354 assert!(
6355 header.starts_with('*') || header.starts_with('~'),
6356 "got {reply}"
6357 );
6358 let n: usize = header[1..].parse().expect("a member count");
6359 let mut got = Vec::with_capacity(n);
6360 for _ in 0..n {
6361 lines.next().expect("a member header");
6362 got.push(lines.next().expect("a member").to_owned());
6363 }
6364 got.sort();
6365 got
6366 }
6367
6368 #[test]
6369 fn the_algebra_answers_what_the_sets_share_and_do_not() {
6370 let mut f = Fixture::new();
6371 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6372 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6373 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
6374
6375 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
6376 assert_eq!(
6377 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
6378 ["1", "2", "3", "4", "5"]
6379 );
6380 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
6381 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
6382
6383 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
6386 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
6387 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
6388 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
6389 }
6390
6391 #[test]
6392 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
6393 let mut f = Fixture::new();
6394 f.run(&[b"SADD", b"a", b"x"]);
6395 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
6396 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
6397 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
6398
6399 f.run(&[b"HELLO", b"3"]);
6400 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
6401 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
6402 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
6403 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
6404 }
6405
6406 #[test]
6407 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
6408 let mut f = Fixture::new();
6409 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6410 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6411
6412 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
6413 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
6414 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
6415 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
6416 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
6417 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
6418
6419 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
6422 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6423 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
6424 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
6425
6426 f.run(&[b"SET", b"str", b"v"]);
6429 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
6430 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
6431 }
6432
6433 #[test]
6434 fn sintercard_counts_without_building_and_stops_at_a_limit() {
6435 let mut f = Fixture::new();
6436 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6437 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
6438
6439 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
6440 assert_eq!(
6441 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6442 ":2\r\n"
6443 );
6444 assert_eq!(
6445 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6446 ":3\r\n",
6447 "a limit of zero is no limit"
6448 );
6449 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
6450 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
6451
6452 assert_eq!(
6454 f.run(&[b"SINTERCARD", b"0", b"a"]),
6455 "-ERR numkeys should be greater than 0\r\n"
6456 );
6457 assert_eq!(
6458 f.run(&[b"SINTERCARD", b"abc", b"a"]),
6459 "-ERR numkeys should be greater than 0\r\n"
6460 );
6461 assert_eq!(
6462 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
6463 "-ERR Number of keys can't be greater than number of args\r\n"
6464 );
6465 assert_eq!(
6466 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
6467 "-ERR LIMIT can't be negative\r\n"
6468 );
6469 assert_eq!(
6470 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
6471 "-ERR syntax error\r\n"
6472 );
6473 f.run(&[b"SADD", b"LIMIT", b"2"]);
6475 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
6476 }
6477
6478 #[test]
6481 fn sunioncard_and_sdiffcard_count_without_building() {
6482 let mut f = Fixture::new();
6483 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6484 f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
6485
6486 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
6487 assert_eq!(
6488 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6489 ":2\r\n"
6490 );
6491 assert_eq!(
6492 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6493 ":6\r\n",
6494 "a limit of zero is no limit"
6495 );
6496 assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6497 assert_eq!(
6498 f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6499 ":4\r\n",
6500 "a missing key adds nothing to a union"
6501 );
6502
6503 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6504 assert_eq!(
6505 f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6506 ":1\r\n"
6507 );
6508 assert_eq!(
6509 f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6510 ":2\r\n",
6511 "a difference is not symmetric"
6512 );
6513 assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6514 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6515 assert_eq!(
6516 f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6517 ":0\r\n",
6518 "nothing taken away from nothing"
6519 );
6520
6521 for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6524 assert_eq!(
6525 f.run(&[name, b"0", b"a"]),
6526 "-ERR numkeys should be greater than 0\r\n"
6527 );
6528 assert_eq!(
6529 f.run(&[name, b"abc", b"a"]),
6530 "-ERR numkeys should be greater than 0\r\n"
6531 );
6532 assert_eq!(
6533 f.run(&[name, b"-1", b"a"]),
6534 "-ERR numkeys should be greater than 0\r\n"
6535 );
6536 assert_eq!(
6537 f.run(&[name, b"3", b"a", b"b"]),
6538 "-ERR Number of keys can't be greater than number of args\r\n"
6539 );
6540 assert_eq!(
6541 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6542 "-ERR LIMIT can't be negative\r\n"
6543 );
6544 assert_eq!(
6545 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6546 "-ERR LIMIT can't be negative\r\n",
6547 "a LIMIT that is not a number gets the negative message too"
6548 );
6549 assert_eq!(
6550 f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6551 "-ERR syntax error\r\n"
6552 );
6553 assert_eq!(
6554 f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6555 "-ERR syntax error\r\n"
6556 );
6557 assert_eq!(
6558 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6559 "-ERR syntax error\r\n"
6560 );
6561 }
6562
6563 f.run(&[b"SADD", b"LIMIT", b"2"]);
6565 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6566 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6567 }
6568
6569 #[test]
6570 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6571 let mut f = Fixture::new();
6572 f.run(&[b"SADD", b"a", b"1"]);
6573 f.run(&[b"SADD", b"d", b"old"]);
6574 f.run(&[b"SET", b"str", b"v"]);
6575
6576 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6577 for bad in [
6578 &[b"SINTER".as_slice(), b"a", b"str"][..],
6579 &[b"SUNION".as_slice(), b"str"][..],
6580 &[b"SDIFF".as_slice(), b"a", b"str"][..],
6581 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6582 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6583 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6584 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6585 ] {
6586 let reply = f.run(bad);
6587 assert_eq!(reply, wrong, "for {:?}", bad[0]);
6588 }
6589 assert_eq!(
6590 f.run(&[b"SMEMBERS", b"d"]),
6591 "*1\r\n$3\r\nold\r\n",
6592 "and the destination was left alone every time"
6593 );
6594 }
6595
6596 #[test]
6599 fn churning_sets_does_not_grow_the_server() {
6600 let mut f = Fixture::new();
6601 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6602 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6603 .chain(std::iter::once(&b"s"[..]))
6604 .chain(members.iter().map(Vec::as_slice))
6605 .collect();
6606
6607 f.run(&args);
6608 f.run(&[b"DEL", b"s"]);
6609 f.server.compact_step();
6610 let after_first = f.server.memory_bytes();
6611
6612 for _ in 0..200 {
6613 f.run(&args);
6614 f.run(&[b"DEL", b"s"]);
6615 f.server.compact_step();
6616 }
6617 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6618 assert!(
6619 f.server.memory_bytes() <= after_first * 2,
6620 "held {} after two hundred passes against {after_first} after one",
6621 f.server.memory_bytes()
6622 );
6623 }
6624
6625 #[test]
6633 fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6634 let mut f = Fixture::new();
6635 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6636 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6637 assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6638 assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6639 assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6640 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6641
6642 assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6644 assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6645 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6646
6647 f.run(&[b"SET", b"num", b"12345"]);
6648 assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6649 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6650 assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6651 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6652 assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6653 }
6654
6655 #[test]
6660 fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6661 let mut f = Fixture::new();
6662 f.run(&[b"SET", b"mykey", b"foobar"]);
6663 assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6664 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6665 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6666 assert_eq!(
6667 f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6668 ":6\r\n"
6669 );
6670 assert_eq!(
6671 f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6672 ":25\r\n"
6673 );
6674 assert_eq!(
6675 f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6676 ":17\r\n"
6677 );
6678 assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6679
6680 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6683
6684 assert_eq!(
6686 f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6687 "-ERR syntax error\r\n"
6688 );
6689 assert_eq!(
6690 f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6691 "-ERR syntax error\r\n"
6692 );
6693 }
6694
6695 #[test]
6701 fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6702 let mut f = Fixture::new();
6703 f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6704 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6705 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6706 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6707 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6708 assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6709
6710 f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6711 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6712 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6713 assert_eq!(
6714 f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6715 ":8\r\n"
6716 );
6717
6718 assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6721 assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6722 }
6723
6724 #[test]
6726 fn the_eight_combinations_write_what_a_real_server_writes() {
6727 let mut f = Fixture::new();
6728 f.run(&[b"SET", b"a", b"abc"]);
6729 f.run(&[b"SET", b"b", b"abd"]);
6730 let cases: &[(&[u8], &str)] = &[
6731 (b"AND", "ab`"),
6732 (b"OR", "abg"),
6733 (b"XOR", "\u{0}\u{0}\u{7}"),
6734 (b"DIFF", "\u{0}\u{0}\u{3}"),
6735 (b"DIFF1", "\u{0}\u{0}\u{4}"),
6736 (b"ANDOR", "ab`"),
6737 (b"ONE", "\u{0}\u{0}\u{7}"),
6738 ];
6739 for (op, want) in cases {
6740 assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6741 assert_eq!(
6742 f.run(&[b"GET", b"d"]),
6743 format!("$3\r\n{want}\r\n"),
6744 "{op:?}"
6745 );
6746 }
6747 assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6749 assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6750
6751 assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6754 assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6755
6756 f.run(&[b"SET", b"dest", b"x"]);
6759 assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6760 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6761 }
6762
6763 #[test]
6765 fn bitop_names_the_operation_in_its_own_complaints() {
6766 let mut f = Fixture::new();
6767 f.run(&[b"SET", b"a", b"abc"]);
6768 assert_eq!(
6769 f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6770 "-ERR syntax error\r\n"
6771 );
6772 assert_eq!(
6773 f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6774 "-ERR BITOP NOT must be called with a single source key.\r\n"
6775 );
6776 for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6777 assert_eq!(
6778 f.run(&[b"BITOP", op, b"d", b"a"]),
6779 format!(
6780 "-ERR BITOP {} must be called with at least two source keys.\r\n",
6781 String::from_utf8_lossy(op)
6782 )
6783 );
6784 }
6785 f.run(&[b"LPUSH", b"l", b"x"]);
6786 assert_eq!(
6787 f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6788 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6789 );
6790 }
6791
6792 #[test]
6794 fn bitfield_reads_and_writes_packed_fields() {
6795 let mut f = Fixture::new();
6796 assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6797 assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6798
6799 assert_eq!(
6800 f.run(&[
6801 b"BITFIELD",
6802 b"bf",
6803 b"INCRBY",
6804 b"u2",
6805 b"100",
6806 b"1",
6807 b"GET",
6808 b"u4",
6809 b"0"
6810 ]),
6811 "*2\r\n:1\r\n:0\r\n"
6812 );
6813 assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6816
6817 assert_eq!(
6819 f.run(&[
6820 b"BITFIELD",
6821 b"bf",
6822 b"SET",
6823 b"u8",
6824 b"#0",
6825 b"255",
6826 b"GET",
6827 b"u8",
6828 b"#0"
6829 ]),
6830 "*2\r\n:0\r\n:255\r\n"
6831 );
6832
6833 assert_eq!(
6834 f.run(&[
6835 b"BITFIELD",
6836 b"bf",
6837 b"OVERFLOW",
6838 b"SAT",
6839 b"INCRBY",
6840 b"i8",
6841 b"0",
6842 b"120",
6843 b"INCRBY",
6844 b"i8",
6845 b"0",
6846 b"120"
6847 ]),
6848 "*2\r\n:119\r\n:127\r\n"
6849 );
6850 assert_eq!(
6851 f.run(&[
6852 b"BITFIELD",
6853 b"bf2",
6854 b"OVERFLOW",
6855 b"FAIL",
6856 b"INCRBY",
6857 b"u2",
6858 b"0",
6859 b"5"
6860 ]),
6861 "*1\r\n$-1\r\n"
6862 );
6863 assert_eq!(
6864 f.run(&[
6865 b"BITFIELD",
6866 b"bf3",
6867 b"OVERFLOW",
6868 b"WRAP",
6869 b"INCRBY",
6870 b"u2",
6871 b"0",
6872 b"5"
6873 ]),
6874 "*1\r\n:1\r\n"
6875 );
6876 assert_eq!(
6877 f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6878 "*1\r\n:4611686018427387904\r\n"
6879 );
6880 }
6881
6882 #[test]
6888 fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6889 let mut f = Fixture::new();
6890 let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6891 assert_eq!(
6892 f.run(&[
6893 b"BITFIELD",
6894 b"bad",
6895 b"SET",
6896 b"u8",
6897 b"0",
6898 b"1",
6899 b"GET",
6900 b"u99",
6901 b"0"
6902 ]),
6903 bad_type
6904 );
6905 assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6906 assert_eq!(
6907 f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6908 bad_type
6909 );
6910 assert_eq!(
6911 f.run(&[b"BITFIELD", b"bad", b"GET"]),
6912 "-ERR syntax error\r\n"
6913 );
6914 assert_eq!(
6915 f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6916 "-ERR syntax error\r\n"
6917 );
6918 assert_eq!(
6919 f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6920 "-ERR syntax error\r\n"
6921 );
6922 assert_eq!(
6923 f.run(&[
6924 b"BITFIELD",
6925 b"bad",
6926 b"OVERFLOW",
6927 b"NOPE",
6928 b"GET",
6929 b"u8",
6930 b"0"
6931 ]),
6932 "-ERR Invalid OVERFLOW type specified\r\n"
6933 );
6934 assert_eq!(
6935 f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6936 "-ERR value is not an integer or out of range\r\n"
6937 );
6938 for at in [&b"#-1"[..], b"abc"] {
6939 assert_eq!(
6940 f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6941 "-ERR bit offset is not an integer or out of range\r\n"
6942 );
6943 }
6944 }
6945
6946 #[test]
6948 fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6949 let mut f = Fixture::new();
6950 f.run(&[b"SET", b"n", b"123"]);
6951 assert_eq!(
6952 f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6953 "*1\r\n:49\r\n"
6954 );
6955 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6957
6958 assert_eq!(
6960 f.run(&[
6961 b"BITFIELD_RO",
6962 b"n",
6963 b"OVERFLOW",
6964 b"SAT",
6965 b"GET",
6966 b"u8",
6967 b"0"
6968 ]),
6969 "*1\r\n:49\r\n"
6970 );
6971 for sub in [&b"SET"[..], b"INCRBY"] {
6972 assert_eq!(
6973 f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6974 "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6975 );
6976 }
6977
6978 assert_eq!(
6979 f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6980 "*1\r\n:0\r\n"
6981 );
6982 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6983 }
6984
6985 #[test]
6987 fn an_offset_off_the_end_of_the_world_is_refused() {
6988 let mut f = Fixture::new();
6989 let bad = "-ERR bit offset is not an integer or out of range\r\n";
6990 for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6991 assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6992 assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6993 }
6994 for arg in [&b"2"[..], b"-1"] {
6995 assert_eq!(
6996 f.run(&[b"BITPOS", b"k", arg]),
6997 "-ERR The bit argument must be 1 or 0.\r\n"
6998 );
6999 }
7000 assert_eq!(
7001 f.run(&[b"BITPOS", b"k", b"abc"]),
7002 "-ERR value is not an integer or out of range\r\n"
7003 );
7004 assert_eq!(
7005 f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
7006 "-ERR value is not an integer or out of range\r\n"
7007 );
7008 let bad_bit = "-ERR bit is not an integer or out of range\r\n";
7009 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
7010 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
7011 }
7012
7013 #[test]
7015 fn every_bitmap_command_says_wrongtype() {
7016 let mut f = Fixture::new();
7017 f.run(&[b"LPUSH", b"l", b"x"]);
7018 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7019 let cases: &[&[&[u8]]] = &[
7020 &[b"SETBIT", b"l", b"0", b"1"],
7021 &[b"GETBIT", b"l", b"0"],
7022 &[b"BITCOUNT", b"l"],
7023 &[b"BITPOS", b"l", b"1"],
7024 &[b"BITOP", b"AND", b"d", b"l"],
7025 &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
7026 &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
7027 ];
7028 for case in cases {
7029 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
7030 }
7031 }
7032
7033 #[test]
7036 fn a_sketch_is_added_to_and_counted() {
7037 let mut f = Fixture::new();
7038 assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
7040 assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
7041 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
7042 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
7043 assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
7046 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
7047
7048 assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
7049 assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
7050 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
7051 }
7052
7053 #[test]
7054 fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
7055 let mut f = Fixture::new();
7056 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7057 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";
7059 let mut reply = b"$27\r\n".to_vec();
7060 reply.extend_from_slice(want);
7061 reply.extend_from_slice(b"\r\n");
7062 assert_eq!(f.raw(&[b"GET", b"h"]), reply);
7063 }
7064
7065 #[test]
7066 fn counting_several_keys_counts_their_union() {
7067 let mut f = Fixture::new();
7068 f.run(&[b"PFADD", b"a", b"x", b"y"]);
7069 f.run(&[b"PFADD", b"b", b"y", b"z"]);
7070 assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
7071 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
7072 assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
7075 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
7076 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
7077 }
7078
7079 #[test]
7080 fn a_merge_keeps_what_the_destination_had() {
7081 let mut f = Fixture::new();
7082 f.run(&[b"PFADD", b"a", b"x", b"y"]);
7083 f.run(&[b"PFADD", b"b", b"z"]);
7084 assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
7085 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
7086 f.run(&[b"PFADD", b"c", b"w"]);
7088 assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
7089 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
7090 assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
7093 assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
7094 }
7095
7096 #[test]
7097 fn the_debug_forms_answer_four_different_shapes() {
7098 let mut f = Fixture::new();
7099 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7100 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
7101 assert_eq!(
7102 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7103 "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
7104 );
7105 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
7106 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
7107 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
7108 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
7109 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
7110 assert_eq!(
7112 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7113 "-ERR HLL encoding is not sparse\r\n"
7114 );
7115
7116 let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
7118 assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
7119 assert_eq!(reply.matches(":0\r\n").count(), 16381);
7120 assert_eq!(reply.matches(":1\r\n").count(), 2);
7121 assert_eq!(reply.matches(":2\r\n").count(), 1);
7122
7123 assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
7124 }
7125
7126 #[test]
7127 fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
7128 let mut f = Fixture::new();
7129 f.run(&[b"SET", b"plain", b"not a sketch"]);
7130 let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
7131 assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
7132 assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
7133 assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
7134 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
7135
7136 f.run(&[b"RPUSH", b"l", b"x"]);
7139 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7140 assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
7141 assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
7142 assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
7143 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
7144 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
7145 }
7146
7147 #[test]
7148 fn pfdebug_has_its_own_complaints() {
7149 let mut f = Fixture::new();
7150 f.run(&[b"PFADD", b"h", b"a"]);
7151 assert_eq!(
7154 f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
7155 "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
7156 );
7157 let gone = "-ERR The specified key does not exist\r\n";
7159 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
7160 assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
7161 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
7162 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
7163 assert_eq!(
7164 f.run(&[b"PFDEBUG"]),
7165 "-ERR wrong number of arguments for 'pfdebug' command\r\n"
7166 );
7167 assert_eq!(
7168 f.run(&[b"PFSELFTEST", b"x"]),
7169 "-ERR wrong number of arguments for 'pfselftest' command\r\n"
7170 );
7171 }
7172
7173 #[test]
7174 fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
7175 let mut f = Fixture::new();
7176 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7177 let reply = f.raw(&[b"GET", b"h"]);
7180 let short = reply[5..reply.len() - 3].to_vec();
7181 f.run(&[b"SET", b"h", &short]);
7182 assert_eq!(
7183 f.run(&[b"PFCOUNT", b"h"]),
7184 "-INVALIDOBJ Corrupted HLL object detected\r\n"
7185 );
7186 }
7187
7188 #[test]
7189 fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
7190 let mut f = Fixture::new();
7191 f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
7194 for i in 0..10_000u32 {
7195 let ele = format!("e{i}");
7196 f.run(&[b"PFADD", b"big", ele.as_bytes()]);
7197 }
7198 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
7199 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
7200
7201 for key in [&b"small"[..], b"big"] {
7202 let mut copy = key.to_vec();
7203 copy.push(b'2');
7204 let bytes = payload(&f.raw(&[b"DUMP", key]));
7205 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
7206 assert_eq!(f.raw(&[b"GET", ©]), f.raw(&[b"GET", key]));
7209 assert_eq!(
7210 f.run(&[b"PFDEBUG", b"ENCODING", ©]),
7211 f.run(&[b"PFDEBUG", b"ENCODING", key])
7212 );
7213 assert_eq!(f.run(&[b"PFCOUNT", ©]), f.run(&[b"PFCOUNT", key]));
7214 }
7215 assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
7216 assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
7217 }
7218
7219 fn bulk(s: &str) -> String {
7223 format!("${}\r\n{s}\r\n", s.len())
7224 }
7225
7226 fn bulks(parts: &[&str]) -> String {
7229 let mut s = format!("*{}\r\n", parts.len());
7230 for p in parts {
7231 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
7232 }
7233 s
7234 }
7235
7236 #[test]
7237 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
7238 let mut f = Fixture::new();
7239 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
7243 assert_eq!(
7244 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7245 bulks(&["c", "b", "a"])
7246 );
7247 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
7248 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
7249 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
7250 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
7251 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
7252 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
7253 }
7254
7255 #[test]
7256 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
7257 let mut f = Fixture::new();
7258 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
7259 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
7260 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7261 f.run(&[b"RPUSH", b"k", b"a"]);
7262 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
7263 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
7264 assert_eq!(
7265 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7266 bulks(&["z", "a", "y"])
7267 );
7268 }
7269
7270 #[test]
7273 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
7274 let mut f = Fixture::new();
7275 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
7276 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
7277 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
7278 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
7279 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7280 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
7283 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
7284 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
7286 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7287 }
7288
7289 #[test]
7290 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
7291 let mut f = Fixture::new();
7292 f.run(&[b"RPUSH", b"k", b"a"]);
7293 let range = "-ERR value is out of range, must be positive\r\n";
7294 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
7295 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
7296 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
7297 assert_eq!(
7300 f.run(&[b"LPOP", b"k", b"1", b"2"]),
7301 "-ERR wrong number of arguments for 'lpop' command\r\n"
7302 );
7303 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7304 }
7305
7306 #[test]
7307 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
7308 let mut f = Fixture::new();
7309 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7310 assert_eq!(
7311 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7312 bulks(&["a", "b", "c"])
7313 );
7314 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
7315 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
7316 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
7317 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
7318 assert_eq!(
7319 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
7320 bulks(&["a", "b", "c"])
7321 );
7322 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
7325 assert_eq!(
7326 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
7327 "-ERR value is not an integer or out of range\r\n"
7328 );
7329 }
7330
7331 #[test]
7332 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
7333 let mut f = Fixture::new();
7334 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7335 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
7336 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
7337 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
7338 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
7339 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
7340 assert_eq!(
7341 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7342 bulks(&["a", "b", "z"])
7343 );
7344 assert_eq!(
7347 f.run(&[b"LSET", b"k", b"99", b"z"]),
7348 "-ERR index out of range\r\n"
7349 );
7350 assert_eq!(
7351 f.run(&[b"LSET", b"nope", b"0", b"z"]),
7352 "-ERR no such key\r\n"
7353 );
7354 }
7355
7356 #[test]
7357 fn linsert_says_three_things_with_one_signed_number() {
7358 let mut f = Fixture::new();
7359 assert_eq!(
7362 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
7363 ":0\r\n"
7364 );
7365 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7366 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
7367 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
7368 assert_eq!(
7369 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7370 bulks(&["X", "a", "b", "Y"])
7371 );
7372 assert_eq!(
7373 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
7374 ":-1\r\n"
7375 );
7376 assert_eq!(
7377 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
7378 "-ERR syntax error\r\n"
7379 );
7380 }
7381
7382 #[test]
7383 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
7384 let mut f = Fixture::new();
7385 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
7386 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
7387 assert_eq!(
7388 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7389 bulks(&["b", "c", "a"])
7390 );
7391 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
7392 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7393 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
7394 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
7395 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7396 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
7397 }
7398
7399 #[test]
7400 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
7401 let mut f = Fixture::new();
7402 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
7403 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
7404 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7405 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
7408 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7409 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
7410 }
7411
7412 #[test]
7413 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
7414 let mut f = Fixture::new();
7415 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
7416 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
7417 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
7418 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
7419 assert_eq!(
7420 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
7421 "*2\r\n:0\r\n:3\r\n"
7422 );
7423 assert_eq!(
7424 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
7425 "*3\r\n:6\r\n:3\r\n:0\r\n"
7426 );
7427 assert_eq!(
7430 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
7431 "*1\r\n:0\r\n"
7432 );
7433 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
7436 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
7437 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
7438 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
7439 }
7440
7441 #[test]
7442 fn lpos_words_its_three_mistakes_the_way_redis_does() {
7443 let mut f = Fixture::new();
7444 f.run(&[b"RPUSH", b"p", b"a"]);
7445 assert_eq!(
7448 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
7449 "-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"
7450 );
7451 assert_eq!(
7452 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
7453 "-ERR COUNT can't be negative\r\n"
7454 );
7455 assert_eq!(
7456 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
7457 "-ERR MAXLEN can't be negative\r\n"
7458 );
7459 assert_eq!(
7460 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
7461 "-ERR syntax error\r\n"
7462 );
7463 assert_eq!(
7464 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
7465 "-ERR syntax error\r\n"
7466 );
7467 }
7468
7469 #[test]
7470 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
7471 let mut f = Fixture::new();
7472 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7473 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
7474 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7475 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
7476 assert_eq!(
7477 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
7478 "$1\r\na\r\n"
7479 );
7480 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
7481 f.run(&[b"DEL", b"r"]);
7484 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
7485 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
7486 assert_eq!(
7487 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
7488 bulks(&["3", "1", "2"])
7489 );
7490 assert_eq!(
7491 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7492 "$-1\r\n"
7493 );
7494 assert_eq!(
7495 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7496 "-ERR syntax error\r\n"
7497 );
7498 }
7499
7500 #[test]
7501 fn a_move_checks_the_destination_before_it_takes_anything() {
7502 let mut f = Fixture::new();
7503 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7504 f.run(&[b"SET", b"str", b"v"]);
7505 assert_eq!(
7506 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7507 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7508 );
7509 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7511 }
7512
7513 #[test]
7514 fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7515 for (from, to, order, want) in [
7519 ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7520 ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7521 ("LEFT", "LEFT", "OBO", ["b", "a"]),
7522 ("LEFT", "LEFT", "BULK", ["a", "b"]),
7523 ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7524 ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7525 ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7526 ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7527 ] {
7528 let mut f = Fixture::new();
7529 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7530 let how = format!("{from} {to} {order}");
7531 let reply = f.run(&[
7532 b"LMOVEM",
7533 b"s",
7534 b"d",
7535 from.as_bytes(),
7536 to.as_bytes(),
7537 b"COUNT",
7538 b"2",
7539 order.as_bytes(),
7540 ]);
7541 assert_eq!(reply, bulks(&want), "the reply for {how}");
7542 assert_eq!(
7543 f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7544 bulks(&want),
7545 "the destination for {how}"
7546 );
7547 }
7548 }
7549
7550 #[test]
7551 fn a_block_move_of_one_needs_no_count_at_all() {
7552 let mut f = Fixture::new();
7553 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7554 assert_eq!(
7555 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7556 bulks(&["a"])
7557 );
7558 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7559 assert_eq!(
7562 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7563 "-ERR syntax error\r\n"
7564 );
7565 assert_eq!(
7566 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7567 "-ERR syntax error\r\n"
7568 );
7569 }
7570
7571 #[test]
7572 fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7573 let mut f = Fixture::new();
7574 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7575 assert_eq!(
7579 f.run(&[
7580 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7581 ]),
7582 "*-1\r\n"
7583 );
7584 assert_eq!(
7585 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7586 bulks(&["a", "b", "c"])
7587 );
7588 assert_eq!(
7590 f.run(&[
7591 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7592 ]),
7593 bulks(&["a", "b", "c"])
7594 );
7595 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7596 assert_eq!(
7597 f.run(&[
7598 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7599 ]),
7600 "*-1\r\n"
7601 );
7602 }
7603
7604 #[test]
7605 fn a_block_move_onto_itself_rotates_by_the_count() {
7606 let mut f = Fixture::new();
7607 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7608 assert_eq!(
7609 f.run(&[
7610 b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7611 ]),
7612 bulks(&["a", "b"])
7613 );
7614 assert_eq!(
7615 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7616 bulks(&["c", "a", "b"])
7617 );
7618 }
7619
7620 #[test]
7621 fn a_block_move_reads_the_count_before_the_ordering_word() {
7622 let mut f = Fixture::new();
7623 f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7624 f.run(&[b"SET", b"str", b"v"]);
7625 let count = "-ERR count should be greater than 0\r\n";
7626 assert_eq!(
7627 f.run(&[
7628 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7629 ]),
7630 count
7631 );
7632 assert_eq!(
7633 f.run(&[
7634 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7635 ]),
7636 count
7637 );
7638 assert_eq!(
7639 f.run(&[
7640 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7641 ]),
7642 "-ERR syntax error\r\n"
7643 );
7644 assert_eq!(
7645 f.run(&[
7646 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7647 ]),
7648 "-ERR syntax error\r\n"
7649 );
7650 assert_eq!(
7653 f.run(&[
7654 b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7655 ]),
7656 count
7657 );
7658 assert_eq!(
7659 f.run(&[
7660 b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7661 ]),
7662 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7663 );
7664 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7665 }
7666
7667 #[test]
7668 fn lmpop_answers_from_the_first_key_that_has_anything() {
7669 let mut f = Fixture::new();
7670 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7671 assert_eq!(
7674 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7675 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7676 );
7677 assert_eq!(
7678 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7679 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7680 );
7681 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7682 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7685 }
7686
7687 #[test]
7688 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7689 let mut f = Fixture::new();
7690 f.run(&[b"RPUSH", b"k", b"a"]);
7691 assert_eq!(
7692 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7693 "-ERR numkeys should be greater than 0\r\n"
7694 );
7695 assert_eq!(
7696 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7697 "-ERR numkeys should be greater than 0\r\n"
7698 );
7699 assert_eq!(
7700 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7701 "-ERR count should be greater than 0\r\n"
7702 );
7703 assert_eq!(
7706 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7707 "-ERR syntax error\r\n"
7708 );
7709 assert_eq!(
7710 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7711 "-ERR syntax error\r\n"
7712 );
7713 assert_eq!(
7714 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7715 "-ERR syntax error\r\n"
7716 );
7717 assert_eq!(
7718 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7719 "-ERR syntax error\r\n"
7720 );
7721 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7722 }
7723
7724 #[test]
7725 fn every_list_command_says_wrongtype_and_writes_nothing() {
7726 let mut f = Fixture::new();
7727 f.run(&[b"SET", b"str", b"v"]);
7728 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7729 for cmd in [
7730 &[b"LPUSH".as_slice(), b"str", b"a"][..],
7731 &[b"RPUSH", b"str", b"a"],
7732 &[b"LPUSHX", b"str", b"a"],
7733 &[b"RPUSHX", b"str", b"a"],
7734 &[b"LPOP", b"str"],
7735 &[b"LPOP", b"str", b"2"],
7736 &[b"RPOP", b"str"],
7737 &[b"LLEN", b"str"],
7738 &[b"LRANGE", b"str", b"0", b"-1"],
7739 &[b"LINDEX", b"str", b"0"],
7740 &[b"LSET", b"str", b"0", b"a"],
7741 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7742 &[b"LREM", b"str", b"0", b"a"],
7743 &[b"LTRIM", b"str", b"0", b"-1"],
7744 &[b"LPOS", b"str", b"a"],
7745 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7746 &[b"RPOPLPUSH", b"str", b"d"],
7747 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7748 &[b"LMPOP", b"1", b"str", b"LEFT"],
7749 ] {
7750 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7751 }
7752 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7753 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7754 }
7755
7756 #[test]
7760 fn a_timeout_has_three_ways_of_being_wrong() {
7761 let mut f = Fixture::new();
7762 let not_float = "-ERR timeout is not a float or out of range\r\n";
7763 let range = "-ERR timeout is out of range\r\n";
7764 for (bad, want) in [
7765 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7766 (&[b"BLPOP", b"k", b"nan"], not_float),
7767 (&[b"BLPOP", b"k", b""], not_float),
7768 (&[b"BLPOP", b"k", b" 1"], not_float),
7771 (&[b"BLPOP", b"k", b"1 "], not_float),
7772 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7773 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7774 (&[b"BLPOP", b"k", b"1e400"], range),
7777 (&[b"BLPOP", b"k", b"inf"], range),
7778 (&[b"BLPOP", b"k", b"9999999999999999"], range),
7779 (&[b"BRPOP", b"k", b"abc"], not_float),
7780 (
7781 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7782 not_float,
7783 ),
7784 (
7785 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7786 "-ERR timeout is negative\r\n",
7787 ),
7788 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7789 ] {
7790 assert_eq!(f.run(bad), want, "for {bad:?}");
7791 }
7792 }
7793
7794 #[test]
7797 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7798 let mut f = Fixture::new();
7799 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7800 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7801 assert_eq!(flow, Flow::Block, "for {timeout:?}");
7802 assert!(out.is_empty(), "for {timeout:?}");
7803 }
7804 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7808 assert_eq!(flow, Flow::Block);
7809 assert!(out.is_empty());
7810 }
7811
7812 #[test]
7813 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7814 let mut f = Fixture::new();
7815 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7816
7817 assert_eq!(
7820 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7821 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7822 );
7823 assert_eq!(
7824 f.run(&[b"BRPOP", b"L", b"0"]),
7825 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7826 );
7827 assert_eq!(
7828 f.run(&[
7829 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7830 ]),
7831 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7832 );
7833 assert_eq!(
7834 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7835 "$1\r\nd\r\n"
7836 );
7837 assert_eq!(
7838 f.run(&[b"EXISTS", b"L"]),
7839 ":0\r\n",
7840 "and the key went with it"
7841 );
7842 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7843 f.run(&[b"RPUSH", b"D", b"x"]);
7846 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7847 assert_eq!(
7848 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7849 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7850 );
7851 }
7852
7853 #[test]
7854 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7855 let mut f = Fixture::new();
7856 f.run(&[b"RPUSH", b"k", b"a"]);
7857 for (bad, want) in [
7858 (
7859 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7860 "-ERR numkeys should be greater than 0\r\n",
7861 ),
7862 (
7863 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7864 "-ERR numkeys should be greater than 0\r\n",
7865 ),
7866 (
7869 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7870 "-ERR syntax error\r\n",
7871 ),
7872 (
7873 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7874 "-ERR syntax error\r\n",
7875 ),
7876 (
7877 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7878 "-ERR syntax error\r\n",
7879 ),
7880 (
7881 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7882 "-ERR syntax error\r\n",
7883 ),
7884 (
7887 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7888 "-ERR count should be greater than 0\r\n",
7889 ),
7890 (
7891 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7892 "-ERR count should be greater than 0\r\n",
7893 ),
7894 ] {
7895 assert_eq!(f.run(bad), want, "for {bad:?}");
7896 }
7897 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7898 }
7899
7900 #[test]
7901 fn a_blocking_move_reads_its_directions_before_its_timeout() {
7902 let mut f = Fixture::new();
7903 assert_eq!(
7906 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7907 "-ERR syntax error\r\n"
7908 );
7909 assert_eq!(
7910 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7911 "-ERR syntax error\r\n"
7912 );
7913 }
7914
7915 #[test]
7919 fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7920 let mut f = Fixture::new();
7921 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7922 assert_eq!(
7923 f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7924 (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7925 );
7926 assert_eq!(
7927 f.run(&[
7928 b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7929 ]),
7930 bulks(&["e", "d"])
7931 );
7932 assert_eq!(
7933 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7934 bulks(&["a", "e", "d"])
7935 );
7936 assert_eq!(
7938 f.run(&[
7939 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7940 ]),
7941 bulks(&["b", "c"])
7942 );
7943 assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7944 }
7945
7946 #[test]
7950 fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7951 let mut f = Fixture::new();
7952 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7953 assert_eq!(
7955 f.flow(&[
7956 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7957 ]),
7958 (Flow::Continue, bulks(&["a", "b"]))
7959 );
7960
7961 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7962 assert_eq!(
7965 f.flow(&[
7966 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7967 ])
7968 .0,
7969 Flow::Block
7970 );
7971 assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7972 }
7973
7974 #[test]
7975 fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7976 let mut f = Fixture::new();
7977 let syntax = "-ERR syntax error\r\n";
7978 assert_eq!(
7980 f.run(&[
7981 b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7982 ]),
7983 syntax
7984 );
7985 assert_eq!(
7987 f.run(&[
7988 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7989 ]),
7990 "-ERR timeout is not a float or out of range\r\n"
7991 );
7992 assert_eq!(
7993 f.run(&[
7994 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7995 ]),
7996 "-ERR timeout is negative\r\n"
7997 );
7998 assert_eq!(
8000 f.run(&[
8001 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
8002 ]),
8003 "-ERR count should be greater than 0\r\n"
8004 );
8005 assert_eq!(
8006 f.run(&[
8007 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
8008 ]),
8009 syntax
8010 );
8011 assert_eq!(
8014 f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
8015 syntax
8016 );
8017 assert_eq!(
8018 f.run(&[
8019 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
8020 ]),
8021 syntax
8022 );
8023 }
8024
8025 #[test]
8028 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
8029 let mut f = Fixture::new();
8030 f.run(&[b"SET", b"S", b"v"]);
8031 f.run(&[b"RPUSH", b"D", b"x"]);
8032 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8033
8034 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
8035 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
8038 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
8039 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
8040 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
8041 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
8044 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
8045 assert_eq!(
8046 f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
8047 wrong
8048 );
8049 assert_eq!(
8050 f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
8051 wrong
8052 );
8053
8054 assert_eq!(
8058 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
8059 .0,
8060 Flow::Block
8061 );
8062 assert_eq!(
8066 f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
8067 .0,
8068 Flow::Block
8069 );
8070 f.run(&[b"RPUSH", b"E", b"1", b"2"]);
8071 assert_eq!(
8072 f.flow(&[
8073 b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
8074 ])
8075 .0,
8076 Flow::Block
8077 );
8078 }
8079
8080 #[test]
8084 fn churning_lists_does_not_grow_the_server() {
8085 let mut f = Fixture::new();
8086 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
8087 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
8088 .into_iter()
8089 .chain(vals.iter().map(Vec::as_slice))
8090 .collect();
8091
8092 f.run(&args);
8093 f.run(&[b"DEL", b"k"]);
8094 f.server.compact_step();
8095 let after_first = f.server.memory_bytes();
8096
8097 for _ in 0..200 {
8098 f.run(&args);
8099 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
8100 f.server.compact_step();
8101 }
8102 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8103 assert!(
8104 f.server.memory_bytes() <= after_first * 2,
8105 "held {} after two hundred passes against {after_first} after one",
8106 f.server.memory_bytes()
8107 );
8108 }
8109
8110 #[test]
8113 fn a_sorted_set_takes_scores_and_gives_them_back() {
8114 let mut f = Fixture::new();
8115 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
8116 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
8117 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
8118 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
8119 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
8120 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
8121 assert_eq!(
8122 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
8123 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
8124 );
8125 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
8126 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
8127 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
8129 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8130 }
8131
8132 #[test]
8133 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
8134 let mut f = Fixture::new();
8135 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
8136 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
8137 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
8138 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
8139
8140 f.out = Out::new(Proto::Resp3);
8141 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
8142 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
8143 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
8144 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
8145 }
8146
8147 #[test]
8148 fn the_zadd_options_gate_what_gets_written() {
8149 let mut f = Fixture::new();
8150 f.run(&[b"ZADD", b"z", b"5", b"a"]);
8151 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
8153 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
8154 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
8155 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
8156 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
8158 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
8159 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
8160 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
8162 assert_eq!(
8163 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
8164 ":2\r\n"
8165 );
8166 }
8167
8168 #[test]
8169 fn zadd_incr_answers_a_score_or_nothing_at_all() {
8170 let mut f = Fixture::new();
8171 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
8172 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
8173 assert_eq!(
8176 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
8177 "$-1\r\n"
8178 );
8179 assert_eq!(
8180 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
8181 "$-1\r\n"
8182 );
8183 assert_eq!(
8184 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
8185 "$-1\r\n"
8186 );
8187 assert_eq!(
8188 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
8189 "$1\r\n8\r\n"
8190 );
8191 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
8192 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
8193 }
8194
8195 #[test]
8196 fn the_two_infinities_will_not_be_added_together() {
8197 let mut f = Fixture::new();
8198 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
8199 let nan = "-ERR resulting score is not a number (NaN)\r\n";
8200 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
8201 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
8202 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
8203 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
8205 }
8206
8207 #[test]
8208 fn zadd_says_its_mistakes_the_way_redis_says_them() {
8209 let mut f = Fixture::new();
8210 assert_eq!(
8213 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
8214 "-ERR syntax error\r\n"
8215 );
8216 assert_eq!(
8217 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
8218 "-ERR XX and NX options at the same time are not compatible\r\n"
8219 );
8220 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
8221 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
8222 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
8223 assert_eq!(
8224 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
8225 "-ERR INCR option supports a single increment-element pair\r\n"
8226 );
8227 assert_eq!(
8229 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
8230 "-ERR syntax error\r\n"
8231 );
8232 assert_eq!(
8234 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
8235 "-ERR value is not a valid float\r\n"
8236 );
8237 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8238 }
8239
8240 #[test]
8241 fn a_rank_says_where_a_member_sits_from_either_end() {
8242 let mut f = Fixture::new();
8243 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8244 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
8245 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
8246 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
8247 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
8248 assert_eq!(
8250 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
8251 "*2\r\n:1\r\n$1\r\n2\r\n"
8252 );
8253 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
8254 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
8255 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
8256 assert_eq!(
8259 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
8260 "-ERR syntax error\r\n"
8261 );
8262 assert_eq!(
8263 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
8264 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
8265 );
8266 }
8267
8268 #[test]
8269 fn the_two_counts_read_their_two_kinds_of_bound() {
8270 let mut f = Fixture::new();
8271 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8272 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
8273 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
8274 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
8275 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
8276 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
8277 assert_eq!(
8278 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
8279 "-ERR min or max is not a float\r\n"
8280 );
8281
8282 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
8283 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
8284 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
8285 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
8286 assert_eq!(
8289 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
8290 "-ERR min or max not valid string range item\r\n"
8291 );
8292 }
8293
8294 #[test]
8300 fn one_range_command_selects_by_rank_or_score_or_name() {
8301 let mut f = Fixture::new();
8302 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8303 assert_eq!(
8304 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8305 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8306 );
8307 assert_eq!(
8308 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
8309 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8310 );
8311 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
8312 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
8313 assert_eq!(
8316 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
8317 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8318 );
8319 assert_eq!(
8320 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
8321 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8322 );
8323 assert_eq!(
8326 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
8327 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8328 );
8329 assert_eq!(
8330 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
8331 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8332 );
8333 assert_eq!(
8334 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
8335 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8336 );
8337 }
8338
8339 #[test]
8342 fn the_older_range_spellings_name_their_high_end_first() {
8343 let mut f = Fixture::new();
8344 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8345 assert_eq!(
8346 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
8347 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8348 );
8349 assert_eq!(
8350 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
8351 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8352 );
8353 assert_eq!(
8354 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
8355 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8356 );
8357 assert_eq!(
8358 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
8359 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8360 );
8361 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
8365 assert_eq!(
8366 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
8367 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
8368 );
8369 assert_eq!(
8370 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
8371 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
8372 );
8373 for cmd in [
8376 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
8377 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
8378 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
8379 ] {
8380 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
8381 }
8382 }
8383
8384 #[test]
8387 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
8388 let mut f = Fixture::new();
8389 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8390 assert_eq!(
8391 f.run(&[
8392 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
8393 ]),
8394 "*1\r\n$1\r\nb\r\n"
8395 );
8396 assert_eq!(
8398 f.run(&[
8399 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
8400 ]),
8401 "*0\r\n"
8402 );
8403 assert_eq!(
8404 f.run(&[
8405 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
8406 ]),
8407 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8408 );
8409 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";
8411 assert_eq!(
8412 f.run(&[
8413 b"ZRANGEBYSCORE",
8414 b"z",
8415 b"1",
8416 b"3",
8417 b"WITHSCORES",
8418 b"LIMIT",
8419 b"0",
8420 b"2"
8421 ]),
8422 both
8423 );
8424 assert_eq!(
8425 f.run(&[
8426 b"ZRANGEBYSCORE",
8427 b"z",
8428 b"1",
8429 b"3",
8430 b"LIMIT",
8431 b"0",
8432 b"2",
8433 b"WITHSCORES"
8434 ]),
8435 both
8436 );
8437 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
8440 assert_eq!(
8441 f.run(&[
8442 b"ZREVRANGE",
8443 b"z",
8444 b"0",
8445 b"-1",
8446 b"WITHSCORES",
8447 b"LIMIT",
8448 b"0",
8449 b"1"
8450 ]),
8451 needs_by
8452 );
8453 assert_eq!(
8454 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
8455 needs_by
8456 );
8457 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
8458 assert_eq!(
8459 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
8460 not_bylex
8461 );
8462 assert_eq!(
8463 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
8464 not_bylex
8465 );
8466 for cmd in [
8469 &[
8470 b"ZRANGE".as_slice(),
8471 b"z",
8472 b"0",
8473 b"-1",
8474 b"BYSCORE",
8475 b"BYLEX",
8476 ][..],
8477 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
8478 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
8479 ] {
8480 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8481 }
8482 assert_eq!(
8483 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
8484 "-ERR min or max is not a float\r\n"
8485 );
8486 assert_eq!(
8487 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
8488 "-ERR min or max not valid string range item\r\n"
8489 );
8490 assert_eq!(
8491 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8492 "-ERR value is not an integer or out of range\r\n"
8493 );
8494 }
8495
8496 #[test]
8499 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8500 let mut f = Fixture::new();
8501 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8502 assert_eq!(
8503 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8504 "*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"
8505 );
8506 f.out = Out::new(Proto::Resp3);
8507 assert_eq!(
8508 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8509 "*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"
8510 );
8511 assert_eq!(
8512 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8513 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8514 );
8515 }
8516
8517 #[test]
8519 fn a_range_store_writes_the_window_into_another_key() {
8520 let mut f = Fixture::new();
8521 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8522 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8523 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8526 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8527 assert_eq!(
8528 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8529 ":2\r\n"
8530 );
8531 assert_eq!(
8532 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8533 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8534 );
8535 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8538 assert_eq!(
8539 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8540 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8541 );
8542 assert_eq!(
8545 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8546 "-ERR syntax error\r\n"
8547 );
8548 }
8549
8550 #[test]
8553 fn the_three_removals_share_their_window_with_the_reads() {
8554 let mut f = Fixture::new();
8555 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8556 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8557 assert_eq!(
8558 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8559 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8560 );
8561 assert_eq!(
8562 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8563 ":1\r\n"
8564 );
8565 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8566 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8568 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8569 assert_eq!(
8570 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8571 ":0\r\n"
8572 );
8573 assert_eq!(
8574 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8575 "-ERR value is not an integer or out of range\r\n"
8576 );
8577 }
8578
8579 #[test]
8581 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8582 let mut f = Fixture::new();
8583 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8584 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8585 assert_eq!(
8586 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8587 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8588 );
8589 assert_eq!(
8592 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8593 "*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"
8594 );
8595 assert_eq!(
8596 f.run(&[
8597 b"ZUNION",
8598 b"2",
8599 b"z",
8600 b"y",
8601 b"WEIGHTS",
8602 b"2",
8603 b"3",
8604 b"WITHSCORES"
8605 ]),
8606 "*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"
8607 );
8608 assert_eq!(
8609 f.run(&[
8610 b"ZUNION",
8611 b"2",
8612 b"z",
8613 b"y",
8614 b"AGGREGATE",
8615 b"MIN",
8616 b"WITHSCORES"
8617 ]),
8618 "*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"
8619 );
8620 assert_eq!(
8621 f.run(&[
8622 b"ZUNION",
8623 b"2",
8624 b"z",
8625 b"y",
8626 b"AGGREGATE",
8627 b"MAX",
8628 b"WITHSCORES"
8629 ]),
8630 "*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"
8631 );
8632 assert_eq!(
8633 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8634 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8635 );
8636 assert_eq!(
8637 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8638 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8639 );
8640 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8641 f.run(&[b"SADD", b"p", b"a", b"d"]);
8644 assert_eq!(
8645 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8646 "*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"
8647 );
8648 for cmd in [
8651 &[
8652 b"ZDIFF".as_slice(),
8653 b"2",
8654 b"z",
8655 b"y",
8656 b"WEIGHTS",
8657 b"1",
8658 b"1",
8659 ][..],
8660 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8661 ] {
8662 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8663 }
8664 }
8665
8666 #[test]
8668 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8669 let mut f = Fixture::new();
8670 f.run(&[b"ZADD", b"z", b"1", b"a"]);
8671 f.run(&[b"ZADD", b"y", b"2", b"b"]);
8672 assert_eq!(
8674 f.run(&[b"ZUNION", b"0", b"z"]),
8675 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8676 );
8677 assert_eq!(
8678 f.run(&[b"ZUNION", b"-1", b"z"]),
8679 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8680 );
8681 assert_eq!(
8682 f.run(&[b"ZINTERCARD", b"0", b"z"]),
8683 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8684 );
8685 assert_eq!(
8688 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8689 "-ERR syntax error\r\n"
8690 );
8691 assert_eq!(
8692 f.run(&[b"ZUNION", b"x", b"z"]),
8693 "-ERR value is not an integer or out of range\r\n"
8694 );
8695 assert_eq!(
8698 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8699 "-ERR syntax error\r\n"
8700 );
8701 assert_eq!(
8702 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8703 "-ERR weight value is not a float\r\n"
8704 );
8705 assert_eq!(
8706 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8707 "-ERR syntax error\r\n"
8708 );
8709 }
8710
8711 #[test]
8713 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8714 let mut f = Fixture::new();
8715 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8716 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8717 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8718 assert_eq!(
8719 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8720 "*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"
8721 );
8722 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8723 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8724 assert_eq!(
8727 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8728 ":0\r\n"
8729 );
8730 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8731 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8733 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8734 for cmd in [
8735 &[
8736 b"ZUNIONSTORE".as_slice(),
8737 b"d",
8738 b"2",
8739 b"z",
8740 b"y",
8741 b"WITHSCORES",
8742 ][..],
8743 &[
8744 b"ZDIFFSTORE",
8745 b"d",
8746 b"2",
8747 b"z",
8748 b"y",
8749 b"WEIGHTS",
8750 b"1",
8751 b"1",
8752 ],
8753 ] {
8754 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8755 }
8756 }
8757
8758 #[test]
8760 fn intercard_counts_and_stops_at_its_limit() {
8761 let mut f = Fixture::new();
8762 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8763 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8764 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8765 assert_eq!(
8767 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8768 ":2\r\n"
8769 );
8770 assert_eq!(
8771 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8772 ":1\r\n"
8773 );
8774 let bad = "-ERR LIMIT can't be negative\r\n";
8777 assert_eq!(
8778 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8779 bad
8780 );
8781 assert_eq!(
8782 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8783 bad
8784 );
8785 for cmd in [
8786 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8787 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8788 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8789 ] {
8790 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8791 }
8792 }
8793
8794 #[test]
8796 fn a_draw_answers_one_member_or_an_array_of_them() {
8797 let mut f = Fixture::new();
8798 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8799 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8802 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8803 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8804 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8805 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8808 assert!(all.starts_with("*3\r\n"), "{all}");
8809 for m in ["a", "b", "c"] {
8810 assert!(all.contains(m), "{all}");
8811 }
8812 assert!(
8815 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8816 "five draws with replacement"
8817 );
8818 assert!(
8819 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8820 .starts_with("*4\r\n"),
8821 "two pairs, flat on RESP2"
8822 );
8823 f.out = Out::new(Proto::Resp3);
8824 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8825 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8826 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8827 f.out = Out::new(Proto::Resp2);
8828 assert_eq!(
8829 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8830 "-ERR syntax error\r\n"
8831 );
8832 assert_eq!(
8833 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8834 "-ERR value is not an integer or out of range\r\n"
8835 );
8836 }
8837
8838 #[test]
8840 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8841 let mut f = Fixture::new();
8842 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8843 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";
8844 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8845 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8846 assert_eq!(
8847 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8848 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8849 );
8850 assert_eq!(
8851 f.run(&[b"ZSCAN", b"nokey", b"0"]),
8852 "*2\r\n$1\r\n0\r\n*0\r\n"
8853 );
8854 f.out = Out::new(Proto::Resp3);
8857 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8858 f.out = Out::new(Proto::Resp2);
8859 assert_eq!(
8860 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8861 "-ERR NOVALUES option can only be used in HSCAN\r\n"
8862 );
8863 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8864 assert_eq!(
8865 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8866 "-ERR syntax error\r\n"
8867 );
8868 }
8869
8870 #[test]
8872 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8873 let mut f = Fixture::new();
8874 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8875 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8877 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8878 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8879 assert_eq!(
8881 f.run(&[b"ZPOPMIN", b"z", b"2"]),
8882 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8883 );
8884 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8887 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8888 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8889 assert_eq!(
8891 f.run(&[b"ZPOPMIN", b"z", b"9"]),
8892 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8893 );
8894 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8895
8896 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8897 f.out = Out::new(Proto::Resp3);
8898 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8899 assert_eq!(
8900 f.run(&[b"ZPOPMIN", b"z", b"1"]),
8901 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8902 );
8903 f.out = Out::new(Proto::Resp2);
8904 let bad = "-ERR value is out of range, must be positive\r\n";
8907 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8908 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8909 assert_eq!(
8910 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8911 "-ERR syntax error\r\n"
8912 );
8913 }
8914
8915 #[test]
8917 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8918 let mut f = Fixture::new();
8919 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8920 assert_eq!(
8921 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8922 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8923 );
8924 assert_eq!(
8927 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8928 "*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"
8929 );
8930 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8932 f.out = Out::new(Proto::Resp3);
8933 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8934 f.out = Out::new(Proto::Resp2);
8935 let numkeys = "-ERR numkeys should be greater than 0\r\n";
8936 for bad in [
8937 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8938 &[b"ZMPOP", b"-1", b"z", b"MIN"],
8939 &[b"ZMPOP", b"x", b"z", b"MIN"],
8940 ] {
8941 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8942 }
8943 let count = "-ERR count should be greater than 0\r\n";
8944 for bad in [
8945 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8946 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8947 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8948 ] {
8949 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8950 }
8951 let syntax = "-ERR syntax error\r\n";
8952 for bad in [
8953 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8956 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8957 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8958 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8959 ] {
8960 assert_eq!(f.run(bad), syntax, "{bad:?}");
8961 }
8962 }
8963
8964 #[test]
8967 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8968 let mut f = Fixture::new();
8969 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8970 assert_eq!(
8971 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8972 (
8973 Flow::Continue,
8974 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8975 )
8976 );
8977 assert_eq!(
8978 f.run(&[b"BZPOPMAX", b"z", b"0"]),
8979 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8980 );
8981 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8982 assert_eq!(
8983 f.run(&[
8984 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8985 ]),
8986 "*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"
8987 );
8988 f.out = Out::new(Proto::Resp3);
8989 assert_eq!(
8990 f.run(&[b"BZPOPMIN", b"z", b"0"]),
8991 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8992 );
8993 f.out = Out::new(Proto::Resp2);
8994 assert_eq!(
8996 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8997 (Flow::Block, String::new())
8998 );
8999 assert_eq!(
9000 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
9001 (Flow::Block, String::new())
9002 );
9003 assert_eq!(
9006 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
9007 "-ERR timeout is not a float or out of range\r\n"
9008 );
9009 assert_eq!(
9010 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
9011 "-ERR numkeys should be greater than 0\r\n"
9012 );
9013 assert_eq!(
9014 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
9015 "-ERR timeout is negative\r\n"
9016 );
9017 }
9018
9019 #[test]
9023 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
9024 let mut f = Fixture::new();
9025 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
9026 assert_eq!(f.server.parked(), 1);
9027 f.run(&[b"SET", b"z", b"v"]);
9030 let mut out = Out::new(Proto::Resp2);
9031 assert!(!f.server.serve_waiter(0, 0, &mut out));
9032 assert!(out.as_slice().is_empty());
9033 f.run(&[b"DEL", b"z"]);
9034 f.run(&[b"ZADD", b"z", b"5", b"m"]);
9035 assert!(f.server.serve_waiter(0, 0, &mut out));
9036 assert_eq!(
9037 core::str::from_utf8(out.as_slice()).expect("ascii"),
9038 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
9039 );
9040 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
9043 }
9044
9045 #[test]
9046 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
9047 let mut f = Fixture::new();
9048 f.run(&[b"SET", b"s", b"v"]);
9049 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9050 for cmd in [
9051 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
9052 &[b"ZINCRBY", b"s", b"1", b"a"],
9053 &[b"ZCARD", b"s"],
9054 &[b"ZSCORE", b"s", b"a"],
9055 &[b"ZMSCORE", b"s", b"a"],
9056 &[b"ZREM", b"s", b"a"],
9057 &[b"ZRANK", b"s", b"a"],
9058 &[b"ZREVRANK", b"s", b"a"],
9059 &[b"ZCOUNT", b"s", b"1", b"2"],
9060 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
9061 &[b"ZRANGE", b"s", b"0", b"-1"],
9062 &[b"ZREVRANGE", b"s", b"0", b"-1"],
9063 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
9064 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
9065 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
9066 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
9067 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
9068 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
9069 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
9070 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
9071 &[b"ZUNION", b"1", b"s"],
9072 &[b"ZINTER", b"1", b"s"],
9073 &[b"ZDIFF", b"1", b"s"],
9074 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
9075 &[b"ZINTERSTORE", b"d", b"1", b"s"],
9076 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
9077 &[b"ZINTERCARD", b"1", b"s"],
9078 &[b"ZRANDMEMBER", b"s"],
9079 &[b"ZSCAN", b"s", b"0"],
9080 &[b"ZPOPMIN", b"s"],
9081 &[b"ZPOPMAX", b"s", b"2"],
9082 &[b"ZMPOP", b"1", b"s", b"MIN"],
9083 &[b"BZPOPMIN", b"s", b"0"],
9084 &[b"BZPOPMAX", b"s", b"0"],
9085 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
9086 ] {
9087 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
9088 }
9089 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
9090 }
9091
9092 #[test]
9096 fn churning_sorted_sets_does_not_grow_the_server() {
9097 let mut f = Fixture::new();
9098 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
9099 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
9100 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
9101 for i in 0..200 {
9102 args.push(&scores[i]);
9103 args.push(&members[i]);
9104 }
9105
9106 f.run(&args);
9107 f.run(&[b"DEL", b"z"]);
9108 f.server.compact_step();
9109 let after_first = f.server.memory_bytes();
9110
9111 for _ in 0..200 {
9112 f.run(&args);
9113 f.run(&[b"DEL", b"z"]);
9114 f.server.compact_step();
9115 }
9116 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9117 assert!(
9118 f.server.memory_bytes() <= after_first * 2,
9119 "held {} after two hundred passes against {after_first} after one",
9120 f.server.memory_bytes()
9121 );
9122 }
9123
9124 fn sicily(f: &mut Fixture) {
9132 f.run(&[
9133 b"GEOADD",
9134 b"Sicily",
9135 b"13.361389",
9136 b"38.115556",
9137 b"Palermo",
9138 b"15.087269",
9139 b"37.502669",
9140 b"Catania",
9141 ]);
9142 f.run(&[
9143 b"GEOADD",
9144 b"Sicily",
9145 b"13.583333",
9146 b"37.316667",
9147 b"Agrigento",
9148 ]);
9149 }
9150
9151 #[test]
9152 fn places_go_in_as_scores_and_come_back_as_positions() {
9153 let mut f = Fixture::new();
9154 assert_eq!(
9155 f.run(&[
9156 b"GEOADD",
9157 b"Sicily",
9158 b"13.361389",
9159 b"38.115556",
9160 b"Palermo",
9161 b"15.087269",
9162 b"37.502669",
9163 b"Catania"
9164 ]),
9165 ":2\r\n"
9166 );
9167 assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
9171 assert_eq!(
9172 f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
9173 "$16\r\n3479099956230698\r\n"
9174 );
9175 assert_eq!(
9176 f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
9177 "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
9178 );
9179 assert_eq!(
9180 f.run(&[
9181 b"GEOHASH",
9182 b"Sicily",
9183 b"Palermo",
9184 b"Catania",
9185 b"NonExisting"
9186 ]),
9187 "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
9188 );
9189 assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
9193 assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
9194 }
9195
9196 #[test]
9197 fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
9198 let mut f = Fixture::new();
9199 sicily(&mut f);
9200 assert_eq!(
9201 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
9202 "$11\r\n166274.1516\r\n"
9203 );
9204 assert_eq!(
9205 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
9206 "$8\r\n166.2742\r\n"
9207 );
9208 assert_eq!(
9209 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
9210 "$8\r\n103.3182\r\n"
9211 );
9212 assert_eq!(
9216 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
9217 "$-1\r\n"
9218 );
9219 assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
9220 assert_eq!(
9221 f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
9222 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
9223 );
9224 assert_eq!(
9225 f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
9226 "-ERR syntax error\r\n"
9227 );
9228 }
9229
9230 #[test]
9231 fn a_search_finds_what_is_inside_it_nearest_first() {
9232 let mut f = Fixture::new();
9233 sicily(&mut f);
9234 let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
9235 assert_eq!(
9236 f.run(&[
9237 b"GEOSEARCH",
9238 b"Sicily",
9239 b"FROMLONLAT",
9240 b"15",
9241 b"37",
9242 b"BYRADIUS",
9243 b"200",
9244 b"km",
9245 b"ASC"
9246 ]),
9247 all
9248 );
9249 assert_eq!(
9252 f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
9253 all
9254 );
9255 assert_eq!(
9256 f.run(&[
9257 b"GEORADIUS_RO",
9258 b"Sicily",
9259 b"15",
9260 b"37",
9261 b"200",
9262 b"km",
9263 b"ASC"
9264 ]),
9265 all
9266 );
9267 assert_eq!(
9270 f.run(&[
9271 b"GEORADIUS",
9272 b"Sicily",
9273 b"15",
9274 b"37",
9275 b"200",
9276 b"km",
9277 b"DESC",
9278 b"COUNT",
9279 b"1"
9280 ]),
9281 "*1\r\n$7\r\nPalermo\r\n"
9282 );
9283 assert_eq!(
9284 f.run(&[
9285 b"GEORADIUS",
9286 b"Sicily",
9287 b"15",
9288 b"37",
9289 b"200",
9290 b"km",
9291 b"COUNT",
9292 b"1"
9293 ]),
9294 "*1\r\n$7\r\nCatania\r\n"
9295 );
9296 let empty = "*0\r\n";
9299 assert_eq!(
9300 f.run(&[
9301 b"GEOSEARCH",
9302 b"Sicily",
9303 b"FROMLONLAT",
9304 b"15",
9305 b"37",
9306 b"BYRADIUS",
9307 b"1",
9308 b"km"
9309 ]),
9310 empty
9311 );
9312 assert_eq!(
9313 f.run(&[
9314 b"GEOSEARCH",
9315 b"nokey",
9316 b"FROMLONLAT",
9317 b"15",
9318 b"37",
9319 b"BYRADIUS",
9320 b"1",
9321 b"km"
9322 ]),
9323 empty
9324 );
9325 assert_eq!(
9326 f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
9327 empty
9328 );
9329 }
9330
9331 #[test]
9332 fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
9333 let mut f = Fixture::new();
9334 sicily(&mut f);
9335 assert_eq!(
9336 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
9337 "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9338 );
9339 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";
9342 assert_eq!(
9343 f.run(&[
9344 b"GEORADIUSBYMEMBER_RO",
9345 b"Sicily",
9346 b"Agrigento",
9347 b"100",
9348 b"km",
9349 b"WITHDIST"
9350 ]),
9351 with_dist
9352 );
9353 assert_eq!(
9354 f.run(&[
9355 b"GEOSEARCH",
9356 b"Sicily",
9357 b"FROMMEMBER",
9358 b"Agrigento",
9359 b"BYRADIUS",
9360 b"100",
9361 b"km",
9362 b"ASC",
9363 b"WITHDIST"
9364 ]),
9365 with_dist
9366 );
9367 assert_eq!(
9368 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
9369 "-ERR could not decode requested zset member\r\n"
9370 );
9371 }
9372
9373 #[test]
9374 fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
9375 let mut f = Fixture::new();
9376 sicily(&mut f);
9377 assert_eq!(
9381 f.run(&[
9382 b"GEOSEARCH",
9383 b"Sicily",
9384 b"FROMLONLAT",
9385 b"15",
9386 b"37",
9387 b"BYBOX",
9388 b"400",
9389 b"400",
9390 b"km",
9391 b"ASC",
9392 b"WITHCOORD",
9393 b"WITHDIST",
9394 b"WITHHASH"
9395 ]),
9396 "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
9397 $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
9398 *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
9399 $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
9400 *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
9401 $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
9402 );
9403 }
9404
9405 #[test]
9406 fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
9407 let mut f = Fixture::new();
9408 sicily(&mut f);
9409 let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
9410 $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
9411 $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
9412 assert_eq!(
9413 f.run(&[
9414 b"GEOSEARCHSTORE",
9415 b"dst",
9416 b"Sicily",
9417 b"FROMLONLAT",
9418 b"15",
9419 b"37",
9420 b"BYRADIUS",
9421 b"200",
9422 b"km",
9423 b"ASC"
9424 ]),
9425 ":3\r\n"
9426 );
9427 assert_eq!(
9428 f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
9429 hashes
9430 );
9431 assert_eq!(
9434 f.run(&[
9435 b"GEORADIUS",
9436 b"Sicily",
9437 b"15",
9438 b"37",
9439 b"200",
9440 b"km",
9441 b"STORE",
9442 b"dst3"
9443 ]),
9444 ":3\r\n"
9445 );
9446 assert_eq!(
9447 f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
9448 hashes
9449 );
9450 assert_eq!(
9459 f.run(&[
9460 b"GEOSEARCHSTORE",
9461 b"dst2",
9462 b"Sicily",
9463 b"FROMLONLAT",
9464 b"15",
9465 b"37",
9466 b"BYRADIUS",
9467 b"200",
9468 b"km",
9469 b"ASC",
9470 b"STOREDIST"
9471 ]),
9472 ":3\r\n"
9473 );
9474 for (member, want) in [
9475 ("Catania", 56.441_257_870_158_19),
9476 ("Agrigento", 130.423_487_067_147_14),
9477 ("Palermo", 190.442_429_847_757_92),
9478 ] {
9479 let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
9480 let got: f64 = reply
9481 .trim_start_matches(|c: char| c != '\n')
9482 .trim()
9483 .parse()
9484 .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
9485 assert!(
9486 (got - want).abs() < 1e-9,
9487 "{member} scored {got} not {want}"
9488 );
9489 }
9490 assert_eq!(
9493 f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9494 "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9495 );
9496 assert_eq!(
9500 f.run(&[
9501 b"GEOSEARCHSTORE",
9502 b"dst",
9503 b"nokey",
9504 b"FROMLONLAT",
9505 b"15",
9506 b"37",
9507 b"BYRADIUS",
9508 b"200",
9509 b"km"
9510 ]),
9511 ":0\r\n"
9512 );
9513 assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9514 }
9515
9516 #[test]
9517 fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9518 let mut f = Fixture::new();
9519 sicily(&mut f);
9520 assert_eq!(
9523 f.run(&[
9524 b"GEOADD",
9525 b"Sicily",
9526 b"XX",
9527 b"CH",
9528 b"13.361389",
9529 b"38.115556",
9530 b"Palermo"
9531 ]),
9532 ":0\r\n"
9533 );
9534 assert_eq!(
9535 f.run(&[
9536 b"GEOADD",
9537 b"Sicily",
9538 b"NX",
9539 b"13.361389",
9540 b"38.9",
9541 b"Palermo"
9542 ]),
9543 ":0\r\n"
9544 );
9545 assert_eq!(
9546 f.run(&[
9547 b"GEOADD",
9548 b"Sicily",
9549 b"CH",
9550 b"13.361389",
9551 b"38.9",
9552 b"Palermo"
9553 ]),
9554 ":1\r\n"
9555 );
9556 assert_eq!(
9559 f.run(&[
9560 b"GEOADD",
9561 b"new",
9562 b"13.361389",
9563 b"38.115556",
9564 b"here",
9565 b"181",
9566 b"38",
9567 b"there"
9568 ]),
9569 "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9570 );
9571 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9572 assert_eq!(
9573 f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9574 "-ERR value is not a valid float\r\n"
9575 );
9576 assert_eq!(
9579 f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9580 "-ERR syntax error\r\n"
9581 );
9582 assert_eq!(
9583 f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9584 "-ERR syntax error\r\n"
9585 );
9586 assert_eq!(
9587 f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9588 "-ERR syntax error\r\n"
9589 );
9590 assert_eq!(
9591 f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9592 "-ERR wrong number of arguments for 'geoadd' command\r\n"
9593 );
9594 }
9595
9596 #[test]
9599 fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9600 let mut f = Fixture::new();
9601 sicily(&mut f);
9602 let cases: &[(&[&[u8]], &str)] = &[
9603 (
9604 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9605 "-ERR need numeric radius\r\n",
9606 ),
9607 (
9608 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9609 "-ERR radius cannot be negative\r\n",
9610 ),
9611 (
9612 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9613 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9614 ),
9615 (
9616 &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9617 "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9618 ),
9619 (
9620 &[
9621 b"GEOSEARCH",
9622 b"Sicily",
9623 b"FROMLONLAT",
9624 b"15",
9625 b"37",
9626 b"BYBOX",
9627 b"x",
9628 b"1",
9629 b"km",
9630 ],
9631 "-ERR need numeric width\r\n",
9632 ),
9633 (
9634 &[
9635 b"GEOSEARCH",
9636 b"Sicily",
9637 b"FROMLONLAT",
9638 b"15",
9639 b"37",
9640 b"BYBOX",
9641 b"1",
9642 b"y",
9643 b"km",
9644 ],
9645 "-ERR need numeric height\r\n",
9646 ),
9647 (
9648 &[
9649 b"GEOSEARCH",
9650 b"Sicily",
9651 b"FROMLONLAT",
9652 b"15",
9653 b"37",
9654 b"BYBOX",
9655 b"-1",
9656 b"1",
9657 b"km",
9658 ],
9659 "-ERR height or width cannot be negative\r\n",
9660 ),
9661 (
9662 &[
9663 b"GEOSEARCH",
9664 b"Sicily",
9665 b"FROMLONLAT",
9666 b"15",
9667 b"37",
9668 b"BYRADIUS",
9669 b"1",
9670 b"km",
9671 b"ANY",
9672 ],
9673 "-ERR the ANY argument requires COUNT argument\r\n",
9674 ),
9675 (
9676 &[
9677 b"GEOSEARCH",
9678 b"Sicily",
9679 b"FROMLONLAT",
9680 b"15",
9681 b"37",
9682 b"BYRADIUS",
9683 b"1",
9684 b"km",
9685 b"COUNT",
9686 b"0",
9687 ],
9688 "-ERR COUNT must be > 0\r\n",
9689 ),
9690 (
9691 &[
9692 b"GEOSEARCH",
9693 b"Sicily",
9694 b"BYRADIUS",
9695 b"1",
9696 b"km",
9697 b"BYBOX",
9698 b"1",
9699 b"1",
9700 b"km",
9701 ],
9702 "-ERR syntax error\r\n",
9703 ),
9704 (
9705 &[
9706 b"GEOSEARCH",
9707 b"Sicily",
9708 b"FROMMEMBER",
9709 b"Palermo",
9710 b"FROMLONLAT",
9711 b"1",
9712 b"2",
9713 b"BYRADIUS",
9714 b"1",
9715 b"km",
9716 ],
9717 "-ERR syntax error\r\n",
9718 ),
9719 (
9722 &[
9723 b"geosearch",
9724 b"Sicily",
9725 b"BYRADIUS",
9726 b"1",
9727 b"km",
9728 b"ASC",
9729 b"WITHDIST",
9730 ],
9731 "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9732 ),
9733 (
9734 &[
9735 b"GEOSEARCH",
9736 b"Sicily",
9737 b"FROMLONLAT",
9738 b"15",
9739 b"37",
9740 b"ASC",
9741 b"WITHDIST",
9742 ],
9743 "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9744 ),
9745 (
9748 &[
9749 b"GEOSEARCHSTORE",
9750 b"d",
9751 b"Sicily",
9752 b"FROMLONLAT",
9753 b"15",
9754 b"37",
9755 b"BYRADIUS",
9756 b"1",
9757 b"km",
9758 b"WITHCOORD",
9759 ],
9760 "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9761 ),
9762 (
9763 &[
9764 b"GEORADIUS",
9765 b"Sicily",
9766 b"15",
9767 b"37",
9768 b"1",
9769 b"km",
9770 b"WITHDIST",
9771 b"STORE",
9772 b"d",
9773 ],
9774 "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9775 ),
9776 (
9779 &[
9780 b"GEORADIUS_RO",
9781 b"Sicily",
9782 b"15",
9783 b"37",
9784 b"1",
9785 b"km",
9786 b"STORE",
9787 b"d",
9788 ],
9789 "-ERR syntax error\r\n",
9790 ),
9791 (
9792 &[
9793 b"GEOSEARCH",
9794 b"Sicily",
9795 b"FROMLONLAT",
9796 b"15",
9797 b"37",
9798 b"BYRADIUS",
9799 b"1",
9800 b"km",
9801 b"STOREDIST",
9802 ],
9803 "-ERR syntax error\r\n",
9804 ),
9805 ];
9806 for (parts, want) in cases {
9807 assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9808 }
9809 }
9810
9811 #[test]
9814 fn every_geo_command_says_wrongtype() {
9815 let mut f = Fixture::new();
9816 f.run(&[b"SET", b"s", b"v"]);
9817 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9818 let cases: &[&[&[u8]]] = &[
9819 &[b"GEOADD", b"s", b"13", b"38", b"m"],
9820 &[b"GEOPOS", b"s", b"m"],
9821 &[b"GEOHASH", b"s", b"m"],
9822 &[b"GEODIST", b"s", b"a", b"b"],
9823 &[
9824 b"GEOSEARCH",
9825 b"s",
9826 b"FROMLONLAT",
9827 b"15",
9828 b"37",
9829 b"BYRADIUS",
9830 b"1",
9831 b"km",
9832 ],
9833 &[
9834 b"GEOSEARCHSTORE",
9835 b"d",
9836 b"s",
9837 b"FROMLONLAT",
9838 b"15",
9839 b"37",
9840 b"BYRADIUS",
9841 b"1",
9842 b"km",
9843 ],
9844 &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9845 &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9846 &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9847 &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9848 ];
9849 for case in cases {
9850 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9851 }
9852 assert_eq!(
9855 f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9856 wrong
9857 );
9858 }
9859
9860 #[test]
9863 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9864 let mut f = Fixture::new();
9865 assert_eq!(
9868 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9869 ":3\r\n"
9870 );
9871 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9872 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9873 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9874 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9876 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9877 assert_eq!(
9878 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9879 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9880 );
9881 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9883 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9884 }
9885
9886 #[test]
9889 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9890 let mut f = Fixture::new();
9891 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9892 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9893 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9894 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9895 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9896 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9898 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9899 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9900
9901 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9905 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9906 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9907 assert_eq!(
9910 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9911 "-ERR array index overflow\r\n"
9912 );
9913 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9914 }
9915
9916 #[test]
9919 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9920 let mut f = Fixture::new();
9921 f.run(&[b"ARSET", b"a", b"1", b"x"]);
9922 assert_eq!(
9923 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9924 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9925 );
9926 assert_eq!(
9929 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9930 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9931 );
9932 assert_eq!(
9934 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9935 "*2\r\n$-1\r\n$-1\r\n"
9936 );
9937 assert_eq!(
9941 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9942 "-ERR range exceeds maximum of 1000000 items\r\n"
9943 );
9944 }
9945
9946 #[test]
9949 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9950 let mut f = Fixture::new();
9951 assert_eq!(
9952 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9953 "-ERR invalid array index\r\n"
9954 );
9955 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9956 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9957 assert_eq!(
9958 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9959 "-ERR invalid array index\r\n"
9960 );
9961 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9962 assert_eq!(
9965 f.run(&[b"ARGET", b"a", b"-1"]),
9966 "-ERR invalid array index\r\n"
9967 );
9968 assert_eq!(
9971 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9972 "-ERR wrong number of arguments for 'armset' command\r\n"
9973 );
9974 assert_eq!(
9975 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9976 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9977 );
9978 }
9979
9980 #[test]
9981 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9982 let mut f = Fixture::new();
9983 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9984 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9985 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9986 assert_eq!(
9989 f.run(&[
9990 b"ARDELRANGE",
9991 b"a",
9992 b"100",
9993 b"200",
9994 b"0",
9995 b"18446744073709551614"
9996 ]),
9997 ":2\r\n"
9998 );
9999 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
10000 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
10001 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
10002 }
10003
10004 #[test]
10007 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
10008 let mut f = Fixture::new();
10009 let long = vec![b'v'; 200];
10010 f.run(&[
10011 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
10012 b"short", b"5", &long, b"6", b"-0",
10013 ]);
10014 assert_eq!(
10018 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
10019 format!(
10020 "*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",
10021 String::from_utf8_lossy(&long)
10022 )
10023 );
10024 }
10025
10026 #[test]
10027 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
10028 let mut f = Fixture::new();
10029 f.run(&[b"ARSET", b"a", b"0", b"x"]);
10030 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
10031 assert_eq!(
10032 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
10033 "$12\r\nsliced-array\r\n"
10034 );
10035 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
10037 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
10038 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
10039 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
10040 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
10041 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
10042 }
10043
10044 #[test]
10045 fn every_array_command_refuses_a_key_holding_something_else() {
10046 let mut f = Fixture::new();
10047 f.run(&[b"SET", b"s", b"v"]);
10048 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10049 for cmd in [
10050 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
10051 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
10052 &[b"ARGET".as_ref(), b"s", b"0"][..],
10053 &[b"ARMGET".as_ref(), b"s", b"0"][..],
10054 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
10055 &[b"ARLEN".as_ref(), b"s"][..],
10056 &[b"ARCOUNT".as_ref(), b"s"][..],
10057 &[b"ARDEL".as_ref(), b"s", b"0"][..],
10058 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
10059 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
10060 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
10061 &[b"ARNEXT".as_ref(), b"s"][..],
10062 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
10063 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
10064 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
10065 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
10066 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
10067 &[b"ARINFO".as_ref(), b"s"][..],
10068 ] {
10069 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
10070 }
10071 }
10072
10073 #[test]
10077 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
10078 let mut f = Fixture::new();
10079 f.run(&[b"SET", b"s", b"v"]);
10080 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10081 let bad = "-ERR invalid array index\r\n";
10082 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
10083 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
10084 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
10085 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
10086 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
10087 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
10088 f.run(&[b"ARSET", b"a", b"0", b"x"]);
10090 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
10091 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
10092 }
10093
10094 #[test]
10095 fn an_append_follows_a_cursor_the_client_can_move() {
10096 let mut f = Fixture::new();
10097 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
10098 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
10099 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
10100 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
10101 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
10102
10103 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
10106 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
10107 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
10108 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
10109 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
10110 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
10111 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
10112
10113 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
10116 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
10117 assert_eq!(
10118 f.run(&[b"ARINSERT", b"a", b"x"]),
10119 "-ERR insert index overflow\r\n"
10120 );
10121 assert_eq!(
10122 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
10123 "-ERR invalid array index\r\n"
10124 );
10125 }
10126
10127 #[test]
10128 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
10129 let mut f = Fixture::new();
10130 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
10131 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
10132 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
10133 assert_eq!(
10134 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
10135 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
10136 );
10137 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
10140 assert_eq!(
10141 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
10142 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
10143 );
10144 assert_eq!(
10147 f.run(&[b"ARRING", b"r", b"0", b"x"]),
10148 "-ERR size must be positive\r\n"
10149 );
10150 assert_eq!(
10151 f.run(&[b"ARRING", b"r", b"big", b"x"]),
10152 "-ERR invalid size\r\n"
10153 );
10154 }
10155
10156 #[test]
10157 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
10158 let mut f = Fixture::new();
10159 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
10160 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
10161 assert_eq!(
10162 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
10163 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
10164 );
10165 assert_eq!(
10166 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
10167 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
10168 );
10169 assert_eq!(
10170 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
10171 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
10172 "more than there is gets what there is"
10173 );
10174 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
10177 assert_eq!(
10178 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
10179 "-ERR syntax error\r\n"
10180 );
10181 assert_eq!(
10182 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
10183 "-ERR invalid COUNT\r\n"
10184 );
10185
10186 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
10189 assert_eq!(
10190 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
10191 "*2\r\n$-1\r\n$1\r\nz\r\n"
10192 );
10193 }
10194
10195 #[test]
10196 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
10197 let mut f = Fixture::new();
10198 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
10199 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
10200 assert_eq!(
10203 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
10204 "*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"
10205 );
10206 assert_eq!(
10207 f.run(&[
10208 b"ARSCAN",
10209 b"a",
10210 b"18446744073709551614",
10211 b"0",
10212 b"LIMIT",
10213 b"1"
10214 ]),
10215 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
10216 );
10217 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
10218 assert_eq!(
10219 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
10220 "-ERR LIMIT must be positive\r\n"
10221 );
10222 assert_eq!(
10223 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
10224 "-ERR syntax error\r\n"
10225 );
10226 assert_eq!(
10227 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
10228 "-ERR wrong number of arguments for 'arscan' command\r\n"
10229 );
10230 }
10231
10232 #[test]
10233 fn a_grep_answers_the_indexes_whose_elements_match() {
10234 let mut f = Fixture::new();
10235 assert_eq!(
10236 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
10237 "*0\r\n"
10238 );
10239 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
10240
10241 assert_eq!(
10244 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
10245 "*3\r\n:0\r\n:1\r\n:2\r\n"
10246 );
10247 assert_eq!(
10248 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
10249 "*3\r\n:2\r\n:1\r\n:0\r\n"
10250 );
10251 assert_eq!(
10252 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
10253 "*2\r\n:1\r\n:2\r\n"
10254 );
10255
10256 assert_eq!(
10259 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
10260 "*1\r\n:0\r\n"
10261 );
10262 assert_eq!(
10263 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
10264 "*2\r\n:0\r\n:3\r\n"
10265 );
10266 assert_eq!(
10267 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
10268 "*1\r\n:2\r\n"
10269 );
10270 assert_eq!(
10271 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
10272 "*2\r\n:1\r\n:2\r\n"
10273 );
10274
10275 let both: &[&[u8]] = &[
10278 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
10279 ];
10280 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
10281 assert_eq!(
10282 f.run(&[
10283 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
10284 ]),
10285 "*0\r\n"
10286 );
10287 assert_eq!(
10288 f.run(&[
10289 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
10290 ]),
10291 "*2\r\n:0\r\n:1\r\n"
10292 );
10293
10294 assert_eq!(
10297 f.run(&[
10298 b"ARGREP",
10299 b"a",
10300 b"-",
10301 b"+",
10302 b"MATCH",
10303 b"a",
10304 b"WITHVALUES",
10305 b"LIMIT",
10306 b"2"
10307 ]),
10308 "*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"
10309 );
10310 assert_eq!(
10311 f.run(&[
10312 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
10313 ]),
10314 "*1\r\n:3\r\n"
10315 );
10316 }
10317
10318 #[test]
10320 fn a_grep_reports_a_broken_command_the_way_redis_does() {
10321 let mut f = Fixture::new();
10322 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
10323 let syntax = "-ERR syntax error\r\n";
10324
10325 assert_eq!(
10328 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
10329 "-ERR invalid array index\r\n"
10330 );
10331 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
10332 assert_eq!(
10334 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
10335 syntax
10336 );
10337 assert_eq!(
10338 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
10339 syntax
10340 );
10341 assert_eq!(
10342 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
10343 syntax,
10344 "a command with no predicate in it at all"
10345 );
10346 assert_eq!(
10347 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
10348 "-ERR LIMIT must be positive\r\n"
10349 );
10350 assert_eq!(
10351 f.run(&[
10352 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
10353 ]),
10354 "-ERR value is not an integer or out of range\r\n"
10355 );
10356 assert_eq!(
10357 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
10358 "-ERR regular expression is empty\r\n"
10359 );
10360 assert_eq!(
10361 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
10362 "-ERR invalid regular expression: Missing ')'\r\n"
10363 );
10364 assert_eq!(
10365 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
10366 "-ERR regular expression backreferences are not supported\r\n"
10367 );
10368 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
10371 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
10372 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
10373 }
10374
10375 #[test]
10376 fn an_op_reduces_a_range_to_one_number() {
10377 let mut f = Fixture::new();
10378 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
10379 assert_eq!(
10380 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
10381 "$4\r\n-0.5\r\n"
10382 );
10383 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
10384 assert_eq!(
10385 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
10386 "$3\r\n2.5\r\n"
10387 );
10388 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
10389 assert_eq!(
10390 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
10391 ":1\r\n"
10392 );
10393 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
10396 assert_eq!(
10397 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
10398 "$19\r\n0.30000000000000004\r\n"
10399 );
10400 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
10401 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
10402
10403 f.run(&[b"ARSET", b"w", b"0", b"word"]);
10406 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
10407 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
10408 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
10409
10410 assert_eq!(
10411 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
10412 "-ERR unknown operation\r\n"
10413 );
10414 assert_eq!(
10415 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
10416 "-ERR MATCH requires a value argument\r\n"
10417 );
10418 assert_eq!(
10419 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
10420 "-ERR wrong number of arguments for 'arop' command\r\n"
10421 );
10422 }
10423
10424 #[test]
10425 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
10426 let mut f = Fixture::new();
10427 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
10428 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
10429 let short = f.run(&[b"ARINFO", b"a"]);
10430 assert!(
10431 short.starts_with("*14\r\n"),
10432 "seven pairs on RESP2: {short}"
10433 );
10434 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
10435 assert!(
10436 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
10437 "{short}"
10438 );
10439 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
10440 let full = f.run(&[b"ARINFO", b"a", b"full"]);
10441 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
10442 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
10445 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
10446 assert!(
10447 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
10448 "{full}"
10449 );
10450 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
10451
10452 let mut g = Fixture::new();
10454 g.run(&[b"HELLO", b"3"]);
10455 g.run(&[b"ARINSERT", b"a", b"x"]);
10456 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
10457 assert!(map.starts_with("%12\r\n"), "{map}");
10458 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
10459 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
10460 }
10461
10462 #[test]
10463 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
10464 let mut f = Fixture::new();
10465 for (score, want) in [
10468 ("3", "3"),
10469 ("3.5", "3.5"),
10470 ("0.3", "0.3"),
10471 ("1e30", "1e+30"),
10472 ("1e19", "1e+19"),
10473 ("1e-7", "1e-7"),
10474 ("0.000001", "0.000001"),
10475 ("4611686018427387904", "4611686018427387904"),
10476 ("-0", "-0"),
10477 ] {
10478 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
10479 assert_eq!(
10480 f.run(&[b"ZSCORE", b"z", b"m"]),
10481 format!("${}\r\n{want}\r\n", want.len()),
10482 "score {score}"
10483 );
10484 }
10485
10486 let mut g = Fixture::new();
10489 g.run(&[b"HELLO", b"3"]);
10490 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10491 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10492 assert_eq!(
10497 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10498 "$31\r\n1000000000000000000000000000000\r\n"
10499 );
10500 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10501 assert_eq!(
10502 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10503 "$20\r\n10000000000000000000\r\n"
10504 );
10505 }
10506
10507 #[test]
10510 fn a_node_comes_back_with_the_fields_it_went_in_with() {
10511 let mut f = Fixture::new();
10512 assert_eq!(
10513 f.run(&[
10514 b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10515 ]),
10516 ":1\r\n"
10517 );
10518 assert_eq!(
10524 f.run(&[b"G.NGET", b"social", b"ada"]),
10525 "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10526 );
10527 assert_eq!(
10530 f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10531 ":0\r\n"
10532 );
10533 assert_eq!(
10534 f.run(&[b"G.NGET", b"social", b"ada"]),
10535 "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10536 );
10537 assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10540 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10541 assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10542 assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10543
10544 assert_eq!(
10547 f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10548 "-ERR syntax error\r\n"
10549 );
10550 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10551
10552 let mut g = Fixture::new();
10554 g.run(&[b"HELLO", b"3"]);
10555 g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10556 assert_eq!(
10557 g.run(&[b"G.NGET", b"social", b"ada"]),
10558 "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10559 );
10560 }
10561
10562 #[test]
10563 fn an_edge_creates_the_ends_it_needs() {
10564 let mut f = Fixture::new();
10565 assert_eq!(
10566 f.run(&[
10567 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10568 ]),
10569 ":1\r\n"
10570 );
10571 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10573 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10574 assert_eq!(
10575 f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10576 "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10577 );
10578 assert_eq!(
10579 f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10580 "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10581 );
10582 assert_eq!(
10585 f.run(&[
10586 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10587 ]),
10588 ":0\r\n"
10589 );
10590 assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10591 assert_eq!(
10593 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10594 ":1\r\n"
10595 );
10596 assert_eq!(
10597 f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10598 ":1\r\n"
10599 );
10600
10601 assert_eq!(
10602 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10603 ":1\r\n"
10604 );
10605 assert_eq!(
10606 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10607 ":0\r\n"
10608 );
10609 assert_eq!(
10612 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10613 ":0\r\n"
10614 );
10615 assert_eq!(
10616 f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10617 ":0\r\n"
10618 );
10619 assert_eq!(
10620 f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10621 ":0\r\n"
10622 );
10623 }
10624
10625 #[test]
10628 fn a_hop_answers_a_cursor_and_a_page() {
10629 let mut f = Fixture::new();
10630 for i in 0..25u32 {
10631 let dst = format!("n{i}");
10632 f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10633 }
10634 let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10636 assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10637
10638 let mut seen = 0;
10639 let mut cursor = String::from("0");
10640 loop {
10641 let page = f.run(&[
10642 b"G.OUT",
10643 b"social",
10644 b"hub",
10645 b"FOLLOWS",
10646 b"COUNT",
10647 b"7",
10648 b"CURSOR",
10649 cursor.as_bytes(),
10650 ]);
10651 let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10652 cursor = head
10653 .rsplit("\r\n")
10654 .next()
10655 .expect("the cursor line")
10656 .to_string();
10657 seen += rest
10658 .split_once("\r\n")
10659 .expect("the page length")
10660 .0
10661 .parse::<usize>()
10662 .expect("a length");
10663 if cursor == "0" {
10664 break;
10665 }
10666 }
10667 assert_eq!(seen, 25, "every neighbour once across the pages");
10668
10669 assert_eq!(
10672 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10673 "*2\r\n$1\r\n0\r\n*0\r\n"
10674 );
10675 assert_eq!(
10676 f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10677 "*2\r\n$1\r\n0\r\n*0\r\n"
10678 );
10679 assert_eq!(
10680 f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10681 "*2\r\n$1\r\n0\r\n*0\r\n"
10682 );
10683 assert_eq!(
10684 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10685 "-ERR COUNT must be a positive integer\r\n"
10686 );
10687 assert_eq!(
10688 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10689 "-ERR syntax error\r\n"
10690 );
10691 }
10692
10693 #[test]
10694 fn a_degree_counts_one_way_or_both() {
10695 let mut f = Fixture::new();
10696 f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10697 f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10698 f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10699 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10700 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10701 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10702 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10703 assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10704 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10705 assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10706 assert_eq!(
10707 f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10708 "-ERR syntax error\r\n"
10709 );
10710 }
10711
10712 #[test]
10715 fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10716 let mut f = Fixture::new();
10717 for (src, dst) in [
10718 ("ada", "grace"),
10719 ("ada", "alan"),
10720 ("grace", "edsger"),
10721 ("alan", "edsger"),
10722 ("edsger", "barbara"),
10723 ] {
10724 f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10725 }
10726 assert_eq!(
10729 f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10730 "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10731 );
10732 assert_eq!(
10733 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10734 "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10735 );
10736 let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10737 assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10738 assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10739 assert_eq!(
10741 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10742 "*1\r\n$5\r\ngrace\r\n"
10743 );
10744 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10746 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10747 assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10748 assert_eq!(
10749 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10750 "-ERR DEPTH must be a positive integer\r\n"
10751 );
10752 assert_eq!(
10753 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10754 "-ERR syntax error\r\n"
10755 );
10756 }
10757
10758 #[test]
10761 fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10762 let mut f = Fixture::new();
10763 for i in 0..6u32 {
10766 let src = format!("n{i}");
10767 let dst = format!("n{}", i + 1);
10768 f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10769 }
10770 assert_eq!(
10771 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10772 "*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"
10773 );
10774 f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10775 assert_eq!(
10776 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10777 "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10778 );
10779 assert_eq!(
10782 f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10783 "*1\r\n$2\r\nn2\r\n"
10784 );
10785 assert_eq!(
10786 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10787 "*0\r\n"
10788 );
10789 assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10791 f.run(&[b"G.NADD", b"road", b"island"]);
10794 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10795 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10796 assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10797 assert_eq!(
10798 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10799 "-ERR syntax error\r\n"
10800 );
10801 }
10802
10803 #[test]
10807 fn the_keyspace_sees_a_graph_key_like_any_other() {
10808 let mut f = Fixture::new();
10809 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10810 assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10811 assert_eq!(
10812 f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10813 "$9\r\nadjacency\r\n"
10814 );
10815 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10816 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10817 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10818 let held = f.server.memory_bytes();
10822 for i in 0..200u32 {
10823 let dst = format!("n{i}");
10824 f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10825 }
10826 assert!(
10827 f.server.memory_bytes() > held,
10828 "two hundred edges cost something: {held} then {}",
10829 f.server.memory_bytes()
10830 );
10831 f.run(&[b"DEL", b"big"]);
10832
10833 assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10836 assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10837 assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10838 assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10839 assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10840 f.run(&[b"SELECT", b"1"]);
10841 assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10842
10843 assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10844 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10845 f.run(&[b"G.NADD", b"g", b"n"]);
10846 assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10847 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10848 }
10849
10850 #[test]
10853 fn a_graph_cannot_be_copied_or_dumped() {
10854 let mut f = Fixture::new();
10855 f.run(&[b"G.NADD", b"social", b"ada"]);
10856 assert_eq!(
10857 f.run(&[b"COPY", b"social", b"other"]),
10858 "-ERR COPY is not supported for a graph\r\n"
10859 );
10860 assert_eq!(
10861 f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10862 "-ERR COPY is not supported for a graph\r\n"
10863 );
10864 assert_eq!(
10865 f.run(&[b"DUMP", b"social"]),
10866 "-ERR DUMP is not supported for a graph\r\n"
10867 );
10868 assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10870 }
10871
10872 #[test]
10875 fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10876 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10877 let mut f = Fixture::new();
10878 f.run(&[b"G.NADD", b"social", b"ada"]);
10879 assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10880 assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10881 assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10882
10883 f.run(&[b"SET", b"str", b"v"]);
10884 for cmd in [
10885 vec![b"G.NADD".as_ref(), b"str", b"n"],
10886 vec![b"G.NGET".as_ref(), b"str", b"n"],
10887 vec![b"G.NDEL".as_ref(), b"str", b"n"],
10888 vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10889 vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10890 vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10891 vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10892 vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10893 vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10894 vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10895 ] {
10896 assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10897 }
10898 }
10899
10900 #[test]
10903 fn a_graph_goes_when_its_last_node_does() {
10904 let mut f = Fixture::new();
10905 f.run(&[
10906 b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10907 ]);
10908 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10909 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10911 assert_eq!(
10912 f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10913 ":0\r\n"
10914 );
10915 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10916 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10917
10918 assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10919 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10920 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10921 assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10922
10923 f.run(&[b"G.NADD", b"social", b"first"]);
10926 f.run(&[b"G.NADD", b"social", b"second"]);
10927 f.run(&[b"G.NDEL", b"social", b"first"]);
10928 f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10929 assert_eq!(
10930 f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10931 "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10932 );
10933 }
10934
10935 #[test]
10940 fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10941 let mut f = Fixture::new();
10942 let doc = br#"{"a":1,"b":{"c":true}}"#;
10943 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10944 assert_eq!(
10947 f.run(&[b"JSON.GET", b"doc"]),
10948 bulk(r#"{"a":1,"b":{"c":true}}"#)
10949 );
10950 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10951 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10952 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10953 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10956 assert_eq!(
10957 f.run(&[b"JSON.GET", b"doc", b".nope"]),
10958 "-ERR Path does not exist\r\n"
10959 );
10960 assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10961 assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10964 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10965 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10966 assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10967 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10968 }
10969
10970 #[test]
10976 fn the_two_json_errors_that_carry_no_prefix() {
10977 let mut f = Fixture::new();
10978 f.run(&[b"SET", b"plain", b"x"]);
10979 let wrong = "-Existing key has wrong Redis type\r\n";
10980 assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10981 assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10982 assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10983 assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10984 assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10985
10986 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10987 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10991 assert_eq!(
10992 f.run(&[b"JSON.GET", b"doc"]),
10993 bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10994 );
10995 assert_eq!(
10996 f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10997 "-Err wrong static path\r\n"
10998 );
10999 }
11000
11001 #[test]
11003 fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
11004 let mut f = Fixture::new();
11005 assert_eq!(
11007 f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
11008 "-ERR new objects must be created at the root\r\n"
11009 );
11010 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
11011 assert_eq!(
11014 f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
11015 "-ERR new objects must be created at the root\r\n"
11016 );
11017 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
11018 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
11019
11020 f.run(&[
11021 b"JSON.SET",
11022 b"doc",
11023 b"$",
11024 br#"{"o":{},"arr":[1,2],"s":"x"}"#,
11025 ]);
11026 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
11028 assert_eq!(
11031 f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
11032 "$-1\r\n"
11033 );
11034 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
11035 assert_eq!(
11037 f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
11038 "-ERR array index out of range\r\n"
11039 );
11040 assert_eq!(
11041 f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
11042 "-ERR array index out of range\r\n"
11043 );
11044 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
11045 assert_eq!(
11048 f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
11049 "$-1\r\n"
11050 );
11051 assert_eq!(
11052 f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
11053 "$-1\r\n"
11054 );
11055 assert_eq!(
11056 f.run(&[b"JSON.GET", b"doc"]),
11057 bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
11058 );
11059 assert!(
11063 f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
11064 .starts_with("-this is not the start of a value")
11065 );
11066 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
11067 }
11068
11069 #[test]
11072 fn the_json_commands_that_do_not_answer_text() {
11073 let mut f = Fixture::new();
11074 let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
11075 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11076
11077 assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
11078 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
11079 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
11080 assert_eq!(
11081 f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
11082 format!("*1\r\n{}", bulk("integer"))
11083 );
11084 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
11087 assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
11088
11089 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
11092 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
11093 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
11096 assert_eq!(
11097 f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
11098 "-ERR Path does not exist or not a bool\r\n"
11099 );
11100 assert_eq!(
11101 f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
11102 "-ERR Path does not exist or not a bool\r\n"
11103 );
11104 assert_eq!(
11105 f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
11106 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11107 );
11108
11109 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
11112 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
11113 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
11114 assert_eq!(
11115 f.run(&[b"JSON.GET", b"doc"]),
11116 bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
11117 );
11118
11119 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
11122 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
11123 assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
11128 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
11129 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
11130 assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
11131 assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
11132 assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
11133 assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
11134 assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
11135 }
11136
11137 #[test]
11144 fn json_get_lays_out_the_wrapper_it_builds() {
11145 let mut f = Fixture::new();
11146 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
11147
11148 assert_eq!(
11149 f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
11150 bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
11151 );
11152 assert_eq!(
11154 f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
11155 bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
11156 );
11157 let fmt: &[&[u8]] = &[b"INDENT", b" ", b"NEWLINE", b"\n", b"SPACE", b" "];
11158 let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
11159 one.extend_from_slice(fmt);
11160 one.push(b"$.b");
11161 assert_eq!(
11162 f.run(&one),
11163 bulk("[\n [\n 1,\n {\n \"c\": 2\n }\n ]\n]")
11164 );
11165 let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
11166 two.extend_from_slice(fmt);
11167 two.push(b"$.a");
11168 two.push(b"$.nope");
11169 assert_eq!(
11170 f.run(&two),
11171 bulk("{\n \"$.a\": [\n 1\n ],\n \"$.nope\": []\n}")
11172 );
11173 let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
11176 root.push(b".a");
11177 assert_eq!(f.run(&root), bulk("1"));
11178 }
11179
11180 #[test]
11183 fn json_mget_answers_once_per_key_whatever_is_under_them() {
11184 let mut f = Fixture::new();
11185 f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
11186 f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
11187 f.run(&[b"SET", b"plain", b"x"]);
11188 assert_eq!(
11189 f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
11190 format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
11191 );
11192 assert_eq!(
11195 f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
11196 format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
11197 );
11198 assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
11201 }
11202
11203 #[test]
11211 fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
11212 let mut f = Fixture::new();
11213 let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
11214 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11215
11216 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
11217 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
11218 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
11219 assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
11220 assert_eq!(
11221 f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
11222 format!("*2\r\n{}{}", bulk("x"), bulk("y"))
11223 );
11224 assert_eq!(
11227 f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
11228 "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
11229 );
11230
11231 assert_eq!(
11234 f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
11235 "-ERR Path does not exist\r\n"
11236 );
11237 assert_eq!(
11238 f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
11239 "-ERR Path does not exist\r\n"
11240 );
11241 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
11242 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
11245 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
11248
11249 assert_eq!(
11253 f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
11254 "-ERR Path does not exist or not an array\r\n"
11255 );
11256 assert_eq!(
11257 f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
11258 "-ERR Path does not exist or not an object\r\n"
11259 );
11260 assert_eq!(
11261 f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
11262 "-WRONGTYPE wrong type of path value - expected object\r\n"
11263 );
11264 assert_eq!(
11265 f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
11266 "-WRONGTYPE wrong type of path value - expected string\r\n"
11267 );
11268
11269 assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
11272 assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
11273 assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
11274 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
11275 assert_eq!(
11276 f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
11277 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11278 );
11279 assert_eq!(
11281 f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
11282 "-ERR Path does not exist or not an object\r\n"
11283 );
11284 }
11285
11286 #[test]
11292 fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
11293 let mut f = Fixture::new();
11294 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
11295
11296 assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
11297 assert_eq!(
11298 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
11299 "*1\r\n:6\r\n"
11300 );
11301 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
11302
11303 assert_eq!(
11306 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
11307 ":7\r\n"
11308 );
11309 assert_eq!(
11310 f.run(&[b"JSON.GET", b"doc", b".a"]),
11311 bulk("[1,2,3,4,5,0,6]")
11312 );
11313 assert_eq!(
11314 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
11315 ":8\r\n"
11316 );
11317 assert_eq!(
11319 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
11320 "-ERR index out of bounds\r\n"
11321 );
11322 assert_eq!(
11323 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
11324 "-ERR index out of bounds\r\n"
11325 );
11326
11327 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
11330 assert_eq!(
11331 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
11332 ":3\r\n"
11333 );
11334 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
11335 assert_eq!(
11336 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
11337 ":2\r\n"
11338 );
11339 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
11340 assert_eq!(
11341 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
11342 ":0\r\n"
11343 );
11344 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11345
11346 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
11349 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
11350 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
11351 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
11352 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
11353
11354 for call in [
11357 &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
11358 &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
11359 &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
11360 &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
11361 ] {
11362 for path in [&b".n"[..], &b".nope"[..]] {
11363 let args: Vec<&[u8]> = call
11364 .iter()
11365 .map(|a| if *a == b"PATH" { path } else { *a })
11366 .collect();
11367 assert_eq!(
11368 f.run(&args),
11369 "-ERR Path does not exist or not an array\r\n",
11370 "{} {}",
11371 String::from_utf8_lossy(call[0]),
11372 String::from_utf8_lossy(path)
11373 );
11374 }
11375 }
11376
11377 assert_eq!(
11380 f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
11381 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11382 );
11383 assert_eq!(
11384 f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
11385 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11386 );
11387
11388 assert!(
11393 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
11394 .starts_with("-this is not the start of a value")
11395 );
11396 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11397 }
11398
11399 #[test]
11407 fn json_arrinsert_is_all_or_nothing_across_the_matches() {
11408 let mut f = Fixture::new();
11409 let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
11410 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11411 assert_eq!(
11412 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
11413 "-ERR index out of bounds\r\n"
11414 );
11415 assert_eq!(
11416 f.run(&[b"JSON.GET", b"doc"]),
11417 bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
11418 );
11419 assert_eq!(
11421 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
11422 "*3\r\n:4\r\n:3\r\n:2\r\n"
11423 );
11424 assert_eq!(
11425 f.run(&[b"JSON.GET", b"doc"]),
11426 bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
11427 );
11428 }
11429
11430 #[test]
11438 fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
11439 let mut f = Fixture::new();
11440 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
11441
11442 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
11443 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
11444 assert_eq!(
11445 f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
11446 "*1\r\n:1\r\n"
11447 );
11448
11449 assert_eq!(
11452 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
11453 ":3\r\n"
11454 );
11455 assert_eq!(
11458 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
11459 ":-1\r\n"
11460 );
11461
11462 for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
11465 assert_eq!(
11466 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
11467 ":3\r\n",
11468 "{}",
11469 String::from_utf8_lossy(start)
11470 );
11471 }
11472 assert_eq!(
11473 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
11474 ":0\r\n"
11475 );
11476 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
11479 assert_eq!(
11480 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
11481 ":-1\r\n"
11482 );
11483
11484 f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
11488 assert_eq!(
11489 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11490 ":0\r\n"
11491 );
11492 assert_eq!(
11493 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11494 ":1\r\n"
11495 );
11496 assert_eq!(
11497 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11498 ":-1\r\n"
11499 );
11500
11501 assert_eq!(
11505 f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11506 "-ERR Path does not exist\r\n"
11507 );
11508 assert_eq!(
11509 f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11510 "-WRONGTYPE wrong type of path value - expected array\r\n"
11511 );
11512 assert_eq!(
11513 f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11514 "-ERR Path does not exist\r\n"
11515 );
11516 assert_eq!(
11517 f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11518 "-ERR Path does not exist\r\n"
11519 );
11520 }
11521
11522 #[test]
11525 fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11526 let mut f = Fixture::new();
11527 let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11528 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11529
11530 assert_eq!(
11533 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11534 bulk("9").as_str()
11535 );
11536 assert_eq!(
11538 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11539 bulk("[11]").as_str()
11540 );
11541 assert_eq!(
11544 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11545 bulk("13.0").as_str()
11546 );
11547 assert_eq!(
11548 f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11549 bulk("number").as_str()
11550 );
11551 assert_eq!(
11552 f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11553 bulk("3.0").as_str()
11554 );
11555 assert_eq!(
11556 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11557 bulk("-8").as_str()
11558 );
11559 f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11562 assert_eq!(
11563 f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11564 bulk("1.224744871391589").as_str()
11565 );
11566 assert_eq!(
11567 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11568 "-ERR result is not a number\r\n"
11569 );
11570 f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11574 assert_eq!(
11575 f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11576 "-ERR numeric overflow\r\n"
11577 );
11578 f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11579 assert_eq!(
11580 f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11581 "-ERR numeric overflow\r\n"
11582 );
11583 f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11585 assert_eq!(
11586 f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11587 "-ERR result is not a number\r\n"
11588 );
11589
11590 assert_eq!(
11594 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11595 bulk("[null]").as_str()
11596 );
11597 assert_eq!(
11598 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11599 bulk("[]").as_str()
11600 );
11601 assert_eq!(
11602 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11603 "-ERR Path does not exist or does not contains a number\r\n"
11604 );
11605 assert_eq!(
11606 f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11607 "-ERR Path does not exist or does not contains a number\r\n"
11608 );
11609 assert_eq!(
11612 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11613 "-bad input number\r\n"
11614 );
11615 assert_eq!(
11616 f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11617 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11618 );
11619 assert_eq!(
11620 f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11621 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11622 );
11623 }
11624
11625 #[test]
11628 fn json_strappend_reads_its_shape_off_the_argument_count() {
11629 let mut f = Fixture::new();
11630 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11631
11632 assert_eq!(
11633 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11634 ":3\r\n"
11635 );
11636 assert_eq!(
11637 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11638 "*1\r\n:4\r\n"
11639 );
11640 assert_eq!(
11643 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11644 ":6\r\n"
11645 );
11646 f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11649 assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11650 assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11651
11652 assert_eq!(
11656 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11657 "-WRONGTYPE wrong type of path value - expected string\r\n"
11658 );
11659 assert_eq!(
11660 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11661 "*1\r\n$-1\r\n"
11662 );
11663 assert_eq!(
11664 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11665 "-ERR Path does not exist or not a string\r\n"
11666 );
11667 assert_eq!(
11668 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11669 "*0\r\n"
11670 );
11671 assert_eq!(
11672 f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11673 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11674 );
11675 }
11676
11677 #[test]
11680 fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11681 let mut f = Fixture::new();
11682 let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11685
11686 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11687 assert_eq!(
11688 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11689 ":4\r\n"
11690 );
11691 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11692 assert_eq!(
11693 f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11694 ":2\r\n"
11695 );
11696 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11697 assert_eq!(
11698 f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11699 ":1\r\n"
11700 );
11701 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11702 assert_eq!(
11703 f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11704 bulk("1").as_str()
11705 );
11706 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11707 assert_eq!(
11708 f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11709 bulk("13").as_str()
11710 );
11711 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11712 assert_eq!(
11713 f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11714 ":4\r\n"
11715 );
11716 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11717 assert_eq!(
11718 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11719 bulk("false").as_str()
11720 );
11721 assert_eq!(
11724 f.run(&[b"JSON.GET", b"doc", b".a"]),
11725 bulk("[false,true,false]").as_str()
11726 );
11727
11728 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11731 assert_eq!(
11732 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11733 ":3\r\n"
11734 );
11735 assert_eq!(
11736 f.run(&[b"JSON.GET", b"doc", b".a"]),
11737 bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11738 );
11739 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11742 assert_eq!(
11743 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11744 "-ERR Path does not exist or not an array\r\n"
11745 );
11746 assert_eq!(
11747 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11748 "-ERR Path does not exist or not a bool\r\n"
11749 );
11750 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11754 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11755 assert_eq!(
11756 f.run(&[b"JSON.GET", b"doc", b".a"]),
11757 bulk("[[],[2]]").as_str()
11758 );
11759 }
11760
11761 #[test]
11764 fn a_write_reaches_a_match_that_sits_inside_another_match() {
11765 let mut f = Fixture::new();
11766 let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11767
11768 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11769 assert_eq!(
11770 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11771 "*3\r\n:3\r\n:2\r\n:3\r\n"
11772 );
11773 assert_eq!(
11774 f.run(&[b"JSON.GET", b"doc", b"$"]),
11775 bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11776 );
11777
11778 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11781 assert_eq!(
11782 f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11783 "*3\r\n:1\r\n:1\r\n:1\r\n"
11784 );
11785 assert_eq!(
11786 f.run(&[b"JSON.GET", b"doc", b"$"]),
11787 bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11788 );
11789
11790 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11793 assert_eq!(
11794 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11795 bulk("[null,8,8]").as_str()
11796 );
11797 }
11798
11799 #[test]
11802 fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11803 let mut f = Fixture::new();
11804 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11805
11806 assert_eq!(
11809 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11810 bulk("[null]").as_str()
11811 );
11812 assert_eq!(
11813 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11814 bulk("[null]").as_str()
11815 );
11816 assert_eq!(
11817 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11818 bulk("[]").as_str()
11819 );
11820 assert_eq!(
11821 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11822 "-ERR Path does not exist or does not contains a number\r\n"
11823 );
11824 assert_eq!(
11826 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11827 "-bad input number\r\n"
11828 );
11829
11830 assert_eq!(
11832 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11833 "*1\r\n$-1\r\n"
11834 );
11835 assert_eq!(
11836 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11837 "-ERR Path does not exist or not a string\r\n"
11838 );
11839 assert_eq!(
11840 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11841 "-WRONGTYPE wrong type of path value - expected string\r\n"
11842 );
11843
11844 assert_eq!(
11846 f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11847 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11848 );
11849 assert_eq!(
11850 f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11851 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11852 );
11853 }
11854
11855 #[test]
11858 fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11859 let mut f = Fixture::new();
11860
11861 assert_eq!(
11865 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11866 "+OK\r\n"
11867 );
11868 assert_eq!(
11869 f.run(&[b"JSON.GET", b"doc", b"$"]),
11870 bulk(r#"[{"x":null,"y":1}]"#).as_str()
11871 );
11872
11873 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11876 assert_eq!(
11877 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11878 "+OK\r\n"
11879 );
11880 assert_eq!(
11881 f.run(&[b"JSON.GET", b"doc", b"$"]),
11882 bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11883 );
11884
11885 assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11887 assert_eq!(
11888 f.run(&[b"JSON.GET", b"doc", b"$"]),
11889 bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11890 );
11891
11892 assert_eq!(
11896 f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11897 "+OK\r\n"
11898 );
11899 assert_eq!(
11900 f.run(&[b"JSON.GET", b"doc", b"$"]),
11901 bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11902 );
11903
11904 assert_eq!(
11908 f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11909 "+OK\r\n"
11910 );
11911 assert_eq!(
11912 f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11913 bulk(r#"[{"z":null}]"#).as_str()
11914 );
11915 assert_eq!(
11916 f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11917 "$-1\r\n"
11918 );
11919 assert_eq!(
11920 f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11921 "-Err wrong static path\r\n"
11922 );
11923
11924 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11926 assert_eq!(
11927 f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11928 "+OK\r\n"
11929 );
11930 assert_eq!(
11931 f.run(&[b"JSON.GET", b"doc", b"$"]),
11932 bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11933 );
11934
11935 assert_eq!(
11937 f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11938 "-ERR syntax error\r\n"
11939 );
11940 assert_eq!(
11941 f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11942 "-ERR new objects must be created at the root\r\n"
11943 );
11944 f.run(&[b"SET", b"str", b"x"]);
11945 assert_eq!(
11946 f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11947 "-Existing key has wrong Redis type\r\n"
11948 );
11949 }
11950
11951 #[test]
11954 fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11955 let mut f = Fixture::new();
11956 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11957 assert_eq!(
11958 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11959 "+OK\r\n"
11960 );
11961 assert_eq!(
11965 f.run(&[b"JSON.GET", b"doc", b"$"]),
11966 bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11967 );
11968
11969 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11972 assert_eq!(
11973 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11974 "+OK\r\n"
11975 );
11976 assert_eq!(
11977 f.run(&[b"JSON.GET", b"doc", b"$"]),
11978 bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11979 );
11980 }
11981
11982 #[test]
11985 fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11986 let mut f = Fixture::new();
11987 let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11988 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11989
11990 assert_eq!(
11991 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11992 bulk(r#"["a","c"]"#).as_str()
11993 );
11994 assert_eq!(
11997 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11998 bulk(r#"["a","c"]"#).as_str()
11999 );
12000 assert_eq!(
12002 f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
12003 bulk(r#""a""#).as_str()
12004 );
12005 assert_eq!(
12006 f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
12007 "*1\r\n$6\r\nobject\r\n"
12008 );
12009
12010 assert_eq!(
12014 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
12015 bulk("[9,10]").as_str()
12016 );
12017 assert_eq!(
12018 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
12019 "+OK\r\n"
12020 );
12021 assert_eq!(
12022 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
12023 "-Err wrong static path\r\n"
12024 );
12025 assert_eq!(
12026 f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
12027 ":2\r\n"
12028 );
12029 assert_eq!(
12030 f.run(&[b"JSON.GET", b"doc", b"$"]),
12031 bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
12032 );
12033
12034 assert!(
12037 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
12038 .starts_with("-ERR")
12039 );
12040 assert!(
12041 f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
12042 .starts_with("-ERR")
12043 );
12044 }
12045
12046 #[test]
12049 fn a_filter_takes_the_membership_operators_and_the_methods_too() {
12050 let mut f = Fixture::new();
12051 let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
12052 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
12053
12054 for (path, want) in [
12055 (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
12056 (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
12057 (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
12058 (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
12059 (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
12060 (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
12061 (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
12062 (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
12063 (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
12064 (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
12065 (b"$.box[?(@.n~)].t", "[]"),
12066 (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
12067 (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
12068 (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
12069 (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
12070 ] {
12071 assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
12072 }
12073
12074 assert_eq!(
12077 f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
12078 "+OK\r\n"
12079 );
12080 assert_eq!(
12081 f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
12082 bulk(r#"["b"]"#).as_str()
12083 );
12084 }
12085
12086 #[test]
12089 fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
12090 let mut f = Fixture::new();
12091 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
12092 assert_eq!(
12093 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12094 "+OK\r\n"
12095 );
12096 assert_eq!(
12097 f.run(&[b"JSON.GET", b"doc", b"$"]),
12098 bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
12099 );
12100 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
12103 assert_eq!(
12104 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12105 "+OK\r\n"
12106 );
12107 assert_eq!(
12108 f.run(&[b"JSON.GET", b"doc", b"$"]),
12109 bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
12110 );
12111 }
12112
12113 #[test]
12116 fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
12117 let mut f = Fixture::new();
12118 assert_eq!(
12119 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
12120 "+OK\r\n"
12121 );
12122 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
12123 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
12124
12125 assert_eq!(
12127 f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
12128 "+OK\r\n"
12129 );
12130 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
12131
12132 f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
12137 assert_eq!(
12138 f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
12139 "$-1\r\n"
12140 );
12141 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
12142 assert_eq!(
12143 f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
12144 "$-1\r\n"
12145 );
12146 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12147
12148 f.run(&[b"SET", b"str", b"x"]);
12153 assert_eq!(
12154 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
12155 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
12156 );
12157 assert_eq!(
12158 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
12159 "-Existing key has wrong Redis type\r\n"
12160 );
12161 assert_eq!(
12162 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
12163 "-ERR new objects must be created at the root\r\n"
12164 );
12165 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
12166
12167 assert_eq!(
12172 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
12173 "-Err wrong static path\r\n"
12174 );
12175 assert_eq!(
12176 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
12177 "-ERR array index out of range\r\n"
12178 );
12179 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12180
12181 f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
12185 assert_eq!(
12186 f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
12187 "+OK\r\n"
12188 );
12189 assert_eq!(
12190 f.run(&[b"JSON.GET", b"c", b"$"]),
12191 bulk(r#"[{"n":3}]"#).as_str()
12192 );
12193
12194 assert_eq!(
12197 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
12198 "-ERR wrong number of arguments for 'json.mset' command\r\n"
12199 );
12200 }
12201
12202 #[test]
12205 fn json_resp_answers_the_document_as_resp_types() {
12206 let mut f = Fixture::new();
12207 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
12208 assert_eq!(
12209 f.run(&[b"JSON.RESP", b"doc"]),
12210 "*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"
12211 );
12212 assert_eq!(
12214 f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
12215 "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
12216 );
12217
12218 f.run(&[
12219 b"JSON.SET",
12220 b"doc",
12221 b"$",
12222 br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
12223 ]);
12224 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
12225 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
12226 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
12229 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
12230 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
12231
12232 assert_eq!(
12235 f.run(&[b"JSON.RESP", b"doc", b".nope"]),
12236 "-ERR Path does not exist\r\n"
12237 );
12238 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
12239 assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
12240 assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
12241 }
12242
12243 #[test]
12247 fn json_debug_answers_a_byte_count_and_its_own_help() {
12248 let mut f = Fixture::new();
12249 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
12250 let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
12251 assert!(one.starts_with(':'), "{one}");
12252 assert_eq!(
12253 f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
12254 format!("*1\r\n{one}")
12255 );
12256 let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
12257 assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
12258
12259 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
12263 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
12264 assert_eq!(
12265 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
12266 "-ERR Path does not exist\r\n"
12267 );
12268 assert_eq!(
12269 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
12270 "*0\r\n"
12271 );
12272
12273 assert_eq!(
12274 f.run(&[b"JSON.DEBUG", b"HELP"]),
12275 "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
12276 $34\r\nHELP - this message\r\n"
12277 );
12278 assert_eq!(
12279 f.run(&[b"JSON.DEBUG", b"NOPE"]),
12280 "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
12281 );
12282 assert_eq!(
12283 f.run(&[b"JSON.DEBUG", b"MEMORY"]),
12284 "-ERR wrong number of arguments for 'json.debug' command\r\n"
12285 );
12286 }
12287
12288 #[test]
12293 fn the_first_vadd_decides_how_wide_the_set_is() {
12294 let mut f = Fixture::new();
12295 assert_eq!(
12296 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
12297 ":1\r\n"
12298 );
12299 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12300 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12301 assert_eq!(
12304 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
12305 ":0\r\n"
12306 );
12307 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12308 assert_eq!(
12311 f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
12312 "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
12313 );
12314 assert_eq!(
12317 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
12318 ":1\r\n"
12319 );
12320 assert_eq!(
12321 f.run(&[b"VEMB", b"v", b"nowhere"]),
12322 "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
12323 );
12324 assert_eq!(
12328 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
12329 "-ERR asked quantization mismatch with existing vector set\r\n"
12330 );
12331 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
12334 assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
12335 assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
12336 }
12337
12338 #[test]
12341 fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
12342 let mut f = Fixture::new();
12343 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
12344 assert_eq!(
12350 f.run(&[b"VEMB", b"v", b"a"]),
12351 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12352 );
12353 f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
12355 assert_eq!(
12356 f.run(&[b"VEMB", b"n", b"a"]),
12357 "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
12358 );
12359 f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
12362 assert_eq!(
12363 f.run(&[b"VEMB", b"b", b"a"]),
12364 "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
12365 );
12366 assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
12367 assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
12368
12369 let mut f = Fixture::new();
12373 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
12374 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
12375 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
12376 assert_eq!(
12377 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
12378 "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
12379 $8\r\nopposite\r\n$1\r\n0\r\n"
12380 );
12381 assert_eq!(
12384 f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
12385 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12386 );
12387 assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
12390 assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
12391 assert_eq!(
12394 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
12395 "*1\r\n$6\r\nacross\r\n"
12396 );
12397 assert_eq!(
12398 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
12399 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12400 );
12401 assert_eq!(
12405 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
12406 "*1\r\n$6\r\nacross\r\n"
12407 );
12408
12409 let mut g = Fixture::new();
12412 g.run(&[b"HELLO", b"3"]);
12413 g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12414 assert_eq!(
12415 g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
12416 "%1\r\n$4\r\neast\r\n,1\r\n"
12417 );
12418 }
12419
12420 #[test]
12422 fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
12423 let mut f = Fixture::new();
12424 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12425 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12426 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
12427 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
12428 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
12431 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
12432 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
12434 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12435 assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
12438 assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
12439 assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
12440 assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
12443 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
12444 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
12445 assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
12446
12447 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12449 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12450 assert_eq!(
12451 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
12452 "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
12453 );
12454 }
12455
12456 #[test]
12459 fn vrem_takes_the_attribute_with_it() {
12460 let mut f = Fixture::new();
12461 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12462 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12463 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
12464 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
12465 assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
12466 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12469
12470 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12473 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12474 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12475 f.run(&[b"VREM", b"v", b"east"]);
12476 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
12477 assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
12478 }
12479
12480 #[test]
12483 fn vinfo_says_partition_first() {
12484 let mut f = Fixture::new();
12485 f.run(&[
12486 b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
12487 ]);
12488 f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
12489 let info = f.run(&[b"VINFO", b"v"]);
12490 assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12491 assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12494 assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12495 assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12496 assert!(
12499 info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12500 "{info}"
12501 );
12502 let mut f = Fixture::new();
12503 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12504 assert!(
12505 f.run(&[b"VINFO", b"v"])
12506 .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12507 );
12508 assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12509 }
12510
12511 fn named() -> Fixture {
12513 let mut f = Fixture::new();
12514 for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12515 .iter()
12516 .enumerate()
12517 {
12518 let x = (i + 1).to_string();
12519 f.run(&[
12520 b"VADD",
12521 b"r",
12522 b"VALUES",
12523 b"2",
12524 x.as_bytes(),
12525 b"1",
12526 name.as_bytes(),
12527 ]);
12528 }
12529 f
12530 }
12531
12532 #[test]
12535 fn vrange_walks_the_names_and_not_the_vectors() {
12536 let mut f = named();
12537 assert_eq!(
12538 f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12539 "*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"
12540 );
12541 assert_eq!(
12542 f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12543 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12544 "the high end is a name and not a prefix, so delta is past it"
12545 );
12546 assert_eq!(
12547 f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12548 "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12549 );
12550 assert_eq!(
12551 f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12552 "*1\r\n$4\r\nbeta\r\n"
12553 );
12554 assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12555 f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12558 assert_eq!(
12559 f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12560 "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12561 );
12562 assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12563 }
12564
12565 #[test]
12568 fn a_vrange_count_of_zero_asks_for_nothing() {
12569 let mut f = named();
12570 assert_eq!(
12571 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12572 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12573 );
12574 assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12575 assert!(
12576 f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12577 .starts_with("*5\r\n"),
12578 "a negative count is no limit at all"
12579 );
12580 }
12581
12582 #[test]
12585 fn vrange_says_which_end_it_could_not_read() {
12586 let mut f = named();
12587 assert_eq!(
12588 f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12589 "-ERR invalid start range format\r\n"
12590 );
12591 assert_eq!(
12592 f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12593 "-ERR invalid end range format\r\n",
12594 "the high end is spelled wrong, which is worth saying before the \
12595 low end being on the wrong side"
12596 );
12597 assert_eq!(
12598 f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12599 "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12600 );
12601 assert_eq!(
12604 f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12605 "-ERR invalid start range format\r\n"
12606 );
12607 assert_eq!(
12608 f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12609 "-ERR invalid COUNT value\r\n"
12610 );
12611 assert_eq!(
12612 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12613 "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12614 );
12615 f.run(&[b"SET", b"s", b"x"]);
12616 assert!(
12617 f.run(&[b"VRANGE", b"s", b"-", b"+"])
12618 .starts_with("-WRONGTYPE")
12619 );
12620 }
12621
12622 #[test]
12625 fn reduce_is_refused_and_not_ignored() {
12626 let mut f = Fixture::new();
12627 let reduce = f.run(&[
12628 b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12629 ]);
12630 assert!(
12631 reduce.starts_with("-ERR REDUCE is not supported."),
12632 "{reduce}"
12633 );
12634 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12635 }
12636
12637 #[test]
12640 fn vsim_filter_reads_the_attributes() {
12641 let mut f = Fixture::new();
12642 for (name, x, y, attr) in [
12643 ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12644 ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12645 ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12646 ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12647 ] {
12648 f.run(&[
12649 b"VADD",
12650 b"v",
12651 b"VALUES",
12652 b"2",
12653 x.as_bytes(),
12654 y.as_bytes(),
12655 name.as_bytes(),
12656 b"SETATTR",
12657 attr.as_bytes(),
12658 ]);
12659 }
12660 assert_eq!(
12663 f.run(&[
12664 b"VSIM",
12665 b"v",
12666 b"VALUES",
12667 b"2",
12668 b"9",
12669 b"1",
12670 b"COUNT",
12671 b"2",
12672 b"FILTER",
12673 b".lang == \"en\"",
12674 ]),
12675 "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12676 );
12677 assert_eq!(
12680 f.run(&[
12681 b"VSIM",
12682 b"v",
12683 b"VALUES",
12684 b"2",
12685 b"9",
12686 b"1",
12687 b"FILTER",
12688 b".lang == 'en' and .year > 1980",
12689 ]),
12690 "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12691 );
12692 assert_eq!(
12694 f.run(&[
12695 b"VSIM",
12696 b"v",
12697 b"VALUES",
12698 b"2",
12699 b"9",
12700 b"1",
12701 b"FILTER",
12702 b".lang in ['fr', 'de']",
12703 ]),
12704 "*1\r\n$1\r\nb\r\n"
12705 );
12706 assert_eq!(
12707 f.run(&[
12708 b"VSIM",
12709 b"v",
12710 b"VALUES",
12711 b"2",
12712 b"9",
12713 b"1",
12714 b"FILTER",
12715 b".rating > 3"
12716 ]),
12717 "*0\r\n"
12718 );
12719 assert_eq!(
12722 f.run(&[
12723 b"VSIM",
12724 b"v",
12725 b"VALUES",
12726 b"2",
12727 b"9",
12728 b"1",
12729 b"TRUTH",
12730 b"FILTER",
12731 b".year < 1980",
12732 ]),
12733 "*1\r\n$1\r\nc\r\n"
12734 );
12735 f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12738 assert_eq!(
12739 f.run(&[
12740 b"VSIM",
12741 b"v",
12742 b"VALUES",
12743 b"2",
12744 b"9",
12745 b"1",
12746 b"COUNT",
12747 b"1",
12748 b"FILTER",
12749 b".lang == \"en\"",
12750 ]),
12751 "*1\r\n$1\r\nb\r\n"
12752 );
12753 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12756 assert_eq!(
12757 f.run(&[
12758 b"VSIM",
12759 b"v",
12760 b"VALUES",
12761 b"2",
12762 b"9",
12763 b"1",
12764 b"COUNT",
12765 b"1",
12766 b"FILTER",
12767 b".lang == \"en\"",
12768 ]),
12769 "*1\r\n$1\r\nb\r\n"
12770 );
12771
12772 let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12775 assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12776 assert_eq!(
12777 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12778 "-ERR invalid FILTER expression\r\n"
12779 );
12780 assert_eq!(
12783 f.run(&[
12784 b"VSIM",
12785 b"v",
12786 b"VALUES",
12787 b"2",
12788 b"9",
12789 b"1",
12790 b"COUNT",
12791 b"1",
12792 b"FILTER-EF",
12793 b"500",
12794 b"FILTER",
12795 b".lang == 'en'",
12796 ]),
12797 "*1\r\n$1\r\nb\r\n"
12798 );
12799 assert_eq!(
12800 f.run(&[
12801 b"VSIM",
12802 b"v",
12803 b"VALUES",
12804 b"2",
12805 b"9",
12806 b"1",
12807 b"COUNT",
12808 b"1",
12809 b"FILTER-EF",
12810 b"0"
12811 ]),
12812 "*1\r\n$1\r\nb\r\n"
12813 );
12814 assert_eq!(
12815 f.run(&[
12816 b"VSIM",
12817 b"v",
12818 b"VALUES",
12819 b"2",
12820 b"9",
12821 b"1",
12822 b"FILTER-EF",
12823 b"lots"
12824 ]),
12825 "-ERR EF must be a positive integer\r\n"
12826 );
12827 }
12828
12829 #[test]
12832 fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12833 let mut f = Fixture::new();
12834 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12835 assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12836 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12837 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12838 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12839 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12840 assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12841 assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12842 assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12843 assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12844 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12845
12846 f.run(&[b"SET", b"s", b"1"]);
12848 assert_eq!(
12849 f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12850 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12851 );
12852 assert_eq!(
12853 f.run(&[b"VCARD", b"s"]),
12854 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12855 );
12856 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12857 assert_eq!(
12858 f.run(&[b"GET", b"v"]),
12859 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12860 );
12861 f.run(&[b"G.NADD", b"social", b"ada"]);
12865 assert_eq!(
12866 f.run(&[b"VCARD", b"social"]),
12867 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12868 );
12869 assert_eq!(
12870 f.run(&[b"G.NGET", b"v", b"ada"]),
12871 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12872 );
12873 }
12874
12875 #[test]
12878 fn vrandmember_has_the_two_shapes_srandmember_has() {
12879 let mut f = Fixture::new();
12880 for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12881 let x = (i + 1).to_string();
12882 f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12883 }
12884 let one = f.run(&[b"VRANDMEMBER", b"v"]);
12886 assert!(one.starts_with("$1\r\n"), "{one}");
12887 let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12889 assert!(all.starts_with("*3\r\n"), "{all}");
12890 for name in ["a", "b", "c"] {
12891 assert!(all.contains(name), "{all} is missing {name}");
12892 }
12893 all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12894 assert!(all.starts_with("*2\r\n"), "{all}");
12895 let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12897 assert!(many.starts_with("*5\r\n"), "{many}");
12898 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12900 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12901 }
12902
12903 #[test]
12906 fn vlinks_reports_one_layer_of_partition_neighbours() {
12907 let mut f = Fixture::new();
12908 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12909 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12910 assert_eq!(
12913 f.run(&[b"VLINKS", b"v", b"east"]),
12914 "*1\r\n*1\r\n$5\r\nnorth\r\n"
12915 );
12916 assert_eq!(
12917 f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12918 "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12919 );
12920 assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12921 assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12922 }
12923
12924 #[test]
12927 fn fp32_and_values_are_the_same_vector() {
12928 let mut f = Fixture::new();
12929 let mut blob = Vec::new();
12930 for x in [3.0f32, 4.0] {
12931 blob.extend_from_slice(&x.to_le_bytes());
12932 }
12933 assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12934 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12935 assert_eq!(
12936 f.run(&[b"VEMB", b"v", b"a"]),
12937 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12938 );
12939 assert_eq!(
12945 f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
12946 "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
12947 );
12948 assert_eq!(
12950 f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12951 "-ERR invalid vector specification\r\n"
12952 );
12953 assert_eq!(
12955 f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12956 "-ERR syntax error\r\n"
12957 );
12958 assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12959 }
12960
12961 #[test]
12966 fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12967 let mut f = Fixture::new();
12968 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12969 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12970 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12971 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12972 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12973 assert_eq!(
12976 f.run(&[b"BF.INFO", b"b"]),
12977 "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12978 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12979 +Expansion rate\r\n:2\r\n"
12980 );
12981 assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12982 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12983 assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12986 assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12987 }
12988
12989 #[test]
12995 fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12996 let mut f = Fixture::new();
12997 f.run(&[b"SET", b"s", b"text"]);
12998 assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12999 assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
13000 for cmd in [
13001 vec![&b"BF.ADD"[..], b"s", b"x"],
13002 vec![&b"BF.MADD"[..], b"s", b"x"],
13003 vec![&b"BF.CARD"[..], b"s"],
13004 vec![&b"BF.INFO"[..], b"s"],
13005 vec![&b"BF.DEBUG"[..], b"s"],
13006 vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
13007 ] {
13008 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13009 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
13010 }
13011 assert_eq!(
13014 f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
13015 "-ERR bad error rate\r\n"
13016 );
13017 assert!(
13018 f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
13019 .starts_with("-WRONGTYPE")
13020 );
13021 }
13022
13023 #[test]
13026 fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
13027 let mut f = Fixture::new();
13028 assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
13029 for i in 0..10u32 {
13030 assert_eq!(
13031 f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
13032 ":1\r\n"
13033 );
13034 }
13035 assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
13036 assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
13037 assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
13038 assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
13041 assert_eq!(
13042 f.run(&[b"BF.DEBUG", b"g"]),
13043 "*3\r\n$7\r\nsize:11\r\n\
13044 $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
13045 $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
13046 );
13047
13048 assert_eq!(
13050 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
13051 "+OK\r\n"
13052 );
13053 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
13054 assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
13055 assert_eq!(
13056 f.run(&[b"BF.ADD", b"n", b"c"]),
13057 "-ERR non scaling filter is full\r\n"
13058 );
13059 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
13062 assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
13065 f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
13066 assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
13067 assert_eq!(
13070 f.run(&[
13071 b"BF.RESERVE",
13072 b"q",
13073 b"0.01",
13074 b"2",
13075 b"NONSCALING",
13076 b"EXPANSION",
13077 b"2"
13078 ]),
13079 "-Nonscaling filters cannot expand\r\n"
13080 );
13081 }
13082
13083 #[test]
13086 fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
13087 let mut f = Fixture::new();
13088 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
13089 assert_eq!(
13090 f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
13091 "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
13092 );
13093 assert_eq!(
13094 f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
13095 "*2\r\n:1\r\n:0\r\n"
13096 );
13097 }
13098
13099 #[test]
13102 fn insert_is_a_reserve_and_a_madd_with_different_errors() {
13103 let mut f = Fixture::new();
13104 assert_eq!(
13105 f.run(&[
13106 b"BF.INSERT",
13107 b"i",
13108 b"CAPACITY",
13109 b"50",
13110 b"ERROR",
13111 b"0.001",
13112 b"ITEMS",
13113 b"a",
13114 b"b"
13115 ]),
13116 "*2\r\n:1\r\n:1\r\n"
13117 );
13118 assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
13119 assert_eq!(
13121 f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13122 "-ERR not found\r\n"
13123 );
13124 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13125 assert_eq!(
13128 f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13129 "-Bad capacity\r\n"
13130 );
13131 assert_eq!(
13132 f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
13133 "-Bad error rate\r\n"
13134 );
13135 assert_eq!(
13136 f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
13137 "-Bad expansion\r\n"
13138 );
13139 assert_eq!(
13144 f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
13145 "*1\r\n:1\r\n"
13146 );
13147 assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
13148 assert_eq!(
13152 f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
13153 "*1\r\n:1\r\n"
13154 );
13155 assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
13156 assert_eq!(
13157 f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
13158 "*1\r\n:1\r\n"
13159 );
13160 assert_eq!(
13161 f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
13162 "-ERR not found\r\n"
13163 );
13164 assert_eq!(
13166 f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13167 "-Unknown argument received\r\n"
13168 );
13169 assert_eq!(
13171 f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13172 "*1\r\n:1\r\n"
13173 );
13174 assert!(
13176 f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
13177 .contains("wrong number of arguments")
13178 );
13179 }
13180
13181 #[test]
13184 fn a_dump_replays_into_a_filter_that_answers_the_same() {
13185 let mut f = Fixture::new();
13186 f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
13187 for i in 0..25u32 {
13188 f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
13189 }
13190 assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
13191
13192 let mut iter = b"0".to_vec();
13195 let mut chunks = 0;
13196 loop {
13197 let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
13198 let text = String::from_utf8_lossy(&raw).into_owned();
13199 let next = text
13200 .split("\r\n")
13201 .nth(1)
13202 .and_then(|n| n.strip_prefix(':'))
13203 .expect("a two element reply of an iterator and a chunk")
13204 .to_owned();
13205 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13206 let data = &body[body
13207 .windows(2)
13208 .position(|w| w == b"\r\n")
13209 .expect("a length line")
13210 + 2..body.len() - 2];
13211 if next == "0" {
13212 assert!(data.is_empty(), "the last chunk is empty");
13213 break;
13214 }
13215 let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
13216 assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
13217 iter = next.into_bytes();
13218 chunks += 1;
13219 }
13220 assert_eq!(chunks, 3, "a header and one chunk per link");
13221
13222 assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
13223 assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
13224 for i in 0..25u32 {
13225 assert_eq!(
13226 f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
13227 ":1\r\n"
13228 );
13229 }
13230
13231 assert_eq!(
13234 f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
13235 "-ERR received bad data\r\n"
13236 );
13237 assert_eq!(
13238 f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
13239 "-ERR received bad data\r\n"
13240 );
13241 assert_eq!(
13243 f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
13244 "-ERR invalid offset - no link found\r\n"
13245 );
13246 assert_eq!(
13247 f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
13248 "-ERR Second argument must be numeric\r\n"
13249 );
13250 assert_eq!(
13253 f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
13254 "-Second argument must be numeric\r\n"
13255 );
13256 }
13257
13258 #[test]
13261 fn reserve_reads_its_numbers_the_way_string2ll_does() {
13262 let mut f = Fixture::new();
13263 for (args, want) in [
13264 (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
13265 (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
13266 (
13267 vec![&b"0"[..], b"10"],
13268 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13269 ),
13270 (
13271 vec![&b"1"[..], b"10"],
13272 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13273 ),
13274 (
13275 vec![&b"inf"[..], b"10"],
13276 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13277 ),
13278 (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
13279 (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
13280 (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
13281 (
13282 vec![&b"0.01"[..], b"0"],
13283 "-ERR capacity must be in the range [1, 1073741824]\r\n",
13284 ),
13285 (
13286 vec![&b"0.01"[..], b"1073741825"],
13287 "-ERR capacity must be in the range [1, 1073741824]\r\n",
13288 ),
13289 ] {
13290 let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
13291 cmd.extend(args.iter().copied());
13292 assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
13293 }
13294 assert_eq!(
13295 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
13296 "-ERR no expansion\r\n"
13297 );
13298 assert_eq!(
13299 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
13300 "-ERR bad expansion\r\n"
13301 );
13302 assert_eq!(
13303 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
13304 "-ERR expansion must be in the range [0, 32768]\r\n"
13305 );
13306 assert_eq!(
13308 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
13309 "+OK\r\n"
13310 );
13311 assert_eq!(
13312 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
13313 "-ERR item exists\r\n"
13314 );
13315 assert_eq!(
13316 f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
13317 "-Invalid information value\r\n"
13318 );
13319 assert!(
13320 f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
13321 .contains("wrong number of arguments")
13322 );
13323 }
13324
13325 #[test]
13327 fn the_bloom_family_answers_in_resp3_spelling_too() {
13328 let mut f = Fixture::new();
13329 f.out.set_proto(Proto::Resp3);
13330 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
13331 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
13332 assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
13333 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
13334 assert_eq!(
13335 f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
13336 "*2\r\n#t\r\n#f\r\n"
13337 );
13338 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
13340 assert_eq!(
13341 f.run(&[b"BF.INFO", b"b"]),
13342 "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
13343 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
13344 +Expansion rate\r\n:2\r\n"
13345 );
13346 assert_eq!(
13349 f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
13350 "%1\r\n+Capacity\r\n:100\r\n"
13351 );
13352 }
13353
13354 fn cf_header(
13363 items: u64,
13364 buckets: u64,
13365 deletes: u64,
13366 filters: u64,
13367 geometry: [u16; 3],
13368 ) -> Vec<u8> {
13369 let mut out = Vec::with_capacity(38);
13370 for n in [items, buckets, deletes, filters] {
13371 out.extend_from_slice(&n.to_le_bytes());
13372 }
13373 for n in geometry {
13374 out.extend_from_slice(&n.to_le_bytes());
13375 }
13376 out
13377 }
13378
13379 #[test]
13383 fn cf_add_makes_the_filter_and_counts_the_copies() {
13384 let mut f = Fixture::new();
13385 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13386 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13387 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
13388 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
13391 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
13392 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
13393 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
13394 assert_eq!(
13395 f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
13396 "*2\r\n:1\r\n:0\r\n"
13397 );
13398 assert_eq!(
13401 f.run(&[b"CF.INFO", b"d"]),
13402 "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13403 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
13404 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
13405 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13406 );
13407 assert_eq!(
13408 f.run(&[b"CF.DEBUG", b"d"]),
13409 "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
13410 max_iterations:20 expansion:1\r\n"
13411 );
13412 assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
13413 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13414
13415 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13417 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
13418 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13419 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
13420 assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
13421
13422 assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
13425 assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
13426 assert_eq!(
13427 f.run(&[b"CF.COMPACT", b"gone"]),
13428 "-Cuckoo filter was not found\r\n"
13429 );
13430 assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
13431 assert!(
13435 f.run(&[b"CF.COMPACT", b"a", b"b"])
13436 .contains("wrong number of arguments")
13437 );
13438 }
13439
13440 #[test]
13443 fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
13444 let mut f = Fixture::new();
13445 f.run(&[b"SET", b"s", b"text"]);
13446 assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
13447 assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
13448 assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
13449 assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
13453 assert_eq!(
13454 f.run(&[b"CF.COMPACT", b"s"]),
13455 "-Cuckoo filter was not found\r\n"
13456 );
13457 for cmd in [
13458 vec![&b"CF.ADD"[..], b"s", b"x"],
13459 vec![&b"CF.ADDNX"[..], b"s", b"x"],
13460 vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
13461 vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
13462 vec![&b"CF.INFO"[..], b"s"],
13463 vec![&b"CF.DEBUG"[..], b"s"],
13464 vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
13465 vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
13466 vec![&b"CF.RESERVE"[..], b"s", b"64"],
13467 ] {
13468 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13469 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
13470 }
13471 }
13472
13473 #[test]
13476 fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
13477 let mut f = Fixture::new();
13478 assert_eq!(
13479 f.run(&[
13480 b"CF.RESERVE",
13481 b"r",
13482 b"64",
13483 b"BUCKETSIZE",
13484 b"1",
13485 b"MAXITERATIONS",
13486 b"7",
13487 b"EXPANSION",
13488 b"4"
13489 ]),
13490 "+OK\r\n"
13491 );
13492 assert_eq!(
13493 f.run(&[b"CF.DEBUG", b"r"]),
13494 "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13495 max_iterations:7 expansion:4\r\n"
13496 );
13497 assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13498
13499 assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13500 assert_eq!(
13501 f.run(&[b"CF.RESERVE", b"q", b"1"]),
13502 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13503 );
13504 assert_eq!(
13507 f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13508 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13509 );
13510 assert_eq!(
13511 f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13512 "+OK\r\n"
13513 );
13514
13515 assert_eq!(
13520 f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13521 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13522 );
13523 assert_eq!(
13524 f.run(&[
13525 b"CF.RESERVE",
13526 b"q2",
13527 b"64",
13528 b"EXPANSION",
13529 b"xx",
13530 b"BUCKETSIZE",
13531 b"0"
13532 ]),
13533 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13534 );
13535 assert_eq!(
13536 f.run(&[
13537 b"CF.RESERVE",
13538 b"q2",
13539 b"64",
13540 b"MAXITERATIONS",
13541 b"0",
13542 b"BUCKETSIZE",
13543 b"0"
13544 ]),
13545 "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13546 );
13547 assert_eq!(
13551 f.run(&[
13552 b"CF.RESERVE",
13553 b"q3",
13554 b"64",
13555 b"BUCKETSIZE",
13556 b"1",
13557 b"BUCKETSIZE",
13558 b"0"
13559 ]),
13560 "+OK\r\n"
13561 );
13562 assert_eq!(
13565 f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13566 "+OK\r\n"
13567 );
13568 assert_eq!(
13569 f.run(&[b"CF.DEBUG", b"q4"]),
13570 "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13571 max_iterations:20 expansion:1\r\n"
13572 );
13573 assert!(
13576 f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13577 .contains("wrong number of arguments")
13578 );
13579 }
13580
13581 #[test]
13584 fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13585 let mut f = Fixture::new();
13586 assert_eq!(
13587 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13588 "*2\r\n:1\r\n:1\r\n"
13589 );
13590 assert_eq!(
13591 f.run(&[b"CF.DEBUG", b"i"]),
13592 "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13593 max_iterations:20 expansion:1\r\n"
13594 );
13595 assert_eq!(
13598 f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13599 "*2\r\n:0\r\n:1\r\n"
13600 );
13601 assert_eq!(
13602 f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13603 "-ERR not found\r\n"
13604 );
13605 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13606
13607 assert_eq!(
13608 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13609 "-Bad capacity\r\n"
13610 );
13611 assert_eq!(
13614 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13615 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13616 );
13617 assert_eq!(
13621 f.run(&[
13622 b"CF.INSERT",
13623 b"i",
13624 b"CAPACITY",
13625 b"8",
13626 b"CAPACITY",
13627 b"2",
13628 b"ITEMS",
13629 b"a"
13630 ]),
13631 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13632 );
13633 assert_eq!(
13637 f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13638 "*1\r\n:1\r\n"
13639 );
13640 assert_eq!(
13641 f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13642 "*1\r\n:1\r\n"
13643 );
13644 assert_eq!(
13645 f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13646 "-Unknown argument received\r\n"
13647 );
13648 assert_eq!(
13650 f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13651 "*1\r\n:1\r\n"
13652 );
13653 assert!(
13655 f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13656 .contains("wrong number of arguments")
13657 );
13658 assert!(
13659 f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13660 .contains("wrong number of arguments")
13661 );
13662 }
13663
13664 #[test]
13667 fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13668 let mut f = Fixture::new();
13669 f.run(&[
13670 b"CF.RESERVE",
13671 b"s",
13672 b"4",
13673 b"BUCKETSIZE",
13674 b"1",
13675 b"EXPANSION",
13676 b"0",
13677 ]);
13678 for i in 0..4u32 {
13679 assert_eq!(
13680 f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13681 ":1\r\n"
13682 );
13683 }
13684 assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13685 assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13686 assert_eq!(
13689 f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13690 "*2\r\n:-1\r\n:-1\r\n"
13691 );
13692 assert_eq!(
13693 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13694 "*2\r\n:0\r\n:-1\r\n"
13695 );
13696
13697 let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13703 assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13704 assert_eq!(
13705 f.run(&[b"CF.ADD", b"g", b"q"]),
13706 "-Maximum expansions reached\r\n"
13707 );
13708 assert_eq!(
13709 f.run(&[b"CF.INFO", b"g"]),
13710 "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13711 +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13712 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13713 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13714 );
13715 }
13716
13717 #[test]
13721 fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13722 let mut f = Fixture::new();
13723 f.run(&[
13724 b"CF.RESERVE",
13725 b"src",
13726 b"8",
13727 b"BUCKETSIZE",
13728 b"2",
13729 b"EXPANSION",
13730 b"2",
13731 ]);
13732 for i in 0..40u32 {
13733 f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13734 }
13735 let mut pos = b"0".to_vec();
13739 let mut chunks = 0;
13740 loop {
13741 let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13742 let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13743 let next = head
13744 .split("\r\n")
13745 .nth(1)
13746 .and_then(|n| n.strip_prefix(':'))
13747 .expect("a two element reply of a position and a chunk")
13748 .to_owned();
13749 if next == "0" {
13750 assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13751 break;
13752 }
13753 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13754 let at = body
13755 .windows(2)
13756 .position(|w| w == b"\r\n")
13757 .expect("a length line")
13758 + 2;
13759 let data = &body[at..body.len() - 2];
13760 assert_eq!(
13761 f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13762 "+OK\r\n",
13763 "loading chunk {chunks}"
13764 );
13765 pos = next.into_bytes();
13766 chunks += 1;
13767 }
13768 assert!(chunks >= 2, "a header and at least one chunk");
13769
13770 assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13771 assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13772 for i in 0..40u32 {
13773 assert_eq!(
13774 f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13775 ":1\r\n"
13776 );
13777 }
13778
13779 f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13782 assert_eq!(
13783 f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13784 "*2\r\n:0\r\n$-1\r\n"
13785 );
13786
13787 assert_eq!(
13791 f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13792 "-Invalid position\r\n"
13793 );
13794 assert_eq!(
13795 f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13796 "-Invalid position\r\n"
13797 );
13798 assert_eq!(
13799 f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13800 "-Invalid position\r\n"
13801 );
13802 assert_eq!(
13803 f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13804 "-Couldn't load chunk!\r\n"
13805 );
13806 let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13808 assert_eq!(
13809 f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13810 "-ERR item exists\r\n"
13811 );
13812 assert_eq!(
13816 f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13817 "-Invalid header\r\n"
13818 );
13819 for (why, bad) in [
13820 ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13821 ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13822 (
13823 "a bucket count that is not a power of two",
13824 cf_header(0, 3, 0, 1, [2, 20, 1]),
13825 ),
13826 ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13827 ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13828 (
13829 "a growth nobody could reach",
13830 cf_header(0, 8, 0, 1, [2, 20, 32769]),
13831 ),
13832 (
13833 "a chain that cannot grow and did",
13834 cf_header(0, 8, 0, 2, [2, 20, 0]),
13835 ),
13836 (
13839 "a filter count that wraps",
13840 cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13841 ),
13842 ] {
13843 assert_eq!(
13844 f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13845 "-Couldn't create filter!\r\n",
13846 "{why}"
13847 );
13848 }
13849 }
13850
13851 #[test]
13854 fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13855 let mut f = Fixture::new();
13856 f.out.set_proto(Proto::Resp3);
13857 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13858 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13859 assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13860 assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13861 assert_eq!(
13862 f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13863 "*2\r\n#t\r\n#f\r\n"
13864 );
13865 assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13866 assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13867 assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13869 assert_eq!(
13870 f.run(&[b"CF.INFO", b"c"]),
13871 "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13872 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13873 +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13874 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13875 );
13876
13877 f.run(&[
13882 b"CF.RESERVE",
13883 b"s",
13884 b"4",
13885 b"BUCKETSIZE",
13886 b"1",
13887 b"EXPANSION",
13888 b"0",
13889 ]);
13890 assert_eq!(
13891 f.run(&[
13892 b"CF.INSERT",
13893 b"s",
13894 b"ITEMS",
13895 b"a",
13896 b"b",
13897 b"c",
13898 b"d",
13899 b"e",
13900 b"f"
13901 ]),
13902 "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13903 );
13904 assert_eq!(
13905 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13906 "*2\r\n:0\r\n:-1\r\n"
13907 );
13908 assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13909 assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13912 }
13913
13914 #[test]
13919 fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13920 let mut f = Fixture::new();
13921 assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13922 assert_eq!(
13923 f.run(&[b"CMS.INFO", b"d"]),
13924 "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13925 );
13926 assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13927 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13928 assert_eq!(
13931 f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13932 "+OK\r\n"
13933 );
13934 assert_eq!(
13935 f.run(&[b"CMS.INFO", b"p"]),
13936 "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13937 );
13938 assert_eq!(
13941 f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13942 "-CMS: key already exists\r\n"
13943 );
13944 assert_eq!(
13945 f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13946 "-CMS: invalid width\r\n"
13947 );
13948 assert_eq!(
13949 f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13950 "-CMS: invalid depth\r\n"
13951 );
13952 assert_eq!(
13953 f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13954 "-CMS: invalid overestimation value\r\n"
13955 );
13956 assert_eq!(
13957 f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13958 "-CMS: invalid prob value\r\n"
13959 );
13960 assert_eq!(
13964 f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13965 "-CMS: invalid init arguments\r\n"
13966 );
13967 assert_eq!(
13971 f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13972 "-CMS: Insufficient memory to create the key\r\n"
13973 );
13974 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13975 }
13976
13977 #[test]
13980 fn increments_are_parsed_whole_and_the_counters_saturate() {
13981 let mut f = Fixture::new();
13982 f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13983 assert_eq!(
13984 f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13985 "*2\r\n:3\r\n:4\r\n"
13986 );
13987 assert_eq!(
13990 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13991 "*2\r\n:4\r\n:5\r\n"
13992 );
13993 assert_eq!(
13995 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13996 "-CMS: Cannot parse number\r\n"
13997 );
13998 assert_eq!(
13999 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
14000 "-CMS: Number cannot be negative\r\n"
14001 );
14002 assert_eq!(
14003 f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
14004 "*2\r\n:5\r\n:4\r\n"
14005 );
14006 f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
14009 assert_eq!(
14010 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
14011 "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
14012 );
14013 assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
14014 f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
14017 f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
14018 f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
14019 assert_eq!(
14020 f.run(&[b"CMS.INFO", b"w"]),
14021 "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
14022 );
14023 assert!(
14026 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
14027 .contains("wrong number of arguments")
14028 );
14029 assert_eq!(
14030 f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
14031 "-CMS: key does not exist\r\n"
14032 );
14033 assert_eq!(
14034 f.run(&[b"CMS.QUERY", b"nope", b"a"]),
14035 "-CMS: key does not exist\r\n"
14036 );
14037 }
14038
14039 #[test]
14042 fn a_merge_lands_whole_or_not_at_all() {
14043 let mut f = Fixture::new();
14044 for name in [&b"m1"[..], b"m2", b"dst"] {
14045 f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
14046 }
14047 f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
14048 f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
14049 assert_eq!(
14050 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
14051 "+OK\r\n"
14052 );
14053 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
14054 assert_eq!(
14057 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
14058 "+OK\r\n"
14059 );
14060 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
14061 assert_eq!(
14062 f.run(&[
14063 b"CMS.MERGE",
14064 b"dst",
14065 b"2",
14066 b"m1",
14067 b"m2",
14068 b"WEIGHTS",
14069 b"2",
14070 b"3"
14071 ]),
14072 "+OK\r\n"
14073 );
14074 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
14075 assert_eq!(
14078 f.run(&[
14079 b"CMS.MERGE",
14080 b"dst",
14081 b"1",
14082 b"m1",
14083 b"WEIGHTS",
14084 b"4611686018427387904"
14085 ]),
14086 "-CMS: MERGE overflow\r\n"
14087 );
14088 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
14089 f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
14092 assert_eq!(
14093 f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
14094 "-CMS: key does not exist\r\n"
14095 );
14096 assert_eq!(
14097 f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
14098 "-CMS: Number of keys must be positive\r\n"
14099 );
14100 assert_eq!(
14101 f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
14102 "-CMS: wrong number of keys\r\n"
14103 );
14104 assert_eq!(
14105 f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
14106 "-CMS: wrong number of keys/weights\r\n"
14107 );
14108 assert_eq!(
14109 f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
14110 "-CMS: width/depth is not equal\r\n"
14111 );
14112 assert_eq!(
14113 f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
14114 "-CMS: key does not exist\r\n"
14115 );
14116 }
14117
14118 #[test]
14121 fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14122 let mut f = Fixture::new();
14123 f.run(&[b"SET", b"s", b"text"]);
14124 for cmd in [
14125 vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
14126 vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
14127 vec![&b"CMS.QUERY"[..], b"s", b"a"],
14128 vec![&b"CMS.INFO"[..], b"s"],
14129 vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
14130 ] {
14131 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14132 let reply = f.run(&cmd);
14133 assert!(
14136 reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
14137 "{name}: {reply}"
14138 );
14139 }
14140 f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
14141 assert_eq!(
14145 f.run(&[b"COPY", b"c", b"c2"]),
14146 "-ERR not supported for this module key\r\n"
14147 );
14148 assert_eq!(
14149 f.run(&[b"DUMP", b"c"]),
14150 "-ERR DUMP is not supported for this module key\r\n"
14151 );
14152 f.run(&[b"G.NADD", b"g", b"a"]);
14154 assert_eq!(
14155 f.run(&[b"COPY", b"g", b"g2"]),
14156 "-ERR COPY is not supported for a graph\r\n"
14157 );
14158 assert_eq!(
14159 f.run(&[b"DUMP", b"g"]),
14160 "-ERR DUMP is not supported for a graph\r\n"
14161 );
14162 assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
14165 assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
14166 assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
14167 assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
14168 assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
14169 }
14170
14171 #[test]
14176 fn a_reserve_takes_three_arguments_or_six() {
14177 let mut f = Fixture::new();
14178 assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
14179 assert_eq!(
14180 f.run(&[b"TOPK.INFO", b"t"]),
14181 "*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"
14182 );
14183 for cmd in [
14186 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
14187 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
14188 ] {
14189 assert!(f.run(&cmd).contains("wrong number of arguments"));
14190 }
14191 assert_eq!(
14192 f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
14193 "+OK\r\n"
14194 );
14195 assert_eq!(
14198 f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
14199 "-TopK: key already exists\r\n"
14200 );
14201 assert_eq!(
14202 f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
14203 "-TopK: invalid k\r\n"
14204 );
14205 assert_eq!(
14206 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
14207 "-TopK: invalid width\r\n"
14208 );
14209 assert_eq!(
14210 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
14211 "-TopK: invalid depth\r\n"
14212 );
14213 assert_eq!(
14215 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
14216 "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
14217 );
14218 assert_eq!(
14219 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
14220 "+OK\r\n"
14221 );
14222 assert_eq!(
14224 f.run(&[
14225 b"TOPK.RESERVE",
14226 b"w",
14227 b"1",
14228 b"4294967295",
14229 b"4294967295",
14230 b"0.9"
14231 ]),
14232 "-ERR Insufficient memory to create topk data structure\r\n"
14233 );
14234 }
14235
14236 #[test]
14238 fn the_kept_set_is_what_query_and_list_answer_from() {
14239 let mut f = Fixture::new();
14240 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
14241 assert_eq!(
14244 f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
14245 "*2\r\n$-1\r\n$-1\r\n"
14246 );
14247 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
14248 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
14251 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
14252 assert_eq!(
14253 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
14254 "*3\r\n:1\r\n:0\r\n:1\r\n"
14255 );
14256 assert_eq!(
14258 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14259 "*3\r\n:11\r\n:1\r\n:6\r\n"
14260 );
14261 assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
14262 assert_eq!(
14263 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
14264 "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
14265 );
14266 assert_eq!(
14269 f.run(&[b"TOPK.LIST", b"t", b"w"]),
14270 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14271 );
14272 assert_eq!(
14273 f.run(&[b"TOPK.LIST", b"t", b""]),
14274 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14275 );
14276 assert_eq!(
14277 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
14278 "-WITHCOUNT keyword expected\r\n"
14279 );
14280 assert_eq!(
14283 f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
14284 "-WITHCOUNT keyword expected\r\n"
14285 );
14286 assert_eq!(
14287 f.run(&[b"TOPK.LIST", b"missing"]),
14288 "-TopK: key does not exist\r\n"
14289 );
14290 f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
14292 assert_eq!(
14293 f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
14294 "*1\r\n$-1\r\n"
14295 );
14296 assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
14297 assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
14298 }
14299
14300 #[test]
14303 fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
14304 let mut f = Fixture::new();
14305 f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
14306 assert_eq!(
14310 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
14311 format!(
14312 "*2\r\n$-1\r\n-{}\r\n",
14313 "TopK: increment must be an integer greater or equal to 0 and smaller or equal to 100,000"
14314 )
14315 );
14316 assert_eq!(
14317 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14318 "*3\r\n:3\r\n:0\r\n:0\r\n"
14319 );
14320 assert_eq!(
14322 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
14323 "*1\r\n$-1\r\n"
14324 );
14325 assert!(
14326 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
14327 .contains("smaller or equal to 100,000")
14328 );
14329 assert!(
14331 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
14332 .contains("wrong number of arguments")
14333 );
14334 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
14335 }
14336
14337 #[test]
14339 fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
14340 let mut f = Fixture::new();
14341 f.run(&[b"HELLO", b"3"]);
14342 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
14343 f.run(&[b"TOPK.ADD", b"t", b"a"]);
14344 assert_eq!(
14345 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
14346 "*2\r\n#t\r\n#f\r\n"
14347 );
14348 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
14350 assert_eq!(
14351 f.run(&[b"TOPK.INFO", b"t"]),
14352 "%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"
14353 );
14354 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
14355 }
14356
14357 #[test]
14360 fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14361 let mut f = Fixture::new();
14362 f.run(&[b"SET", b"s", b"text"]);
14363 for cmd in [
14364 vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
14365 vec![&b"TOPK.ADD"[..], b"s", b"a"],
14366 vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
14367 vec![&b"TOPK.QUERY"[..], b"s", b"a"],
14368 vec![&b"TOPK.COUNT"[..], b"s", b"a"],
14369 vec![&b"TOPK.LIST"[..], b"s"],
14370 vec![&b"TOPK.INFO"[..], b"s"],
14371 ] {
14372 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14373 let reply = f.run(&cmd);
14374 assert!(
14375 reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
14376 "{name}: {reply}"
14377 );
14378 }
14379 f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
14380 assert_eq!(
14381 f.run(&[b"COPY", b"t", b"t2"]),
14382 "-ERR not supported for this module key\r\n"
14383 );
14384 assert_eq!(
14385 f.run(&[b"DUMP", b"t"]),
14386 "-ERR DUMP is not supported for this module key\r\n"
14387 );
14388 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14389 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14390 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14391 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
14392 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14393 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14394 assert_eq!(
14397 f.run(&[b"TOPK.INFO", b"t3"]),
14398 "-TopK: key does not exist\r\n"
14399 );
14400 }
14401
14402 #[test]
14407 fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
14408 let mut f = Fixture::new();
14409 assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
14410 assert_eq!(
14412 f.run(&[b"TDIGEST.INFO", b"t"]),
14413 "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
14414 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
14415 +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
14416 );
14417 assert_eq!(
14418 f.run(&[b"TDIGEST.CREATE", b"t"]),
14419 "-ERR T-Digest: key already exists\r\n"
14420 );
14421 assert!(
14423 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
14424 .contains("wrong number of arguments")
14425 );
14426 assert_eq!(
14427 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
14428 "+OK\r\n"
14429 );
14430 assert_eq!(
14431 f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
14432 "+OK\r\n"
14433 );
14434 assert_eq!(
14438 f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
14439 "-ERR T-Digest: error parsing compression parameter\r\n"
14440 );
14441 assert_eq!(
14442 f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
14443 "-ERR T-Digest: wrong keyword\r\n"
14444 );
14445 assert_eq!(
14446 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
14447 "-ERR T-Digest: error parsing compression parameter\r\n"
14448 );
14449 assert_eq!(
14450 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
14451 "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
14452 );
14453 assert_eq!(
14456 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
14457 "-ERR T-Digest: allocation failed\r\n"
14458 );
14459 assert_eq!(
14461 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
14462 "-ERR T-Digest: allocation failed\r\n"
14463 );
14464 assert_eq!(
14467 f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
14468 "-ERR T-Digest: key already exists\r\n"
14469 );
14470 }
14471
14472 #[test]
14475 fn the_quantile_family_answers_what_the_module_answers() {
14476 let mut f = Fixture::new();
14477 f.run(&[b"TDIGEST.CREATE", b"s"]);
14478 assert_eq!(
14479 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
14480 "+OK\r\n"
14481 );
14482 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
14483 assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
14484 assert_eq!(
14486 f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
14487 "*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"
14488 );
14489 assert_eq!(
14490 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14491 "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14492 );
14493 assert_eq!(
14496 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14497 "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14498 );
14499 assert_eq!(
14500 f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14501 "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14502 );
14503 assert_eq!(
14504 f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14505 "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14506 );
14507 assert_eq!(
14508 f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14509 "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14510 );
14511 assert_eq!(
14512 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14513 "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14514 );
14515 assert_eq!(
14516 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14517 "$3\r\n2.5\r\n"
14518 );
14519 assert_eq!(
14520 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14521 "$3\r\n2.5\r\n"
14522 );
14523 assert_eq!(
14525 f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14526 "-ERR T-Digest: quantile should be in [0,1]\r\n"
14527 );
14528 assert_eq!(
14529 f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14530 "-ERR T-Digest: error parsing quantile\r\n"
14531 );
14532 assert_eq!(
14533 f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14534 "-ERR T-Digest: error parsing cdf\r\n"
14535 );
14536 assert_eq!(
14537 f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14538 "-ERR T-Digest: error parsing value\r\n"
14539 );
14540 assert_eq!(
14541 f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14542 "-ERR T-Digest: rank needs to be non negative\r\n"
14543 );
14544 assert_eq!(
14545 f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14546 "-ERR T-Digest: error parsing rank\r\n"
14547 );
14548 assert_eq!(
14551 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14552 "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14553 );
14554 assert_eq!(
14555 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14556 "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14557 );
14558 assert_eq!(
14559 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14560 "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14561 );
14562 assert_eq!(
14563 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14564 "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14565 );
14566 }
14567
14568 #[test]
14571 fn an_empty_digest_has_an_answer_for_everything() {
14572 let mut f = Fixture::new();
14573 f.run(&[b"TDIGEST.CREATE", b"e"]);
14574 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14575 assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14576 assert_eq!(
14577 f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14578 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14579 );
14580 assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14581 assert_eq!(
14582 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14583 "$3\r\nnan\r\n"
14584 );
14585 assert_eq!(
14588 f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14589 "*2\r\n:-2\r\n:-2\r\n"
14590 );
14591 assert_eq!(
14592 f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14593 "*2\r\n:-2\r\n:-2\r\n"
14594 );
14595 assert_eq!(
14596 f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14597 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14598 );
14599 f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14601 assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14602 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14603 f.run(&[b"TDIGEST.CREATE", b"e2"]);
14606 assert_eq!(
14607 f.run(&[b"TDIGEST.INFO", b"e"]),
14608 f.run(&[b"TDIGEST.INFO", b"e2"])
14609 );
14610 }
14611
14612 #[test]
14615 fn a_sample_is_read_the_way_redis_reads_a_double() {
14616 let mut f = Fixture::new();
14617 f.run(&[b"TDIGEST.CREATE", b"a"]);
14618 for bad in [
14621 &b"nan"[..],
14622 b"1e400",
14623 b"-1e400",
14624 b"1e309",
14625 b"1e-400",
14626 b"",
14627 b" 1",
14628 b"1 ",
14629 b"1e",
14630 b"--1",
14631 ] {
14632 assert_eq!(
14633 f.run(&[b"TDIGEST.ADD", b"a", bad]),
14634 "-ERR T-Digest: error parsing val parameter\r\n",
14635 "{}",
14636 String::from_utf8_lossy(bad)
14637 );
14638 }
14639 for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14642 assert_eq!(
14643 f.run(&[b"TDIGEST.ADD", b"a", word]),
14644 "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14645 "{}",
14646 String::from_utf8_lossy(word)
14647 );
14648 }
14649 for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14652 assert_eq!(
14653 f.run(&[b"TDIGEST.ADD", b"a", good]),
14654 "+OK\r\n",
14655 "{}",
14656 String::from_utf8_lossy(good)
14657 );
14658 }
14659 assert!(
14661 f.run(&[b"TDIGEST.INFO", b"a"])
14662 .contains("Observations\r\n:6\r\n")
14663 );
14664 assert_eq!(
14667 f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14668 "-ERR T-Digest: error parsing val parameter\r\n"
14669 );
14670 assert!(
14671 f.run(&[b"TDIGEST.INFO", b"a"])
14672 .contains("Observations\r\n:6\r\n")
14673 );
14674 }
14675
14676 #[test]
14679 fn a_merge_sweeps_the_destination_between_its_inputs() {
14680 let mut f = Fixture::new();
14681 f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14682 f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14683 f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14684 f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14685 assert_eq!(
14686 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14687 "+OK\r\n"
14688 );
14689 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14694 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14695 assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14696 assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14697 assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14698 assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14699 assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14700 assert!(
14703 f.run(&[b"TDIGEST.INFO", b"m1"])
14704 .contains("Merged nodes\r\n:3\r\n")
14705 );
14706 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14709 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14710 assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14711 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14712 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14715 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14716 assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14717 assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14718 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14720 assert!(
14721 f.run(&[b"TDIGEST.INFO", b"d"])
14722 .contains("Compression\r\n:500\r\n")
14723 );
14724 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14726 assert!(
14727 f.run(&[b"TDIGEST.INFO", b"d"])
14728 .contains("Observations\r\n:12\r\n")
14729 );
14730 assert_eq!(
14732 f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14733 "-ERR T-Digest: error parsing numkeys\r\n"
14734 );
14735 assert_eq!(
14736 f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14737 "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14738 );
14739 assert!(
14740 f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14741 .contains("wrong number of arguments")
14742 );
14743 assert!(
14744 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14745 .contains("wrong number of arguments")
14746 );
14747 assert_eq!(
14748 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14749 "-ERR T-Digest: wrong keyword\r\n"
14750 );
14751 assert_eq!(
14754 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14755 "-ERR T-Digest: key does not exist\r\n"
14756 );
14757 assert!(
14758 f.run(&[b"TDIGEST.INFO", b"d"])
14759 .contains("Observations\r\n:12\r\n")
14760 );
14761 assert_eq!(
14764 f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14765 "-ERR T-Digest: key does not exist\r\n"
14766 );
14767 }
14768
14769 #[test]
14771 fn a_digest_answers_doubles_and_a_map_on_resp3() {
14772 let mut f = Fixture::new();
14773 f.run(&[b"HELLO", b"3"]);
14774 f.run(&[b"TDIGEST.CREATE", b"s"]);
14775 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14776 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14777 assert_eq!(
14778 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14779 "*2\r\n,1\r\n,4\r\n"
14780 );
14781 assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14782 assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14784 assert_eq!(
14785 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14786 "*1\r\n,-inf\r\n"
14787 );
14788 f.run(&[b"TDIGEST.CREATE", b"e"]);
14789 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14790 assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14792 assert_eq!(
14795 f.run(&[b"TDIGEST.INFO", b"s"]),
14796 "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14797 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14798 +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14799 );
14800 }
14801
14802 #[test]
14805 fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14806 let mut f = Fixture::new();
14807 f.run(&[b"SET", b"s", b"text"]);
14808 for cmd in [
14809 vec![&b"TDIGEST.CREATE"[..], b"s"],
14810 vec![&b"TDIGEST.RESET"[..], b"s"],
14811 vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14812 vec![&b"TDIGEST.MIN"[..], b"s"],
14813 vec![&b"TDIGEST.MAX"[..], b"s"],
14814 vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14815 vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14816 vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14817 vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14818 vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14819 vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14820 vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14821 vec![&b"TDIGEST.INFO"[..], b"s"],
14822 ] {
14823 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14824 let reply = f.run(&cmd);
14825 assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14826 }
14827 f.run(&[b"TDIGEST.CREATE", b"t"]);
14829 assert!(
14830 f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14831 .starts_with("-WRONGTYPE")
14832 );
14833 assert!(
14834 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14835 .starts_with("-WRONGTYPE")
14836 );
14837 assert_eq!(
14838 f.run(&[b"COPY", b"t", b"t2"]),
14839 "-ERR not supported for this module key\r\n"
14840 );
14841 assert_eq!(
14842 f.run(&[b"DUMP", b"t"]),
14843 "-ERR DUMP is not supported for this module key\r\n"
14844 );
14845 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14846 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14847 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14848 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14849 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14850 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14851 assert_eq!(
14854 f.run(&[b"TDIGEST.INFO", b"t3"]),
14855 "-ERR T-Digest: key does not exist\r\n"
14856 );
14857 assert_eq!(
14860 f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14861 "-ERR T-Digest: key does not exist\r\n"
14862 );
14863 }
14864
14865 fn without_memory(reply: &str) -> String {
14875 let head = "+memoryUsage\r\n:";
14876 let at = reply.find(head).expect("every TS.INFO reports memory");
14877 let rest = &reply[at + head.len()..];
14878 let end = rest.find("\r\n").expect("and it is a whole number");
14879 format!("{}{}", &reply[..at + head.len()], &rest[end..])
14880 }
14881
14882 #[test]
14885 fn a_series_is_made_empty_and_reports_on_itself() {
14886 let mut f = Fixture::new();
14887 assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
14888 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14889 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
14890 assert_eq!(
14894 without_memory(&f.run(&[b"TS.INFO", b"t"])),
14895 "*28\r\n\
14896 +totalSamples\r\n:0\r\n\
14897 +memoryUsage\r\n:\r\n\
14898 +firstTimestamp\r\n:0\r\n\
14899 +lastTimestamp\r\n:0\r\n\
14900 +retentionTime\r\n:0\r\n\
14901 +chunkCount\r\n:1\r\n\
14902 +chunkSize\r\n:4096\r\n\
14903 +chunkType\r\n+compressed\r\n\
14904 +duplicatePolicy\r\n+block\r\n\
14905 +labels\r\n*0\r\n\
14906 +sourceKey\r\n$-1\r\n\
14907 +rules\r\n*0\r\n\
14908 +ignoreMaxTimeDiff\r\n:0\r\n\
14909 +ignoreMaxValDiff\r\n$1\r\n0\r\n"
14910 );
14911 assert_eq!(
14914 f.run(&[b"TS.CREATE", b"t"]),
14915 "-ERR TSDB: key already exists\r\n"
14916 );
14917 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14918 assert_eq!(
14919 f.run(&[b"TS.CREATE", b"str"]),
14920 "-ERR TSDB: key already exists\r\n"
14921 );
14922 assert_eq!(
14925 f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
14926 "-ERR TSDB: Couldn't parse RETENTION\r\n"
14927 );
14928 let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14934 assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
14935 assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
14936 assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
14937 assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
14938 assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
14939 assert_eq!(
14940 f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
14941 "-ERR TSDB: the key is not a TSDB key\r\n"
14942 );
14943 assert_eq!(
14945 f.run(&[b"TS.INFO", b"nope"]),
14946 "-ERR TSDB: the key does not exist\r\n"
14947 );
14948 assert_eq!(
14949 f.run(&[b"TS.GET", b"nope"]),
14950 "-ERR TSDB: the key does not exist\r\n"
14951 );
14952 assert_eq!(
14953 f.run(&[b"TS.ALTER", b"nope"]),
14954 "-ERR TSDB: the key does not exist\r\n"
14955 );
14956 assert_eq!(
14957 f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
14958 "-ERR TSDB: the key does not exist\r\n"
14959 );
14960 }
14961
14962 #[test]
14965 fn the_options_are_a_keyword_scan_and_not_a_grammar() {
14966 let mut f = Fixture::new();
14967 assert_eq!(
14968 f.run(&[
14969 b"TS.CREATE",
14970 b"t",
14971 b"RETENTION",
14972 b"5000",
14973 b"ENCODING",
14974 b"UNCOMPRESSED",
14975 b"CHUNK_SIZE",
14976 b"128",
14977 b"DUPLICATE_POLICY",
14978 b"LAST",
14979 b"IGNORE",
14980 b"10",
14981 b"0.5",
14982 b"LABELS",
14983 b"room",
14984 b"kitchen"
14985 ]),
14986 "+OK\r\n"
14987 );
14988 let info = f.run(&[b"TS.INFO", b"t"]);
14989 assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
14990 assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
14991 assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
14992 assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
14993 assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
14994 assert!(
14997 info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
14998 "{info}"
14999 );
15000 assert!(
15001 info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
15002 "{info}"
15003 );
15004
15005 assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
15007 assert_eq!(
15011 f.run(&[
15012 b"TS.CREATE",
15013 b"g",
15014 b"LABELS",
15015 b"a",
15016 b"b",
15017 b"RETENTION",
15018 b"5"
15019 ]),
15020 "+OK\r\n"
15021 );
15022 let greedy = f.run(&[b"TS.INFO", b"g"]);
15023 assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
15024 assert!(
15025 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"),
15026 "{greedy}"
15027 );
15028
15029 assert_eq!(
15031 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
15032 "-ERR TSDB: Couldn't parse LABELS\r\n"
15033 );
15034 assert_eq!(
15035 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
15036 "-ERR TSDB: Couldn't parse LABELS\r\n"
15037 );
15038 assert_eq!(
15039 f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
15040 "-ERR TSDB: Couldn't parse RETENTION\r\n"
15041 );
15042 assert_eq!(
15045 f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
15046 "-TSDB: Couldn't parse RETENTION\r\n"
15047 );
15048 assert_eq!(
15049 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
15050 "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
15051 );
15052 assert_eq!(
15053 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
15054 "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
15055 );
15056 assert_eq!(
15057 f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
15058 "-ERR TSDB: unknown ENCODING parameter\r\n"
15059 );
15060 assert!(
15063 f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
15064 .contains("wrong number of arguments for 'ts.create' command")
15065 );
15066 assert_eq!(
15067 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
15068 "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
15069 );
15070 assert_eq!(
15071 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
15072 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
15073 );
15074 assert_eq!(
15075 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
15076 "-ERR TSDB: Couldn't parse IGNORE\r\n"
15077 );
15078 assert_eq!(
15079 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
15080 "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
15081 );
15082 assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
15083
15084 assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
15087 let after = f.run(&[b"TS.INFO", b"t"]);
15088 assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
15089 assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
15090 assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
15091 assert_eq!(
15092 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
15093 "-ERR TSDB: unknown ENCODING parameter\r\n"
15094 );
15095 assert_eq!(
15097 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
15098 "+OK\r\n"
15099 );
15100 assert!(
15101 f.run(&[b"TS.INFO", b"t"])
15102 .contains("+chunkType\r\n+uncompressed\r\n")
15103 );
15104 }
15105
15106 #[test]
15109 fn samples_land_where_they_are_put_and_the_newest_comes_back() {
15110 let mut f = Fixture::new();
15111 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
15112 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
15114 assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
15115 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
15118 assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
15119 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
15120 assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
15123 assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
15124
15125 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15128 assert_eq!(
15129 f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
15130 "-ERR TSDB: invalid value\r\n"
15131 );
15132 for bad in [
15136 &b".5"[..],
15137 b"1.",
15138 b"+1",
15139 b" 1",
15140 b"0x10",
15141 b"inf",
15142 b"1e400",
15143 b"--1",
15144 b"1e",
15145 ] {
15146 assert_eq!(
15147 f.run(&[b"TS.ADD", b"v", b"1", bad]),
15148 "-ERR TSDB: invalid value\r\n",
15149 "{}",
15150 String::from_utf8_lossy(bad)
15151 );
15152 }
15153 assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
15155
15156 assert_eq!(
15159 f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
15160 "-ERR TSDB: invalid timestamp\r\n"
15161 );
15162 assert_eq!(
15163 f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
15164 "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
15165 );
15166
15167 assert_eq!(
15170 f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
15171 "-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"
15172 );
15173 assert_eq!(
15174 f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
15175 ":300\r\n"
15176 );
15177 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
15178 assert_eq!(
15181 f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
15182 ":1\r\n"
15183 );
15184 assert_eq!(
15185 f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
15186 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
15187 );
15188
15189 assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
15192 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
15193 assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
15194 assert_eq!(
15195 f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
15196 "-ERR TSDB: Timestamp is older than retention\r\n"
15197 );
15198 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
15200 assert!(
15201 f.run(&[b"TS.INFO", b"r"])
15202 .contains("+totalSamples\r\n:1\r\n")
15203 );
15204
15205 assert_eq!(
15208 f.run(&[
15209 b"TS.CREATE",
15210 b"i",
15211 b"DUPLICATE_POLICY",
15212 b"LAST",
15213 b"IGNORE",
15214 b"10",
15215 b"0.5"
15216 ]),
15217 "+OK\r\n"
15218 );
15219 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
15220 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
15221 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
15222 }
15223
15224 #[test]
15227 fn a_madd_answers_each_triple_and_creates_nothing() {
15228 let mut f = Fixture::new();
15229 assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
15230 assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
15231 assert_eq!(
15232 f.run(&[
15233 b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
15234 ]),
15235 "*3\r\n:100\r\n:100\r\n:200\r\n"
15236 );
15237 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15240 assert_eq!(
15241 f.run(&[
15242 b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
15243 ]),
15244 "*3\r\n\
15245 -ERR TSDB: the key is not a TSDB key\r\n\
15246 -ERR TSDB: the key is not a TSDB key\r\n\
15247 :300\r\n"
15248 );
15249 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
15250 assert_eq!(
15252 f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
15253 "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
15254 );
15255 assert!(
15257 f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
15258 .contains("wrong number of arguments for 'ts.madd' command")
15259 );
15260 }
15261
15262 #[test]
15264 fn an_increment_walks_the_newest_value_up_and_down() {
15265 let mut f = Fixture::new();
15266 assert_eq!(
15267 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15268 ":100\r\n"
15269 );
15270 assert_eq!(
15271 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15272 ":100\r\n"
15273 );
15274 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
15277 assert_eq!(
15278 f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
15279 ":200\r\n"
15280 );
15281 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
15282 assert_eq!(
15285 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
15286 "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
15287 );
15288 assert_eq!(
15292 f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
15293 ":1\r\n"
15294 );
15295 assert_eq!(
15296 f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
15297 ":1\r\n"
15298 );
15299 assert_eq!(
15300 f.run(&[b"TS.INCRBY", b"t", b"nan"]),
15301 "-ERR TSDB: invalid increase/decrease value\r\n"
15302 );
15303 assert_eq!(
15304 f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
15305 "-ERR TSDB: invalid increase/decrease value\r\n"
15306 );
15307 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15310 assert_eq!(
15311 f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
15312 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15313 );
15314 assert_eq!(
15319 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
15320 "-ERR TSDB: invalid timestamp\r\n"
15321 );
15322 assert_eq!(
15325 f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
15326 format!(":{}\r\n", f.server.now_ms())
15327 );
15328 assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
15330 assert_eq!(
15331 f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
15332 "-ERR TSDB: cannot increment/decrement NaN value\r\n"
15333 );
15334 }
15335
15336 #[test]
15338 fn deleting_takes_out_a_span_and_answers_how_many_went() {
15339 let mut f = Fixture::new();
15340 for at in [b"100".as_slice(), b"200", b"300", b"400"] {
15341 f.run(&[b"TS.ADD", b"t", at, b"1"]);
15342 }
15343 assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
15344 assert!(
15345 f.run(&[b"TS.INFO", b"t"])
15346 .contains("+totalSamples\r\n:2\r\n")
15347 );
15348 assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
15350 assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
15352 let empty = f.run(&[b"TS.INFO", b"t"]);
15355 assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
15356 assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
15357 assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
15358 assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
15359 assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
15360 assert_eq!(
15362 f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
15363 "-ERR TSDB: wrong fromTimestamp\r\n"
15364 );
15365 assert_eq!(
15366 f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
15367 "-ERR TSDB: wrong toTimestamp\r\n"
15368 );
15369 assert_eq!(
15370 f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
15371 "-ERR TSDB: wrong fromTimestamp\r\n"
15372 );
15373 }
15374
15375 #[test]
15378 fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
15379 let mut f = Fixture::new();
15380 f.out = Out::new(Proto::Resp3);
15381 assert_eq!(
15382 f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
15383 "+OK\r\n"
15384 );
15385 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
15386 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
15388 assert_eq!(
15389 without_memory(&f.run(&[b"TS.INFO", b"t"])),
15390 "%14\r\n\
15391 +totalSamples\r\n:1\r\n\
15392 +memoryUsage\r\n:\r\n\
15393 +firstTimestamp\r\n:100\r\n\
15394 +lastTimestamp\r\n:100\r\n\
15395 +retentionTime\r\n:0\r\n\
15396 +chunkCount\r\n:1\r\n\
15397 +chunkSize\r\n:4096\r\n\
15398 +chunkType\r\n+compressed\r\n\
15399 +duplicatePolicy\r\n+block\r\n\
15400 +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
15401 +sourceKey\r\n_\r\n\
15402 +rules\r\n%0\r\n\
15403 +ignoreMaxTimeDiff\r\n:0\r\n\
15404 +ignoreMaxValDiff\r\n,0\r\n"
15405 );
15406 }
15407
15408 #[test]
15411 fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
15412 let mut f = Fixture::new();
15413 for (at, v) in [
15414 (b"100".as_slice(), b"1".as_slice()),
15415 (b"200", b"2"),
15416 (b"300", b"3"),
15417 (b"400", b"4"),
15418 ] {
15419 f.run(&[b"TS.ADD", b"t", at, v]);
15420 }
15421 assert_eq!(
15422 f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
15423 "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
15424 *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
15425 );
15426 assert_eq!(
15428 f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
15429 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15430 );
15431 assert_eq!(
15434 f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
15435 "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
15436 );
15437 assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
15439 assert_eq!(
15441 f.run(&[
15442 b"TS.RANGE",
15443 b"t",
15444 b"-",
15445 b"+",
15446 b"FILTER_BY_VALUE",
15447 b"2",
15448 b"3"
15449 ]),
15450 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15451 );
15452 assert_eq!(
15453 f.run(&[
15454 b"TS.RANGE",
15455 b"t",
15456 b"-",
15457 b"+",
15458 b"FILTER_BY_TS",
15459 b"100",
15460 b"400"
15461 ]),
15462 "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
15463 );
15464 assert_eq!(
15466 f.run(&[
15467 b"TS.RANGE",
15468 b"t",
15469 b"-",
15470 b"+",
15471 b"ZZZ",
15472 b"FILTER_BY_TS",
15473 b"400"
15474 ]),
15475 "*1\r\n*2\r\n:400\r\n+4\r\n"
15476 );
15477 assert_eq!(
15479 f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
15480 "*1\r\n*2\r\n:100\r\n+1\r\n"
15481 );
15482 }
15483
15484 #[test]
15486 fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
15487 let mut f = Fixture::new();
15488 for (at, v) in [
15489 (b"100".as_slice(), b"1".as_slice()),
15490 (b"200", b"2"),
15491 (b"300", b"3"),
15492 (b"400", b"4"),
15493 ] {
15494 f.run(&[b"TS.ADD", b"t", at, v]);
15495 }
15496 assert_eq!(
15497 f.run(&[
15498 b"TS.RANGE",
15499 b"t",
15500 b"-",
15501 b"+",
15502 b"AGGREGATION",
15503 b"avg",
15504 b"200"
15505 ]),
15506 "*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"
15507 );
15508 assert_eq!(
15511 f.run(&[
15512 b"TS.RANGE",
15513 b"t",
15514 b"-",
15515 b"+",
15516 b"AGGREGATION",
15517 b"min,max,count",
15518 b"200"
15519 ]),
15520 "*3\r\n\
15521 *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15522 *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15523 *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15524 );
15525 assert_eq!(
15527 f.run(&[
15528 b"TS.RANGE",
15529 b"t",
15530 b"-",
15531 b"+",
15532 b"AGGREGATION",
15533 b"avg",
15534 b"200",
15535 b"BUCKETTIMESTAMP",
15536 b"+"
15537 ]),
15538 "*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"
15539 );
15540 assert_eq!(
15542 f.run(&[
15543 b"TS.RANGE",
15544 b"t",
15545 b"100",
15546 b"400",
15547 b"ALIGN",
15548 b"100",
15549 b"AGGREGATION",
15550 b"sum",
15551 b"200"
15552 ]),
15553 "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15554 );
15555 assert_eq!(
15558 f.run(&[
15559 b"TS.RANGE",
15560 b"t",
15561 b"-",
15562 b"+",
15563 b"AGGREGATION",
15564 b"count",
15565 b"200"
15566 ]),
15567 "*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"
15568 );
15569 assert_eq!(
15570 f.run(&[
15571 b"TS.RANGE",
15572 b"t",
15573 b"-",
15574 b"+",
15575 b"AGGREGATION",
15576 b"count",
15577 b"200",
15578 b"COUNT",
15579 b"1"
15580 ]),
15581 "*1\r\n*2\r\n:0\r\n+1\r\n"
15582 );
15583 }
15584
15585 #[test]
15588 fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15589 let mut f = Fixture::new();
15590 for (at, v) in [
15591 (b"0".as_slice(), b"1".as_slice()),
15592 (b"100", b"2"),
15593 (b"500", b"nan"),
15594 (b"600", b"3"),
15595 ] {
15596 f.run(&[b"TS.ADD", b"g", at, v]);
15597 }
15598 assert_eq!(
15601 f.run(&[
15602 b"TS.RANGE",
15603 b"g",
15604 b"-",
15605 b"+",
15606 b"AGGREGATION",
15607 b"avg",
15608 b"100"
15609 ]),
15610 "*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"
15611 );
15612 assert_eq!(
15614 f.run(&[
15615 b"TS.RANGE",
15616 b"g",
15617 b"-",
15618 b"+",
15619 b"AGGREGATION",
15620 b"sum",
15621 b"100",
15622 b"EMPTY"
15623 ]),
15624 "*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\
15625 *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\
15626 *2\r\n:600\r\n+3\r\n"
15627 );
15628 assert_eq!(
15633 f.run(&[
15634 b"TS.RANGE",
15635 b"g",
15636 b"-",
15637 b"+",
15638 b"AGGREGATION",
15639 b"last",
15640 b"100",
15641 b"EMPTY"
15642 ]),
15643 "*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\
15644 *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\
15645 *2\r\n:600\r\n+3\r\n"
15646 );
15647 assert_eq!(
15648 f.run(&[
15649 b"TS.REVRANGE",
15650 b"g",
15651 b"-",
15652 b"+",
15653 b"AGGREGATION",
15654 b"last",
15655 b"100",
15656 b"EMPTY"
15657 ]),
15658 "*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\
15659 *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\
15660 *2\r\n:0\r\n+1\r\n"
15661 );
15662 assert_eq!(
15665 f.run(&[
15666 b"TS.RANGE",
15667 b"g",
15668 b"500",
15669 b"600",
15670 b"AGGREGATION",
15671 b"last",
15672 b"100",
15673 b"EMPTY"
15674 ]),
15675 "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15676 );
15677 }
15678
15679 #[test]
15682 fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15683 let mut f = Fixture::new();
15684 f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15685 f.run(&[b"SET", b"str", b"x"]);
15686 let cases: &[(&[&[u8]], &str)] = &[
15687 (
15688 &[b"TS.RANGE", b"t"],
15689 "-ERR wrong number of arguments for 'ts.range' command\r\n",
15690 ),
15691 (
15693 &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15694 "-ERR TSDB: the key does not exist\r\n",
15695 ),
15696 (
15697 &[b"TS.RANGE", b"str", b"-", b"+"],
15698 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15699 ),
15700 (
15701 &[b"TS.RANGE", b"t", b"abc", b"+"],
15702 "-ERR TSDB: wrong fromTimestamp\r\n",
15703 ),
15704 (
15705 &[b"TS.RANGE", b"t", b"-", b"abc"],
15706 "-ERR TSDB: wrong toTimestamp\r\n",
15707 ),
15708 (
15709 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15710 "-ERR TSDB: COUNT argument is missing\r\n",
15711 ),
15712 (
15713 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15714 "-ERR TSDB: Couldn't parse COUNT\r\n",
15715 ),
15716 (
15717 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15718 "-ERR TSDB: Invalid COUNT value\r\n",
15719 ),
15720 (
15721 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15722 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15723 ),
15724 (
15725 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15726 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15727 ),
15728 (
15729 &[
15730 b"TS.RANGE",
15731 b"t",
15732 b"-",
15733 b"+",
15734 b"AGGREGATION",
15735 b"nope",
15736 b"100",
15737 ],
15738 "-ERR TSDB: Unknown aggregation type\r\n",
15739 ),
15740 (
15741 &[
15742 b"TS.RANGE",
15743 b"t",
15744 b"-",
15745 b"+",
15746 b"AGGREGATION",
15747 b"avg,,min",
15748 b"100",
15749 ],
15750 "-ERR TSDB: Empty aggregation type in list\r\n",
15751 ),
15752 (
15754 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15755 "-ERR TSDB: Unknown aggregation type\r\n",
15756 ),
15757 (
15758 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15759 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15760 ),
15761 (
15762 &[
15763 b"TS.RANGE",
15764 b"t",
15765 b"-",
15766 b"+",
15767 b"AGGREGATION",
15768 b"avg",
15769 b"100",
15770 b"X",
15771 b"EMPTY",
15772 ],
15773 "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15774 ),
15775 (
15776 &[
15777 b"TS.RANGE",
15778 b"t",
15779 b"-",
15780 b"+",
15781 b"AGGREGATION",
15782 b"avg",
15783 b"100",
15784 b"BUCKETTIMESTAMP",
15785 b"z",
15786 ],
15787 "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15788 ),
15789 (
15790 &[
15791 b"TS.RANGE",
15792 b"t",
15793 b"-",
15794 b"+",
15795 b"AGGREGATION",
15796 b"avg",
15797 b"100",
15798 b"X",
15799 b"Y",
15800 b"BUCKETTIMESTAMP",
15801 b"-",
15802 ],
15803 "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15804 AGGREGATION flag\r\n",
15805 ),
15806 (
15807 &[
15808 b"TS.RANGE",
15809 b"t",
15810 b"-",
15811 b"+",
15812 b"ALIGN",
15813 b"z",
15814 b"AGGREGATION",
15815 b"avg",
15816 b"100",
15817 ],
15818 "-ERR TSDB: unknown ALIGN parameter\r\n",
15819 ),
15820 (
15821 &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15822 "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15823 ),
15824 (
15825 &[
15826 b"TS.RANGE",
15827 b"t",
15828 b"-",
15829 b"+",
15830 b"ALIGN",
15831 b"-",
15832 b"AGGREGATION",
15833 b"avg",
15834 b"100",
15835 ],
15836 "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15837 ),
15838 (
15839 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15840 "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15841 ),
15842 (
15843 &[
15844 b"TS.RANGE",
15845 b"t",
15846 b"-",
15847 b"+",
15848 b"FILTER_BY_VALUE",
15849 b"x",
15850 b"2",
15851 ],
15852 "-ERR TSDB: Couldn't parse MIN\r\n",
15853 ),
15854 (
15855 &[
15856 b"TS.RANGE",
15857 b"t",
15858 b"-",
15859 b"+",
15860 b"FILTER_BY_VALUE",
15861 b"1",
15862 b"y",
15863 ],
15864 "-ERR TSDB: Couldn't parse MAX\r\n",
15865 ),
15866 (
15867 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15868 "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15869 ),
15870 ];
15871 for (argv, want) in cases {
15872 let got = f.run(argv);
15873 assert_eq!(&got, want, "{:?}", argv.last());
15874 }
15875 f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
15879 f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
15880 assert_eq!(
15881 f.run(&[
15882 b"TS.RANGE",
15883 b"wide",
15884 b"-",
15885 b"+",
15886 b"AGGREGATION",
15887 b"avg",
15888 b"1",
15889 b"EMPTY"
15890 ]),
15891 "-ERR TSDB: the requested range holds too many empty buckets\r\n"
15892 );
15893 }
15894
15895 #[test]
15897 fn resp3_writes_a_read_value_as_a_double() {
15898 let mut f = Fixture::new();
15899 f.out = Out::new(Proto::Resp3);
15900 for (at, v) in [
15901 (b"0".as_slice(), b"1".as_slice()),
15902 (b"100", b"2"),
15903 (b"500", b"nan"),
15904 (b"600", b"3"),
15905 ] {
15906 f.run(&[b"TS.ADD", b"g", at, v]);
15907 }
15908 assert_eq!(
15909 f.run(&[
15910 b"TS.RANGE",
15911 b"g",
15912 b"0",
15913 b"100",
15914 b"AGGREGATION",
15915 b"avg,min",
15916 b"200"
15917 ]),
15918 "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
15919 );
15920 assert_eq!(
15921 f.run(&[
15922 b"TS.RANGE",
15923 b"g",
15924 b"500",
15925 b"600",
15926 b"AGGREGATION",
15927 b"last",
15928 b"100",
15929 b"EMPTY"
15930 ]),
15931 "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
15932 );
15933 }
15934
15935 fn joined() -> Fixture {
15938 let mut f = Fixture::new();
15939 f.run(&[b"TS.CREATE", b"z"]);
15940 for (at, v) in [
15941 (b"10".as_slice(), b"1".as_slice()),
15942 (b"20", b"2"),
15943 (b"40", b"4"),
15944 (b"50", b"5"),
15945 ] {
15946 f.run(&[b"TS.ADD", b"x", at, v]);
15947 }
15948 for (at, v) in [
15949 (b"20".as_slice(), b"20".as_slice()),
15950 (b"30", b"30"),
15951 (b"50", b"50"),
15952 (b"60", b"60"),
15953 ] {
15954 f.run(&[b"TS.ADD", b"y", at, v]);
15955 }
15956 f
15957 }
15958
15959 #[test]
15963 fn an_nrange_joins_its_keys_on_the_timestamp() {
15964 let mut f = joined();
15965 assert_eq!(
15967 f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
15968 "*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\
15969 *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
15970 );
15971 assert_eq!(
15973 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
15974 "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
15975 *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15976 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15977 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15978 *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
15979 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15980 );
15981 assert_eq!(
15984 f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
15985 "*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"
15986 );
15987 assert_eq!(
15988 f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
15989 "*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"
15990 );
15991 assert_eq!(
15994 f.run(&[
15995 b"TS.NREVRANGE",
15996 b"2",
15997 b"x",
15998 b"y",
15999 b"-",
16000 b"+",
16001 b"COUNT",
16002 b"1"
16003 ]),
16004 "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
16005 );
16006 assert_eq!(
16007 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
16008 "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
16009 );
16010 assert_eq!(
16012 f.run(&[
16013 b"TS.NRANGE",
16014 b"2",
16015 b"x",
16016 b"y",
16017 b"-",
16018 b"+",
16019 b"FILTER_BY_VALUE",
16020 b"2",
16021 b"30"
16022 ]),
16023 "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
16024 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
16025 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
16026 *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
16027 );
16028 }
16029
16030 #[test]
16034 fn an_nrange_aggregation_names_one_reduction_a_key() {
16035 let mut f = joined();
16036 assert_eq!(
16037 f.run(&[
16038 b"TS.NRANGE",
16039 b"2",
16040 b"x",
16041 b"y",
16042 b"-",
16043 b"+",
16044 b"AGGREGATION",
16045 b"sum",
16046 b"sum",
16047 b"20"
16048 ]),
16049 "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
16050 *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
16051 *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
16052 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
16053 );
16054 assert_eq!(
16056 f.run(&[
16057 b"TS.NRANGE",
16058 b"2",
16059 b"x",
16060 b"y",
16061 b"-",
16062 b"+",
16063 b"AGGREGATION",
16064 b"sum,count",
16065 b"avg",
16066 b"20"
16067 ]),
16068 "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
16069 *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
16070 *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
16071 *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
16072 );
16073 assert_eq!(
16076 f.run(&[
16077 b"TS.NRANGE",
16078 b"2",
16079 b"x",
16080 b"y",
16081 b"-",
16082 b"+",
16083 b"AGGREGATION",
16084 b"avg",
16085 b"sum",
16086 b"100",
16087 b"EMPTY",
16088 b"BUCKETTIMESTAMP",
16089 b"end"
16090 ]),
16091 "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
16092 );
16093 assert_eq!(
16096 f.run(&[
16097 b"TS.NRANGE",
16098 b"2",
16099 b"x",
16100 b"y",
16101 b"-",
16102 b"+",
16103 b"AGGREGATION",
16104 b"avg",
16105 b"COUNT",
16106 b"100"
16107 ]),
16108 "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
16109 );
16110 }
16111
16112 #[test]
16115 fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
16116 let mut f = joined();
16117 f.run(&[b"SET", b"str", b"hi"]);
16118 let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
16119 let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
16120 must be equal to numkeys\r\n";
16121 let cases: &[(&[&[u8]], &str)] = &[
16122 (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
16123 (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
16124 (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
16125 (
16128 &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
16129 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16130 ),
16131 (
16132 &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
16133 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16134 ),
16135 (
16138 &[
16139 b"TS.NRANGE",
16140 b"2",
16141 b"x",
16142 b"y",
16143 b"abc",
16144 b"+",
16145 b"AGGREGATION",
16146 b"nope",
16147 b"sum",
16148 b"100",
16149 ],
16150 "-ERR TSDB: Unknown aggregation type\r\n",
16151 ),
16152 (
16153 &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
16154 "-ERR TSDB: wrong fromTimestamp\r\n",
16155 ),
16156 (
16157 &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
16158 "-ERR TSDB: wrong toTimestamp\r\n",
16159 ),
16160 (
16164 &[
16165 b"TS.NRANGE",
16166 b"2",
16167 b"x",
16168 b"y",
16169 b"-",
16170 b"+",
16171 b"AGGREGATION",
16172 b"avg",
16173 ],
16174 numkeys,
16175 ),
16176 (
16177 &[
16178 b"TS.NRANGE",
16179 b"2",
16180 b"x",
16181 b"y",
16182 b"-",
16183 b"+",
16184 b"AGGREGATION",
16185 b"100",
16186 b"sum",
16187 b"100",
16188 ],
16189 numkeys,
16190 ),
16191 (
16192 &[
16193 b"TS.NRANGE",
16194 b"2",
16195 b"x",
16196 b"y",
16197 b"-",
16198 b"+",
16199 b"AGGREGATION",
16200 b"avg",
16201 b"sum",
16202 b"sum",
16203 b"100",
16204 ],
16205 numkeys,
16206 ),
16207 (
16208 &[
16209 b"TS.NRANGE",
16210 b"2",
16211 b"x",
16212 b"y",
16213 b"-",
16214 b"+",
16215 b"AGGREGATION",
16216 b"avg",
16217 b"sum",
16218 b"abc",
16219 ],
16220 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16221 ),
16222 (
16223 &[
16224 b"TS.NRANGE",
16225 b"2",
16226 b"x",
16227 b"y",
16228 b"-",
16229 b"+",
16230 b"AGGREGATION",
16231 b"avg",
16232 b"sum",
16233 b"0",
16234 ],
16235 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16236 ),
16237 (
16240 &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
16241 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16242 ),
16243 (
16244 &[
16245 b"TS.NRANGE",
16246 b"1",
16247 b"x",
16248 b"-",
16249 b"+",
16250 b"AGGREGATION",
16251 b"100",
16252 b"200",
16253 ],
16254 "-ERR TSDB: Unknown aggregation type\r\n",
16255 ),
16256 (
16258 &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
16259 "-ERR TSDB: the key does not exist\r\n",
16260 ),
16261 (
16262 &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
16263 "-ERR WRONGTYPE Operation against a key \
16264 holding the wrong kind of value\r\n",
16265 ),
16266 ];
16267 for (argv, want) in cases {
16268 let got = f.run(argv);
16269 assert_eq!(&got, want, "{argv:?}");
16270 }
16271 }
16272
16273 #[test]
16275 fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
16276 let mut f = joined();
16277 assert_eq!(
16278 f.run(&[b"TS.READ", b"x", b"-"]),
16279 "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
16280 *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16281 );
16282 assert_eq!(
16285 f.run(&[b"TS.READ", b"x", b"+"]),
16286 "*1\r\n*2\r\n:50\r\n+5\r\n"
16287 );
16288 assert_eq!(
16289 f.run(&[b"TS.READ", b"x", b"25"]),
16290 "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16291 );
16292 assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
16295 assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
16296 assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
16297 assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
16298 assert_eq!(
16302 f.run(&[b"TS.READ", b"x", b"abc"]),
16303 "-TSDB: invalid timestamp\r\n"
16304 );
16305 assert_eq!(
16306 f.run(&[b"TS.READ", b"x", b"-1"]),
16307 "-TSDB: invalid timestamp\r\n"
16308 );
16309 f.run(&[b"SET", b"str", b"hi"]);
16310 assert_eq!(
16311 f.run(&[b"TS.READ", b"str", b"-"]),
16312 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
16313 );
16314 assert_eq!(
16317 f.run(&[b"TS.READ", b"x"]),
16318 "-ERR wrong number of arguments for 'ts.read' command\r\n"
16319 );
16320 assert_eq!(
16321 f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
16322 "-ERR wrong number of arguments for 'ts.read' command\r\n"
16323 );
16324 }
16325
16326 #[test]
16329 fn getkeys_reads_the_count_of_a_joined_read() {
16330 let mut f = Fixture::new();
16331 assert_eq!(
16332 f.run(&[
16333 b"COMMAND",
16334 b"GETKEYS",
16335 b"TS.NRANGE",
16336 b"2",
16337 b"a",
16338 b"b",
16339 b"-",
16340 b"+"
16341 ]),
16342 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
16343 );
16344 assert_eq!(
16345 f.run(&[
16346 b"COMMAND",
16347 b"GETKEYS",
16348 b"TS.NREVRANGE",
16349 b"1",
16350 b"a",
16351 b"-",
16352 b"+"
16353 ]),
16354 "*1\r\n$1\r\na\r\n"
16355 );
16356 for n in [b"0".as_slice(), b"9", b"abc"] {
16359 assert_eq!(
16360 f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
16361 "-ERR Invalid arguments specified for command\r\n"
16362 );
16363 }
16364 }
16365
16366 fn labelled() -> Fixture {
16368 let mut f = Fixture::new();
16369 f.run(&[
16370 b"TS.CREATE",
16371 b"a",
16372 b"LABELS",
16373 b"room",
16374 b"kitchen",
16375 b"x",
16376 b"1",
16377 ]);
16378 f.run(&[
16379 b"TS.CREATE",
16380 b"b",
16381 b"LABELS",
16382 b"room",
16383 b"bedroom",
16384 b"x",
16385 b"2",
16386 ]);
16387 f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
16388 f.run(&[b"TS.CREATE", b"d"]);
16389 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
16390 f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
16391 f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
16392 f
16393 }
16394
16395 #[test]
16398 fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
16399 let mut f = labelled();
16400 let cases: &[(&[&[u8]], &str)] = &[
16401 (
16404 &[b"TS.QUERYINDEX", b"room=kitchen"],
16405 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16406 ),
16407 (
16408 &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
16409 "*1\r\n$1\r\na\r\n",
16410 ),
16411 (
16412 &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
16413 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
16414 ),
16415 (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
16418 (
16420 &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
16421 "*1\r\n$1\r\nc\r\n",
16422 ),
16423 (
16424 &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
16425 "*1\r\n$1\r\na\r\n",
16426 ),
16427 (
16428 &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
16429 "-ERR TSDB: please provide at least one matcher\r\n",
16430 ),
16431 (
16434 &[b"TS.QUERYINDEX", b"room==kitchen"],
16435 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16436 ),
16437 (
16438 &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
16439 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16440 ),
16441 (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
16442 (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
16445 (
16446 &[b"TS.QUERYINDEX", b"=(1)"],
16447 "-ERR TSDB: failed parsing labels\r\n",
16448 ),
16449 (
16450 &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
16451 "-ERR TSDB: failed parsing labels\r\n",
16452 ),
16453 (
16454 &[b"TS.QUERYINDEX", b"room=(kitchen"],
16455 "-ERR TSDB: failed parsing labels\r\n",
16456 ),
16457 (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
16458 (
16459 &[b"TS.QUERYINDEX", b"nonsense"],
16460 "-ERR TSDB: failed parsing labels\r\n",
16461 ),
16462 (
16464 &[b"TS.QUERYINDEX", b"room!=kitchen"],
16465 "-ERR TSDB: please provide at least one matcher\r\n",
16466 ),
16467 (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
16469 (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
16470 (
16471 &[b"TS.QUERYINDEX"],
16472 "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
16473 ),
16474 ];
16475 for (argv, want) in cases {
16476 let got = f.run(argv);
16477 assert_eq!(&got, want, "{:?}", argv.last());
16478 }
16479 }
16480
16481 #[test]
16483 fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
16484 let mut f = labelled();
16485 let cases: &[(&[&[u8]], &str)] = &[
16486 (
16487 &[b"TS.QUERYLABELS", b"LABELS"],
16488 "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16489 ),
16490 (
16491 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16492 "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16493 ),
16494 (
16495 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16496 "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16497 ),
16498 (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16501 (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16502 (
16503 &[b"TS.QUERYLABELS", b"VALUES"],
16504 "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16505 ),
16506 (
16507 &[b"TS.QUERYLABELS", b"ZZZ"],
16508 "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16509 ),
16510 (
16511 &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16512 "-ERR TSDB: unknown argument, expected FILTER\r\n",
16513 ),
16514 (
16515 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16516 "-ERR TSDB: FILTER given with no filter expressions\r\n",
16517 ),
16518 (
16522 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16523 "-ERR TSDB: please provide at least one matcher\r\n",
16524 ),
16525 (
16526 &[
16527 b"TS.QUERYLABELS",
16528 b"LABELS",
16529 b"FILTER",
16530 b"room=kitchen",
16531 b"x=",
16532 ],
16533 "*1\r\n$4\r\nroom\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 mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16546 let mut f = labelled();
16547 let cases: &[(&[&[u8]], &str)] = &[
16548 (
16551 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16552 "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16553 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16554 ),
16555 (
16556 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16557 "*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\
16558 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16559 *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",
16560 ),
16561 (
16563 &[
16564 b"TS.MGET",
16565 b"SELECTED_LABELS",
16566 b"x",
16567 b"FILTER",
16568 b"room=kitchen",
16569 ],
16570 "*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\
16571 *2\r\n:100\r\n+1.5\r\n\
16572 *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",
16573 ),
16574 (
16577 &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16578 "*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",
16579 ),
16580 (
16581 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16582 "*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\
16583 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16584 ),
16585 (
16588 &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16589 "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16590 ),
16591 (
16592 &[b"TS.MGET", b"a", b"b", b"c"],
16593 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16594 ),
16595 (
16596 &[b"TS.MGET", b"FILTER"],
16597 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16598 ),
16599 (
16602 &[
16603 b"TS.MGET",
16604 b"WITHLABELS",
16605 b"SELECTED_LABELS",
16606 b"x",
16607 b"FILTER",
16608 b"bad",
16609 ],
16610 "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16611 ),
16612 (
16613 &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16614 "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16615 ),
16616 ];
16617 for (argv, want) in cases {
16618 let got = f.run(argv);
16619 assert_eq!(&got, want, "{:?}", argv.last());
16620 }
16621 }
16622
16623 #[test]
16626 fn resp3_writes_the_label_surface_as_sets_and_maps() {
16627 let mut f = labelled();
16628 f.out = Out::new(Proto::Resp3);
16629 let cases: &[(&[&[u8]], &str)] = &[
16630 (
16631 &[b"TS.QUERYINDEX", b"room=kitchen"],
16632 "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16633 ),
16634 (
16635 &[b"TS.QUERYLABELS", b"LABELS"],
16636 "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16637 ),
16638 (
16639 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16640 "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16641 ),
16642 (
16645 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16646 "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16647 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16648 ),
16649 (
16650 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16651 "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16652 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16653 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16654 ),
16655 (
16656 &[
16657 b"TS.MGET",
16658 b"SELECTED_LABELS",
16659 b"x",
16660 b"FILTER",
16661 b"room=kitchen",
16662 ],
16663 "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16664 *2\r\n:100\r\n,1.5\r\n\
16665 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16666 ),
16667 (
16670 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16671 "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16672 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16673 ),
16674 ];
16675 for (argv, want) in cases {
16676 let got = f.run(argv);
16677 assert_eq!(&got, want, "{:?}", argv.last());
16678 }
16679 }
16680
16681 fn spanned() -> Fixture {
16684 let mut f = labelled();
16685 f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16686 f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16687 f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16688 f
16689 }
16690
16691 #[test]
16694 fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16695 let mut f = spanned();
16696 let cases: &[(&[&[u8]], &str)] = &[
16697 (
16698 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16699 "*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\
16700 *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",
16701 ),
16702 (
16705 &[
16706 b"TS.MREVRANGE",
16707 b"-",
16708 b"+",
16709 b"WITHLABELS",
16710 b"FILTER",
16711 b"room=kitchen",
16712 ],
16713 "*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\
16714 *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\
16715 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16716 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16717 ),
16718 (
16721 &[
16722 b"TS.MRANGE",
16723 b"-",
16724 b"+",
16725 b"SELECTED_LABELS",
16726 b"x",
16727 b"FILTER",
16728 b"room=kitchen",
16729 ],
16730 "*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\
16731 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16732 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16733 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16734 ),
16735 (
16738 &[
16739 b"TS.MRANGE",
16740 b"-",
16741 b"+",
16742 b"FILTER",
16743 b"room=kitchen",
16744 b"GROUPBY",
16745 b"room",
16746 b"REDUCE",
16747 b"sum",
16748 ],
16749 "*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\
16750 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16751 ),
16752 (
16755 &[
16756 b"TS.MRANGE",
16757 b"-",
16758 b"+",
16759 b"WITHLABELS",
16760 b"FILTER",
16761 b"room=kitchen",
16762 b"GROUPBY",
16763 b"room",
16764 b"REDUCE",
16765 b"max",
16766 ],
16767 "*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\
16768 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16769 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16770 *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",
16771 ),
16772 (
16774 &[
16775 b"TS.MREVRANGE",
16776 b"-",
16777 b"+",
16778 b"COUNT",
16779 b"1",
16780 b"FILTER",
16781 b"room=kitchen",
16782 b"GROUPBY",
16783 b"room",
16784 b"REDUCE",
16785 b"count",
16786 ],
16787 "*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",
16788 ),
16789 (
16791 &[
16792 b"TS.MRANGE",
16793 b"-",
16794 b"+",
16795 b"FILTER",
16796 b"room=kitchen",
16797 b"GROUPBY",
16798 b"nope",
16799 b"REDUCE",
16800 b"sum",
16801 ],
16802 "*0\r\n",
16803 ),
16804 (
16805 &[
16806 b"TS.MRANGE",
16807 b"-",
16808 b"+",
16809 b"AGGREGATION",
16810 b"sum,avg",
16811 b"100",
16812 b"FILTER",
16813 b"room=bedroom",
16814 ],
16815 "*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",
16816 ),
16817 (
16819 &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16820 "-ERR TSDB: missing FILTER argument\r\n",
16821 ),
16822 (
16823 &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16824 "-ERR TSDB: missing labels for filter argument\r\n",
16825 ),
16826 (
16827 &[
16828 b"TS.MRANGE",
16829 b"-",
16830 b"+",
16831 b"GROUPBY",
16832 b"room",
16833 b"REDUCE",
16834 b"sum",
16835 b"FILTER",
16836 b"room=kitchen",
16837 ],
16838 "-ERR TSDB: GROUPBY should always come after filter\r\n",
16839 ),
16840 (
16843 &[
16844 b"TS.MRANGE",
16845 b"-",
16846 b"+",
16847 b"FILTER",
16848 b"room=kitchen",
16849 b"GROUPBY",
16850 b"room",
16851 b"REDUCE",
16852 b"sum",
16853 b"x",
16854 ],
16855 "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16856 ),
16857 (
16859 &[
16860 b"TS.MRANGE",
16861 b"-",
16862 b"+",
16863 b"FILTER",
16864 b"nope",
16865 b"GROUPBY",
16866 b"room",
16867 b"REDUCE",
16868 b"sum",
16869 b"x",
16870 ],
16871 "-ERR TSDB: failed parsing labels\r\n",
16872 ),
16873 (
16874 &[
16875 b"TS.MRANGE",
16876 b"-",
16877 b"+",
16878 b"FILTER",
16879 b"room=kitchen",
16880 b"GROUPBY",
16881 b"room",
16882 b"REDUCE",
16883 b"twa",
16884 ],
16885 "-ERR TSDB: Invalid reducer type\r\n",
16886 ),
16887 (
16888 &[
16889 b"TS.MRANGE",
16890 b"-",
16891 b"+",
16892 b"AGGREGATION",
16893 b"sum,avg",
16894 b"100",
16895 b"FILTER",
16896 b"room=kitchen",
16897 b"GROUPBY",
16898 b"room",
16899 b"REDUCE",
16900 b"sum",
16901 ],
16902 "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
16903 ),
16904 (
16907 &[
16908 b"TS.MRANGE",
16909 b"-",
16910 b"+",
16911 b"SELECTED_LABELS",
16912 b"COUNT",
16913 b"FILTER",
16914 b"room=kitchen",
16915 ],
16916 "-ERR TSDB: Couldn't parse COUNT\r\n",
16917 ),
16918 ];
16919 for (argv, want) in cases {
16920 let got = f.run(argv);
16921 assert_eq!(&got, want, "{argv:?}");
16922 }
16923 }
16924
16925 #[test]
16928 fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
16929 let mut f = spanned();
16930 f.out = Out::new(Proto::Resp3);
16931 let cases: &[(&[&[u8]], &str)] = &[
16932 (
16933 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
16934 "%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\
16935 *1\r\n*2\r\n:200\r\n,2\r\n",
16936 ),
16937 (
16940 &[
16941 b"TS.MRANGE",
16942 b"-",
16943 b"+",
16944 b"AGGREGATION",
16945 b"sum,avg",
16946 b"100",
16947 b"FILTER",
16948 b"room=bedroom",
16949 ],
16950 "%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\
16951 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
16952 ),
16953 (
16954 &[
16955 b"TS.MRANGE",
16956 b"-",
16957 b"+",
16958 b"FILTER",
16959 b"room=kitchen",
16960 b"GROUPBY",
16961 b"room",
16962 b"REDUCE",
16963 b"sum",
16964 ],
16965 "%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\
16966 $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\
16967 *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",
16968 ),
16969 (
16972 &[
16973 b"TS.MRANGE",
16974 b"-",
16975 b"+",
16976 b"WITHLABELS",
16977 b"FILTER",
16978 b"room=kitchen",
16979 b"GROUPBY",
16980 b"room",
16981 b"REDUCE",
16982 b"max",
16983 ],
16984 "%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\
16985 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
16986 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16987 *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",
16988 ),
16989 (
16990 &[
16991 b"TS.MRANGE",
16992 b"-",
16993 b"+",
16994 b"FILTER",
16995 b"room=kitchen",
16996 b"GROUPBY",
16997 b"nope",
16998 b"REDUCE",
16999 b"sum",
17000 ],
17001 "%0\r\n",
17002 ),
17003 ];
17004 for (argv, want) in cases {
17005 let got = f.run(argv);
17006 assert_eq!(&got, want, "{argv:?}");
17007 }
17008 }
17009
17010 #[test]
17012 fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
17013 let mut f = Fixture::new();
17014 f.run(&[b"TS.CREATE", b"src"]);
17015 f.run(&[b"TS.CREATE", b"dst"]);
17016 f.run(&[b"SET", b"plain", b"v"]);
17017 let cases: &[(&[&[u8]], &str)] = &[
17018 (
17022 &[
17023 b"TS.CREATERULE",
17024 b"src",
17025 b"dst",
17026 b"AGGREGATION",
17027 b"nope",
17028 b"x",
17029 ],
17030 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
17031 ),
17032 (
17033 &[
17034 b"TS.CREATERULE",
17035 b"src",
17036 b"dst",
17037 b"AGGREGATION",
17038 b"nope",
17039 b"10",
17040 ],
17041 "-ERR TSDB: Unknown aggregation type\r\n",
17042 ),
17043 (
17044 &[
17045 b"TS.CREATERULE",
17046 b"src",
17047 b"dst",
17048 b"AGGREGATION",
17049 b"avg",
17050 b"0",
17051 ],
17052 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
17053 ),
17054 (
17055 &[
17056 b"TS.CREATERULE",
17057 b"src",
17058 b"dst",
17059 b"AGGREGATION",
17060 b"avg",
17061 b"10",
17062 b"x",
17063 ],
17064 "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
17065 ),
17066 (
17067 &[
17068 b"TS.CREATERULE",
17069 b"src",
17070 b"src",
17071 b"AGGREGATION",
17072 b"avg",
17073 b"10",
17074 ],
17075 "-ERR TSDB: the source key and destination key should be different\r\n",
17076 ),
17077 (
17081 &[
17082 b"TS.CREATERULE",
17083 b"nope",
17084 b"plain",
17085 b"AGGREGATION",
17086 b"avg",
17087 b"10",
17088 ],
17089 "-ERR TSDB: the key does not exist\r\n",
17090 ),
17091 (
17092 &[
17093 b"TS.CREATERULE",
17094 b"src",
17095 b"nope",
17096 b"AGGREGATION",
17097 b"avg",
17098 b"10",
17099 ],
17100 "-ERR TSDB: the key does not exist\r\n",
17101 ),
17102 (
17105 &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
17106 "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
17107 ),
17108 (
17109 &[
17110 b"TS.CREATERULE",
17111 b"src",
17112 b"dst",
17113 b"AGGREGATION",
17114 b"avg",
17115 b"10",
17116 ],
17117 "+OK\r\n",
17118 ),
17119 (
17122 &[
17123 b"TS.CREATERULE",
17124 b"src",
17125 b"dst",
17126 b"AGGREGATION",
17127 b"avg",
17128 b"10",
17129 ],
17130 "-ERR TSDB: the destination key already has a src rule\r\n",
17131 ),
17132 (
17135 &[
17136 b"TS.CREATERULE",
17137 b"dst",
17138 b"src",
17139 b"AGGREGATION",
17140 b"avg",
17141 b"10",
17142 ],
17143 "-ERR TSDB: the source key already has a source rule\r\n",
17144 ),
17145 (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
17146 (
17147 &[b"TS.DELETERULE", b"src", b"dst"],
17148 "-ERR TSDB: compaction rule does not exist\r\n",
17149 ),
17150 (
17154 &[b"TS.DELETERULE", b"src", b"nope"],
17155 "-ERR TSDB: compaction rule does not exist\r\n",
17156 ),
17157 (
17158 &[b"TS.DELETERULE", b"nope", b"dst"],
17159 "-ERR TSDB: the key does not exist\r\n",
17160 ),
17161 ];
17162 for (argv, want) in cases {
17163 let got = f.run(argv);
17164 assert_eq!(&got, want, "{argv:?}");
17165 }
17166 }
17167
17168 #[test]
17170 fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
17171 let mut f = Fixture::new();
17172 f.run(&[b"TS.CREATE", b"src"]);
17173 f.run(&[b"TS.CREATE", b"dst"]);
17174 f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
17177 f.run(&[
17178 b"TS.CREATERULE",
17179 b"src",
17180 b"dst",
17181 b"AGGREGATION",
17182 b"sum",
17183 b"100",
17184 ]);
17185 f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
17186 assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
17187 assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
17190 f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
17191 assert_eq!(
17192 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17193 "*1\r\n*2\r\n:0\r\n+2\r\n"
17194 );
17195 f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
17198 assert_eq!(
17199 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17200 "*1\r\n*2\r\n:0\r\n+11\r\n"
17201 );
17202 assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
17205 assert_eq!(
17206 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17207 "*1\r\n*2\r\n:0\r\n+8\r\n"
17208 );
17209 assert_eq!(
17210 f.run(&[b"TS.GET", b"dst", b"LATEST"]),
17211 "*2\r\n:100\r\n+4\r\n"
17212 );
17213 assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
17215 f.run(&[b"DEL", b"dst"]);
17216 assert_eq!(
17217 f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
17218 "-ERR TSDB: compaction rule does not exist\r\n"
17219 );
17220 }
17221
17222 #[test]
17225 fn xadd_ids_only_ever_go_up() {
17226 let mut f = Fixture::new();
17227 assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
17229 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
17231 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
17232 assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
17233 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17234
17235 assert!(
17236 f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
17237 .contains("equal or smaller")
17238 );
17239 assert!(
17240 f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
17241 .contains("must be greater than 0-0")
17242 );
17243 assert!(
17244 f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
17245 .contains("Invalid stream ID")
17246 );
17247 assert!(
17250 f.run(&[b"XADD", b"s", b"*", b"a"])
17251 .contains("wrong number of arguments")
17252 );
17253
17254 assert_eq!(
17257 f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
17258 "$-1\r\n"
17259 );
17260 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
17261 assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
17262 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
17263 }
17264
17265 #[test]
17268 fn trimming_reads_its_options_the_way_redis_does() {
17269 let mut f = Fixture::new();
17270 for i in 1..=10u32 {
17271 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17272 }
17273 assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
17274 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17275 assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
17276 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17277
17278 assert!(
17282 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
17283 .contains("not an integer")
17284 );
17285 assert!(
17286 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
17287 .contains("MAXLEN argument must be >= 0")
17288 );
17289 assert!(
17292 f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
17293 .contains("without specifying a trimming strategy")
17294 );
17295 assert!(
17296 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
17297 .contains("without the special ~ option")
17298 );
17299 assert!(
17300 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
17301 .contains("at the same time are not compatible")
17302 );
17303 assert!(
17305 f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
17306 .contains("syntax error")
17307 );
17308 assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
17309 }
17310
17311 #[test]
17313 fn xrange_looks_the_key_up_before_it_reads_the_count() {
17314 let mut f = Fixture::new();
17315 f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
17316 f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
17317
17318 assert_eq!(
17319 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17320 "*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\
17321 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17322 );
17323 assert_eq!(
17324 f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
17325 "*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"
17326 );
17327 assert_eq!(
17331 f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
17332 "*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\
17333 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17334 );
17335 assert_eq!(
17336 f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
17337 "*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"
17338 );
17339 assert!(
17340 f.run(&[b"XRANGE", b"s", b"(-", b"+"])
17341 .contains("Invalid stream ID")
17342 );
17343
17344 assert_eq!(
17348 f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
17349 "*0\r\n"
17350 );
17351 assert_eq!(
17352 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
17353 "*-1\r\n"
17354 );
17355 f.run(&[b"SET", b"str", b"v"]);
17356 assert!(
17357 f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
17358 .starts_with("-WRONGTYPE")
17359 );
17360 assert_eq!(
17362 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
17363 "*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"
17364 );
17365 }
17366
17367 #[test]
17369 fn a_bad_id_late_in_the_list_stops_the_whole_command() {
17370 let mut f = Fixture::new();
17371 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17372 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17373 assert!(
17374 f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
17375 .contains("Invalid stream ID")
17376 );
17377 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17378 assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
17379 assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
17380 assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
17381 assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
17382 }
17383
17384 #[test]
17386 fn xgroup_has_an_arity_per_subcommand() {
17387 let mut f = Fixture::new();
17388 assert!(
17389 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17390 .contains("requires the key")
17391 );
17392 assert_eq!(
17393 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
17394 "+OK\r\n"
17395 );
17396 assert!(
17399 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17400 .starts_with("-BUSYGROUP")
17401 );
17402 assert_eq!(
17403 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17404 ":1\r\n"
17405 );
17406 assert_eq!(
17407 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17408 ":0\r\n"
17409 );
17410 assert_eq!(
17411 f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
17412 ":0\r\n"
17413 );
17414
17415 let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
17417 assert!(
17418 short.contains("wrong number of arguments for 'xgroup|destroy' command"),
17419 "{short}"
17420 );
17421 let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
17423 assert!(
17424 odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
17425 "{odd}"
17426 );
17427 assert!(
17428 f.run(&[b"XGROUP", b"NOSUCH", b"s"])
17429 .contains("Try XGROUP HELP")
17430 );
17431
17432 assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
17433 assert!(
17434 f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
17435 .starts_with("-NOGROUP")
17436 );
17437 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
17438 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
17439 assert!(
17440 f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
17441 .contains("requires the key")
17442 );
17443 }
17444
17445 #[test]
17447 fn xreadgroup_hands_out_and_xack_takes_back() {
17448 let mut f = Fixture::new();
17449 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17450 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17451 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17452
17453 let first = f.run(&[
17454 b"XREADGROUP",
17455 b"GROUP",
17456 b"g",
17457 b"c1",
17458 b"COUNT",
17459 b"1",
17460 b"STREAMS",
17461 b"s",
17462 b">",
17463 ]);
17464 assert_eq!(
17465 first,
17466 "*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"
17467 );
17468 assert_eq!(
17471 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
17472 "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
17473 );
17474 assert_eq!(
17475 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17476 "*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"
17477 );
17478
17479 assert_eq!(
17480 f.run(&[b"XPENDING", b"s", b"g"]),
17481 "*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"
17482 );
17483 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
17484 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
17485 assert_eq!(
17487 f.run(&[b"XPENDING", b"s", b"g"]),
17488 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
17489 );
17490
17491 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17494 f.run(&[b"XDEL", b"s", b"2-1"]);
17495 assert_eq!(
17496 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17497 "*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"
17498 );
17499
17500 assert!(
17503 f.run(&[
17504 b"XREADGROUP",
17505 b"GROUP",
17506 b"nope",
17507 b"c",
17508 b"STREAMS",
17509 b"s",
17510 b"+"
17511 ])
17512 .starts_with("-NOGROUP")
17513 );
17514 assert!(
17515 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17516 .contains("meaningless in the context of XREADGROUP")
17517 );
17518 assert!(
17519 f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17520 .contains("only supported by XREADGROUP")
17521 );
17522 assert!(
17523 f.run(&[
17524 b"XREADGROUP",
17525 b"GROUP",
17526 b"g",
17527 b"c",
17528 b"STREAMS",
17529 b"s",
17530 b"a",
17531 b"b"
17532 ])
17533 .contains("Unbalanced 'xreadgroup' list of streams")
17534 );
17535 }
17536
17537 #[test]
17540 fn xread_with_no_block_writes_the_null_itself() {
17541 let mut f = Fixture::new();
17542 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17543 assert_eq!(
17544 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17545 "*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"
17546 );
17547 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17550 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17551 f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17552 assert_eq!(
17553 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17554 "*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"
17555 );
17556 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17558 assert_eq!(
17560 f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17561 "*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"
17562 );
17563 assert_eq!(
17566 f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17567 "*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"
17568 );
17569 assert!(
17571 f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17572 .contains("not an integer")
17573 );
17574 assert!(
17575 f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17576 .contains("timeout is negative")
17577 );
17578 assert!(
17579 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17580 .contains("Unbalanced 'xread' list of streams")
17581 );
17582 }
17583
17584 #[test]
17586 fn a_blocked_xread_wakes_on_the_next_entry() {
17587 let mut f = Fixture::new();
17588 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17589 let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17590 assert_eq!(flow, Flow::Block);
17591 assert!(reply.is_empty());
17592
17593 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17596 assert_eq!(flow, Flow::Block);
17597 assert_eq!(f.server.parked(), 2);
17598
17599 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17600 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";
17601 for at in 0..2 {
17602 let mut out = Out::new(Proto::Resp2);
17603 assert!(f.server.serve_waiter(at, 0, &mut out));
17604 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17605 }
17606
17607 f.server.forget_waiters(7);
17610 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17611 assert_eq!(flow, Flow::Block);
17612 let mut out = Out::new(Proto::Resp2);
17613 assert!(!f.server.serve_waiter(0, 0, &mut out));
17614 assert!(out.as_slice().is_empty());
17615 assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
17616 assert_eq!(
17617 core::str::from_utf8(out.as_slice()).expect("ascii"),
17618 "*-1\r\n"
17619 );
17620 }
17621
17622 #[test]
17624 fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17625 let mut f = Fixture::new();
17626 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17627 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17628 let (flow, _) = f.flow(&[
17629 b"XREADGROUP",
17630 b"GROUP",
17631 b"g",
17632 b"c",
17633 b"BLOCK",
17634 b"0",
17635 b"STREAMS",
17636 b"s",
17637 b">",
17638 ]);
17639 assert_eq!(flow, Flow::Block);
17640
17641 f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17642 let mut out = Out::new(Proto::Resp2);
17643 assert!(f.server.serve_waiter(0, 0, &mut out));
17644 assert_eq!(
17647 core::str::from_utf8(out.as_slice()).expect("ascii"),
17648 "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17649 );
17650 }
17651
17652 #[test]
17654 fn xclaim_reads_ids_until_one_will_not_parse() {
17655 let mut f = Fixture::new();
17656 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17657 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17658 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17659 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17660
17661 assert!(
17664 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17665 .contains("Unrecognized XCLAIM option '-'")
17666 );
17667 assert_eq!(
17668 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17669 "*1\r\n$3\r\n1-1\r\n"
17670 );
17671 f.run(&[b"XDEL", b"s", b"2-1"]);
17674 assert_eq!(
17675 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17676 "*0\r\n"
17677 );
17678 assert!(
17679 f.run(&[b"XPENDING", b"s", b"g"])
17680 .starts_with("*4\r\n:1\r\n")
17681 );
17682 assert!(
17683 f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17684 .starts_with("-NOGROUP")
17685 );
17686 assert!(
17687 f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17688 .contains("Invalid min-idle-time argument for XCLAIM")
17689 );
17690 }
17691
17692 #[test]
17694 fn xautoclaim_reports_what_it_dropped() {
17695 let mut f = Fixture::new();
17696 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17697 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17698 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17699 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17700 f.run(&[b"XDEL", b"s", b"1-1"]);
17701
17702 assert_eq!(
17705 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17706 "*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"
17707 );
17708 assert!(
17709 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17710 .contains("COUNT must be > 0")
17711 );
17712 assert!(
17713 f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17714 .starts_with("-NOGROUP")
17715 );
17716 }
17717
17718 #[test]
17720 fn xdelex_answers_one_integer_an_id() {
17721 let mut f = Fixture::new();
17722 for i in 1..=4 {
17723 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17724 }
17725 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17726 f.run(&[
17727 b"XREADGROUP",
17728 b"GROUP",
17729 b"g",
17730 b"c",
17731 b"COUNT",
17732 b"2",
17733 b"STREAMS",
17734 b"s",
17735 b">",
17736 ]);
17737
17738 assert_eq!(
17740 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17741 "*2\r\n:1\r\n:-1\r\n"
17742 );
17743 assert!(
17746 f.run(&[b"XPENDING", b"s", b"g"])
17747 .starts_with("*4\r\n:2\r\n")
17748 );
17749 assert_eq!(
17751 f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17752 "*1\r\n:1\r\n"
17753 );
17754 assert_eq!(
17756 f.run(&[b"XPENDING", b"s", b"g"]),
17757 "*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"
17758 );
17759
17760 assert_eq!(
17764 f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17765 "*2\r\n:2\r\n:2\r\n"
17766 );
17767
17768 assert_eq!(
17770 f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17771 "*2\r\n:-1\r\n:-1\r\n"
17772 );
17773 assert!(
17775 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17776 .starts_with("-ERR Invalid stream ID")
17777 );
17778 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17779
17780 assert!(
17781 f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17782 .contains("Number of IDs must be a positive integer")
17783 );
17784 assert!(
17785 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17786 .contains("The `numids` parameter must match the number of arguments")
17787 );
17788 assert!(
17791 f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17792 .starts_with("-ERR syntax error")
17793 );
17794 assert!(
17795 f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17796 .starts_with("-ERR syntax error")
17797 );
17798 f.run(&[b"SET", b"str", b"v"]);
17800 assert!(
17801 f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17802 .starts_with("-WRONGTYPE")
17803 );
17804 }
17805
17806 #[test]
17808 fn xackdel_reports_what_the_group_was_holding() {
17809 let mut f = Fixture::new();
17810 for i in 1..=3 {
17811 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17812 }
17813 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17814 f.run(&[
17815 b"XREADGROUP",
17816 b"GROUP",
17817 b"g",
17818 b"c",
17819 b"COUNT",
17820 b"1",
17821 b"STREAMS",
17822 b"s",
17823 b">",
17824 ]);
17825
17826 assert_eq!(
17830 f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17831 "*2\r\n:1\r\n:-1\r\n"
17832 );
17833 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17834
17835 assert_eq!(
17837 f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17838 "*1\r\n:-1\r\n"
17839 );
17840 assert_eq!(
17841 f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17842 "*1\r\n:-1\r\n"
17843 );
17844
17845 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17848 f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17849 assert_eq!(
17850 f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17851 "*1\r\n:2\r\n"
17852 );
17853 assert_eq!(
17854 f.run(&[b"XPENDING", b"s", b"g"]),
17855 "*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"
17856 );
17857 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17858 }
17859
17860 #[test]
17862 fn xnack_releases_an_entry_for_the_next_claim() {
17863 let mut f = Fixture::new();
17864 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17865 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17866 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17867 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17868 f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17871
17872 assert_eq!(
17873 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
17874 ":1\r\n"
17875 );
17876 assert_eq!(
17880 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
17881 "*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"
17882 );
17883 assert_eq!(
17885 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17886 "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
17887 );
17888 assert_eq!(
17890 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17891 "*-1\r\n"
17892 );
17893 assert_eq!(
17895 f.run(&[
17896 b"XAUTOCLAIM",
17897 b"s",
17898 b"g",
17899 b"c2",
17900 b"99999999",
17901 b"-",
17902 b"JUSTID"
17903 ]),
17904 "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
17905 );
17906
17907 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17911 assert!(
17912 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17913 .contains(":-1\r\n:1\r\n")
17914 );
17915 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17917 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17918 assert!(
17919 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17920 .contains(":-1\r\n:0\r\n")
17921 );
17922 f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
17924 assert!(
17925 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17926 .contains(":9223372036854775807\r\n")
17927 );
17928 f.run(&[
17929 b"XNACK",
17930 b"s",
17931 b"g",
17932 b"FATAL",
17933 b"IDS",
17934 b"1",
17935 b"1-1",
17936 b"RETRYCOUNT",
17937 b"3",
17938 ]);
17939 assert!(
17940 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17941 .contains(":-1\r\n:3\r\n")
17942 );
17943
17944 f.run(&[b"XACK", b"s", b"g", b"2-1"]);
17948 assert_eq!(
17949 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
17950 ":0\r\n"
17951 );
17952 assert_eq!(
17953 f.run(&[
17954 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
17955 ]),
17956 ":1\r\n"
17957 );
17958 assert!(
17959 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17960 .contains(":-1\r\n:0\r\n")
17961 );
17962 assert_eq!(
17964 f.run(&[
17965 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
17966 ]),
17967 ":0\r\n"
17968 );
17969
17970 assert_eq!(
17973 f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
17974 "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
17975 );
17976 assert!(
17977 f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
17978 .starts_with("-ERR")
17979 );
17980 assert!(
17982 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
17983 .contains("numids must be a positive integer")
17984 );
17985 assert!(
17986 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
17987 .contains("number of IDs doesn't match numids")
17988 );
17989 assert!(
17992 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
17993 .contains("Unrecognized XNACK option '2-1'")
17994 );
17995 }
17996
17997 #[test]
17999 fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
18000 let mut f = Fixture::new();
18001 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18002 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
18003 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
18004 f.run(&[
18005 b"XREADGROUP",
18006 b"GROUP",
18007 b"g",
18008 b"c1",
18009 b"COUNT",
18010 b"1",
18011 b"STREAMS",
18012 b"s",
18013 b">",
18014 ]);
18015
18016 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
18017 assert!(info.starts_with("*20\r\n"), "{info}");
18020 assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
18021 assert!(
18022 info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
18023 "{info}"
18024 );
18025 assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
18026 assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
18027
18028 let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
18029 assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
18030 assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
18031 assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
18032 assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
18033
18034 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
18038 let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
18039 assert!(consumers.starts_with("*2\r\n"), "{consumers}");
18040 assert!(
18041 consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
18042 "{consumers}"
18043 );
18044 let c1 = consumers.find("c1").unwrap();
18046 let c2 = consumers.find("c2").unwrap();
18047 assert!(c1 < c2, "{consumers}");
18048
18049 let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
18050 assert!(full.starts_with("*18\r\n"), "{full}");
18051 assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
18052 assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
18053
18054 assert!(
18055 f.run(&[b"XINFO", b"STREAM", b"missing"])
18056 .contains("no such key")
18057 );
18058 assert!(
18059 f.run(&[b"XINFO", b"GROUPS", b"missing"])
18060 .contains("no such key")
18061 );
18062 assert!(
18063 f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
18064 .starts_with("-NOGROUP")
18065 );
18066 assert!(
18067 f.run(&[b"XINFO", b"NOSUCH", b"s"])
18068 .contains("Try XINFO HELP")
18069 );
18070 assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
18071 assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
18072 }
18073
18074 #[test]
18076 fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
18077 let mut f = Fixture::new();
18078 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18079 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
18080 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
18081
18082 let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
18083 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");
18084 assert_eq!(
18085 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
18086 "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
18087 );
18088 assert_eq!(
18090 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
18091 "*0\r\n"
18092 );
18093 assert_eq!(
18094 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
18095 list
18096 );
18097 assert!(
18099 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
18100 .contains("syntax error")
18101 );
18102 assert!(
18103 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
18104 .contains("syntax error")
18105 );
18106 assert_eq!(
18107 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
18108 "*0\r\n"
18109 );
18110 assert!(
18111 f.run(&[b"XPENDING", b"missing", b"g"])
18112 .starts_with("-NOGROUP")
18113 );
18114 }
18115
18116 #[test]
18118 fn xsetid_will_not_go_below_what_is_there() {
18119 let mut f = Fixture::new();
18120 f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
18121 assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
18122 assert_eq!(
18123 f.run(&[
18124 b"XSETID",
18125 b"s",
18126 b"10-1",
18127 b"ENTRIESADDED",
18128 b"7",
18129 b"MAXDELETEDID",
18130 b"9-1"
18131 ]),
18132 "+OK\r\n"
18133 );
18134 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
18135 assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
18136 assert!(
18137 info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
18138 "{info}"
18139 );
18140
18141 assert!(
18142 f.run(&[b"XSETID", b"s", b"1-1"])
18143 .contains("smaller than the target stream top item")
18144 );
18145 assert!(
18146 f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
18147 .contains("entries_added must be positive")
18148 );
18149 assert!(
18150 f.run(&[b"XSETID", b"missing", b"1-1"])
18151 .contains("no such key")
18152 );
18153 }
18154
18155 #[test]
18157 fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
18158 let mut f = Fixture::new();
18159 f.run(&[b"HELLO", b"3"]);
18160 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18161 assert_eq!(
18164 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
18165 "%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"
18166 );
18167 assert_eq!(
18170 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
18171 "*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"
18172 );
18173 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
18174 }
18175
18176 struct Mem {
18182 blobs: Vec<Vec<u8>>,
18183 }
18184
18185 impl yo_kv::cold::Blocks for Mem {
18186 fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
18187 self.blobs.push(bytes.to_vec());
18188 Ok(yo_common::Addr::new(
18189 yo_common::Space::Log,
18190 (self.blobs.len() - 1) as u64,
18191 ))
18192 }
18193
18194 fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
18195 self.blobs
18196 .get(at.offset() as usize)
18197 .map(Vec::as_slice)
18198 .ok_or_else(|| {
18199 yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
18200 })
18201 }
18202
18203 fn bytes(&self) -> u64 {
18204 self.blobs.iter().map(|b| b.len() as u64).sum()
18205 }
18206 }
18207
18208 fn filled(attach: bool) -> (Fixture, usize) {
18212 let mut f = Fixture::new();
18213 if attach {
18214 f.server
18215 .striped(0)
18216 .hold_stripe(0)
18217 .attach(Box::new(Mem { blobs: Vec::new() }));
18218 }
18219 let val = vec![b'v'; 256];
18220 for i in 0..24000u32 {
18221 let k = format!("key:{i:08}");
18222 f.run(&[b"SET", k.as_bytes(), &val]);
18223 }
18224 let full = f.server.memory_bytes();
18225 assert!(full > 3 * 1024 * 1024, "the arena is several segments");
18226 (f, full)
18227 }
18228
18229 fn press(f: &mut Fixture, limit: usize) {
18235 let val = vec![b'v'; 256];
18236 for i in 0..3000u32 {
18237 let k = format!("new:{i:08}");
18238 assert_eq!(
18239 f.run(&[b"SET", k.as_bytes(), &val]),
18240 "+OK\r\n",
18241 "write {i} was refused"
18242 );
18243 f.server.refresh_memory();
18244 if f.server.memory_bytes() <= limit {
18245 return;
18246 }
18247 }
18248 panic!(
18249 "it never got under: {} against {limit}",
18250 f.server.memory_bytes()
18251 );
18252 }
18253
18254 #[test]
18255 fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
18256 let mut f = Fixture::new();
18257 assert_eq!(
18258 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18259 "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
18260 "no limit is the default"
18261 );
18262 for (typed, bytes) in [
18265 (&b"0"[..], "0"),
18266 (b"1024", "1024"),
18267 (b"1k", "1000"),
18268 (b"1gb", "1073741824"),
18269 (b"-1", "-1"),
18270 ] {
18271 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
18272 assert_eq!(
18273 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18274 format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
18275 "set {}",
18276 String::from_utf8_lossy(typed)
18277 );
18278 }
18279 for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
18280 assert_eq!(
18281 f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
18282 "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
18283 "refused {}",
18284 String::from_utf8_lossy(bad)
18285 );
18286 }
18287 let info = f.run(&[b"INFO", b"memory"]);
18289 assert!(info.contains("maxstore:-1"), "{info}");
18290 assert!(info.contains("yo_memory_regime:evict"), "{info}");
18291 assert!(info.contains("yo_store_bytes:0"), "{info}");
18292 }
18293
18294 #[test]
18295 fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
18296 let (mut f, full) = filled(true);
18300 let keys = f.run(&[b"DBSIZE"]);
18301 assert!(
18302 f.run(&[b"INFO", b"memory"])
18303 .contains("yo_memory_regime:migrate"),
18304 "a database with somewhere to put values migrates"
18305 );
18306
18307 let limit = full - 2 * 1024 * 1024;
18308 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18309 f.run(&[
18310 b"CONFIG",
18311 b"SET",
18312 b"maxmemory",
18313 limit.to_string().as_bytes(),
18314 ]);
18315 press(&mut f, limit);
18316
18317 assert!(
18318 f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18319 "nothing was thrown away"
18320 );
18321 let after: usize = f.run(&[b"DBSIZE"])[1..]
18322 .trim_end()
18323 .parse()
18324 .expect("a count");
18325 let before: usize = keys[1..].trim_end().parse().expect("a count");
18326 assert!(after > before, "the keys that came in are all still here");
18327 assert!(
18328 f.server.store_bytes() > 0,
18329 "and what came out of memory went to the file"
18330 );
18331 let val = format!("$256\r\n{}\r\n", "v".repeat(256));
18334 assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
18335 assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
18336 }
18337
18338 #[test]
18339 fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
18340 let (mut f, full) = filled(true);
18344 f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
18345 assert!(
18346 f.run(&[b"INFO", b"memory"])
18347 .contains("yo_memory_regime:evict"),
18348 "nothing may go to the file"
18349 );
18350
18351 let limit = full - 2 * 1024 * 1024;
18352 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18353 f.run(&[
18354 b"CONFIG",
18355 b"SET",
18356 b"maxmemory",
18357 limit.to_string().as_bytes(),
18358 ]);
18359 press(&mut f, limit);
18360
18361 assert!(
18362 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18363 "keys were thrown away, which is what was asked for"
18364 );
18365 assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
18366 }
18367
18368 #[test]
18369 fn a_full_file_goes_back_to_evicting() {
18370 let (mut f, full) = filled(true);
18374 f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
18375 let limit = full - 2 * 1024 * 1024;
18376 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18377 f.run(&[
18378 b"CONFIG",
18379 b"SET",
18380 b"maxmemory",
18381 limit.to_string().as_bytes(),
18382 ]);
18383 press(&mut f, limit);
18384
18385 assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
18386 assert!(
18387 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18388 "and then it started evicting"
18389 );
18390 assert!(
18391 f.run(&[b"INFO", b"memory"])
18392 .contains("yo_memory_regime:evict"),
18393 "and it says so"
18394 );
18395 }
18396 #[test]
18412 fn the_string_group_answers_the_same_however_many_stripes_there_are() {
18413 let script: &[&[&[u8]]] = &[
18414 &[b"SET", b"k1", b"v1"],
18417 &[b"SET", b"k2", b"v2"],
18418 &[b"GET", b"k1"],
18419 &[b"GET", b"nothing"],
18420 &[b"GETSET", b"k1", b"v1b"],
18421 &[b"SETNX", b"k1", b"no"],
18422 &[b"SETNX", b"k3", b"yes"],
18423 &[b"APPEND", b"k3", b"!"],
18424 &[b"STRLEN", b"k3"],
18425 &[b"SETRANGE", b"k3", b"1", b"XY"],
18426 &[b"GETRANGE", b"k3", b"0", b"-1"],
18427 &[b"INCR", b"n1"],
18428 &[b"INCRBY", b"n1", b"41"],
18429 &[b"DECRBY", b"n1", b"2"],
18430 &[b"INCRBYFLOAT", b"f1", b"1.5"],
18431 &[b"SETEX", b"e1", b"100", b"v"],
18432 &[b"PSETEX", b"e2", b"100000", b"v"],
18433 &[b"GETEX", b"e1", b"PERSIST"],
18434 &[b"GETDEL", b"k2"],
18435 &[b"GET", b"k2"],
18436 &[b"DIGEST", b"k1"],
18437 &[b"DELEX", b"k3"],
18438 &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
18441 &[b"MGET", b"a", b"b", b"c", b"missing"],
18442 &[b"MSETNX", b"d", b"4", b"e", b"5"],
18443 &[b"MSETNX", b"e", b"6", b"f", b"7"],
18444 &[b"MGET", b"d", b"e", b"f"],
18445 &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
18446 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
18447 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
18448 &[b"MGET", b"g", b"h"],
18449 &[b"SET", b"s1", b"ohmytext"],
18450 &[b"SET", b"s2", b"mynewtext"],
18451 &[b"LCS", b"s1", b"s2"],
18452 &[b"LCS", b"s1", b"s2", b"LEN"],
18453 &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
18454 &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
18455 &[b"LCS", b"s1", b"gone"],
18456 &[b"MSET", b"odd"],
18458 &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
18459 &[b"MGET"],
18460 ];
18461
18462 let mut one = Fixture::new();
18463 let mut many = Fixture::striped(8);
18464 for parts in script {
18465 let a = one.run(parts);
18466 let b = many.run(parts);
18467 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18468 }
18469 }
18470
18471 #[test]
18476 fn a_striped_database_spreads_the_keys_it_is_given() {
18477 let mut f = Fixture::striped(8);
18478 for i in 0..256 {
18479 let key = format!("key:{i}");
18480 f.run(&[b"SET", key.as_bytes(), b"v"]);
18481 }
18482 assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
18483 }
18484
18485 #[test]
18488 fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
18489 let mut one = Fixture::new();
18490 let mut many = Fixture::striped(8);
18491 for f in [&mut one, &mut many] {
18492 f.run(&[b"SET", b"str", b"v"]);
18493 f.server
18499 .striped(0)
18500 .hold(b"list")
18501 .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18502 .expect("a new list");
18503 }
18504 assert_eq!(
18505 one.run(&[b"MGET", b"str", b"list", b"gone"]),
18506 many.run(&[b"MGET", b"str", b"list", b"gone"])
18507 );
18508 }
18509
18510 #[test]
18518 fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18519 let script: &[&[&[u8]]] = &[
18520 &[b"SET", b"k1", b"v1"],
18521 &[b"SET", b"k2", b"v2"],
18522 &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18523 &[b"TYPE", b"k1"],
18524 &[b"TYPE", b"gone"],
18525 &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18526 &[b"EXPIRE", b"k1", b"100"],
18527 &[b"TTL", b"k1"],
18528 &[b"EXPIRE", b"k1", b"200", b"NX"],
18529 &[b"PERSIST", b"k1"],
18530 &[b"TTL", b"k1"],
18531 &[b"PEXPIREAT", b"k2", b"1900000000000"],
18532 &[b"EXPIRETIME", b"k2"],
18533 &[b"PEXPIRETIME", b"k2"],
18534 &[b"PERSIST", b"k2"],
18535 &[b"OBJECT", b"ENCODING", b"k1"],
18536 &[b"OBJECT", b"REFCOUNT", b"k1"],
18537 &[b"OBJECT", b"IDLETIME", b"k1"],
18538 &[b"OBJECT", b"FREQ", b"k1"],
18539 &[b"OBJECT", b"ENCODING", b"gone"],
18540 &[b"OBJECT", b"HELP"],
18541 &[b"RENAME", b"k1", b"k9"],
18542 &[b"GET", b"k9"],
18543 &[b"RENAME", b"gone", b"x"],
18544 &[b"RENAMENX", b"k9", b"k2"],
18545 &[b"RENAMENX", b"k9", b"k8"],
18546 &[b"GET", b"k8"],
18547 &[b"COPY", b"k8", b"c1"],
18548 &[b"COPY", b"k8", b"c1"],
18549 &[b"COPY", b"k8", b"c1", b"REPLACE"],
18550 &[b"COPY", b"k8", b"k8"],
18551 &[b"COPY", b"gone", b"c2"],
18552 &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18553 &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18554 &[b"MOVE", b"c1", b"1"],
18555 &[b"MOVE", b"c1", b"1"],
18556 &[b"MOVE", b"k8", b"0"],
18557 &[b"DEL", b"k2", b"gone"],
18558 &[b"UNLINK", b"k8", b"k8"],
18559 &[b"DBSIZE"],
18560 ];
18561
18562 let mut one = Fixture::new();
18563 let mut many = Fixture::striped(8);
18564 for parts in script {
18565 let a = one.run(parts);
18566 let b = many.run(parts);
18567 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18568 }
18569
18570 for f in [&mut one, &mut many] {
18576 f.run(&[b"SET", b"d1", b"payload"]);
18577 let payload = f
18578 .server
18579 .striped(0)
18580 .hold(b"d1")
18581 .dump(b"d1")
18582 .expect("a key that is there");
18583 assert!(
18584 f.run(&[b"DUMP", b"d1"])
18585 .starts_with(&format!("${}", payload.len())),
18586 "a payload of the length the store gave"
18587 );
18588 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18589 assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18590 assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18591 assert_eq!(
18592 f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18593 "-BUSYKEY Target key name already exists.\r\n"
18594 );
18595 assert_eq!(
18596 f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18597 "-ERR DUMP payload version or checksum are wrong\r\n"
18598 );
18599 }
18600 }
18601
18602 #[test]
18609 fn a_scan_of_a_striped_database_walks_all_of_it() {
18610 let mut f = Fixture::striped(8);
18611 for i in 0..500 {
18612 let key = format!("key:{i}");
18613 f.run(&[b"SET", key.as_bytes(), b"v"]);
18614 }
18615
18616 let mut seen = Vec::new();
18617 let mut cursor = "0".to_owned();
18618 let mut calls = 0;
18619 loop {
18620 let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18621 let (next, keys) = scan_reply(&reply);
18622 seen.extend(keys);
18623 cursor = next;
18624 calls += 1;
18625 assert!(calls < 5_000, "a scan that will not finish");
18626 if cursor == "0" {
18627 break;
18628 }
18629 }
18630 seen.sort();
18631 assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18632 assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18633
18634 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18638 let (_, keys) = scan_reply(&reply);
18639 assert_eq!(keys.len(), 10, "key:40 through key:49");
18640 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18641 let (_, keys) = scan_reply(&reply);
18642 assert!(keys.is_empty(), "nothing here is a list");
18643 }
18644
18645 #[test]
18651 fn a_random_key_can_come_from_any_stripe() {
18652 let mut f = Fixture::striped(8);
18653 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18654 for i in 0..200 {
18655 let key = format!("key:{i}");
18656 f.run(&[b"SET", key.as_bytes(), b"v"]);
18657 }
18658 let mut homes = std::collections::HashSet::new();
18659 for _ in 0..200 {
18660 let got = f.run(&[b"RANDOMKEY"]);
18661 let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18662 assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18663 homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18664 }
18665 assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18666 }
18667
18668 fn apart(f: &mut Fixture, src: &str) -> String {
18672 let home = f.server.striped(0).stripe_of(src.as_bytes());
18673 for i in 0..1_000 {
18674 let dst = format!("dst:{i}");
18675 if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18676 return dst;
18677 }
18678 }
18679 panic!("eight stripes and a thousand keys all landed in one place");
18680 }
18681
18682 #[test]
18685 fn a_rename_across_stripes_takes_everything_with_it() {
18686 let mut f = Fixture::striped(8);
18687 let dst = apart(&mut f, "src");
18688 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18689
18690 f.run(&[b"SET", src, b"v"]);
18691 f.run(&[b"EXPIRE", src, b"100"]);
18692 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18693 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18694 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18695 assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18696
18697 f.server
18702 .striped(0)
18703 .hold(src)
18704 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18705 .expect("a new list");
18706 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18707 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18708 assert_eq!(
18709 f.server.striped(0).hold(dst).llen(dst).expect("a list"),
18710 2,
18711 "the members are on the stripe the key moved to"
18712 );
18713
18714 f.run(&[b"SET", src, b"v"]);
18717 assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18718 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18719 assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18720 }
18721
18722 #[test]
18724 fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18725 let mut f = Fixture::striped(8);
18726 let dst = apart(&mut f, "src");
18727 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18728
18729 f.run(&[b"SET", src, b"v"]);
18730 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18731 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18732 assert_eq!(
18733 f.run(&[b"COPY", src, dst]),
18734 ":0\r\n",
18735 "the destination is taken"
18736 );
18737 f.run(&[b"SET", src, b"w"]);
18738 assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18739 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18740
18741 f.run(&[b"DEL", src, dst]);
18745 f.server
18746 .striped(0)
18747 .hold(src)
18748 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18749 .expect("a new list");
18750 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18751 f.server
18752 .striped(0)
18753 .hold(src)
18754 .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18755 .expect("a list that is there");
18756 assert_eq!(f.server.striped(0).hold(src).llen(src).expect("a list"), 3);
18757 assert_eq!(f.server.striped(0).hold(dst).llen(dst).expect("a list"), 2);
18758 }
18759
18760 #[test]
18768 fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18769 let script: &[&[&[u8]]] = &[
18770 &[b"SET", b"k1", b"foobar"],
18771 &[b"SETBIT", b"b1", b"7", b"1"],
18772 &[b"SETBIT", b"b1", b"7", b"0"],
18773 &[b"GETBIT", b"k1", b"6"],
18774 &[b"GETBIT", b"k1", b"100"],
18775 &[b"BITCOUNT", b"k1"],
18776 &[b"BITCOUNT", b"k1", b"0", b"0"],
18777 &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18778 &[b"BITPOS", b"k1", b"1"],
18779 &[b"BITPOS", b"k1", b"0", b"2"],
18780 &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18781 &[
18782 b"BITFIELD",
18783 b"bf",
18784 b"SET",
18785 b"u8",
18786 b"0",
18787 b"255",
18788 b"GET",
18789 b"u8",
18790 b"0",
18791 ],
18792 &[
18793 b"BITFIELD",
18794 b"bf",
18795 b"OVERFLOW",
18796 b"SAT",
18797 b"INCRBY",
18798 b"u8",
18799 b"0",
18800 b"10",
18801 ],
18802 &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18803 &[b"SET", b"s1", b"abc"],
18806 &[b"SET", b"s2", b"abd"],
18807 &[b"SET", b"s3", b"a"],
18808 &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18809 &[b"GET", b"d1"],
18810 &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18811 &[b"GET", b"d2"],
18812 &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18813 &[b"STRLEN", b"d3"],
18814 &[b"BITOP", b"NOT", b"d4", b"s1"],
18815 &[b"STRLEN", b"d4"],
18816 &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18817 &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18818 &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18819 &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18820 &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18823 &[b"EXISTS", b"d1"],
18824 &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18825 &[b"GET", b"d9"],
18826 &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18830 &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18831 &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18832 &[b"BITOP", b"DIFF", b"d1", b"s1"],
18833 &[b"BITOP", b"NOPE", b"d1", b"s1"],
18834 &[b"BITCOUNT", b"list"],
18835 &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18836 ];
18837
18838 let mut one = Fixture::new();
18839 let mut many = Fixture::striped(8);
18840 for f in [&mut one, &mut many] {
18841 plant_list(f, b"list");
18842 }
18843 for parts in script {
18844 let a = one.run(parts);
18845 let b = many.run(parts);
18846 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18847 }
18848 }
18849
18850 fn plant_list(f: &mut Fixture, key: &[u8]) {
18856 f.server
18857 .striped(0)
18858 .hold(key)
18859 .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18860 .expect("a new list");
18861 }
18862
18863 #[test]
18870 fn a_bitop_across_stripes_reads_every_source() {
18871 let mut f = Fixture::striped(8);
18872 let other = apart(&mut f, "src");
18873 let (src, far) = (b"src".as_slice(), other.as_bytes());
18874 assert_ne!(
18875 f.server.striped(0).stripe_of(src),
18876 f.server.striped(0).stripe_of(far),
18877 "the two keys are the point of the test"
18878 );
18879
18880 f.run(&[b"SET", src, b"abc"]);
18881 f.run(&[b"SET", far, b"abd"]);
18882 assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
18883 assert_eq!(
18884 f.run(&[b"GET", far]),
18885 "$3\r\nab`\r\n",
18886 "a destination that is also a source"
18887 );
18888 f.run(&[b"SET", far, b"abd"]);
18889 assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
18890 assert_eq!(
18891 f.run(&[b"GET", src]),
18892 "$3\r\n\0\0\x07\r\n",
18893 "and the other way round"
18894 );
18895
18896 f.run(&[b"SET", src, b"abc"]);
18900 f.run(&[b"DEL", far]);
18901 assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
18902 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
18903 f.run(&[b"SET", src, b"abc"]);
18904 f.run(&[b"DEL", far]);
18905 plant_list(&mut f, far);
18906 assert_eq!(
18907 f.run(&[b"BITOP", b"OR", b"out", src, far]),
18908 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18909 );
18910 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
18911 }
18912
18913 #[test]
18915 fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
18916 let script: &[&[&[u8]]] = &[
18917 &[b"PFADD", b"h1", b"a", b"b", b"c"],
18918 &[b"PFADD", b"h1", b"a"],
18919 &[b"PFADD", b"h2"],
18920 &[b"PFADD", b"h2", b"c", b"d", b"e"],
18921 &[b"PFCOUNT", b"h1"],
18922 &[b"PFCOUNT", b"h2"],
18923 &[b"PFCOUNT", b"missing"],
18924 &[b"PFCOUNT", b"h1", b"h2"],
18926 &[b"PFCOUNT", b"h1", b"missing"],
18927 &[b"PFMERGE", b"m", b"h1", b"h2"],
18928 &[b"PFCOUNT", b"m"],
18929 &[b"STRLEN", b"m"],
18930 &[b"PFMERGE", b"m"],
18931 &[b"PFCOUNT", b"m"],
18932 &[b"PFMERGE", b"m2", b"missing"],
18933 &[b"PFCOUNT", b"m2"],
18934 &[b"PFDEBUG", b"ENCODING", b"h1"],
18937 &[b"PFDEBUG", b"DECODE", b"h1"],
18938 &[b"PFDEBUG", b"TODENSE", b"h1"],
18939 &[b"PFDEBUG", b"ENCODING", b"h1"],
18940 &[b"PFDEBUG", b"TODENSE", b"h1"],
18941 &[b"PFCOUNT", b"h1", b"h2"],
18942 &[b"PFSELFTEST"],
18943 &[b"SET", b"plain", b"not a sketch at all"],
18945 &[b"PFADD", b"plain", b"a"],
18946 &[b"PFCOUNT", b"plain"],
18947 &[b"PFCOUNT", b"h1", b"plain"],
18948 &[b"PFMERGE", b"plain", b"h1"],
18949 &[b"PFMERGE", b"m", b"plain"],
18950 &[b"PFDEBUG", b"ENCODING", b"gone"],
18951 &[b"PFDEBUG", b"NOPE", b"h1"],
18952 ];
18953
18954 let mut one = Fixture::new();
18955 let mut many = Fixture::striped(8);
18956 for parts in script {
18957 let a = one.run(parts);
18958 let b = many.run(parts);
18959 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18960 }
18961 }
18962
18963 #[test]
18972 fn the_set_group_answers_the_same_however_many_stripes_there_are() {
18973 const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
18974 let script: &[&[&[u8]]] = &[
18975 &[b"SADD", b"s1", b"a", b"b", b"c"],
18976 &[b"SADD", b"s1", b"a"],
18977 &[b"SADD", b"s2", b"b", b"c", b"d"],
18978 &[b"SADD", b"ints", b"1", b"2", b"3"],
18979 &[b"SCARD", b"s1"],
18980 &[b"SISMEMBER", b"s1", b"a"],
18981 &[b"SISMEMBER", b"s1", b"z"],
18982 &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
18983 &[b"SMEMBERS", b"s1"],
18984 &[b"SREM", b"s1", b"c"],
18985 &[b"SADD", b"s1", b"c"],
18986 &[b"SSCAN", b"s1", b"0"],
18987 &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
18988 &[b"SADD", b"one", b"m"],
18991 &[b"SRANDMEMBER", b"one"],
18992 &[b"SRANDMEMBER", b"one", b"-3"],
18993 &[b"SRANDMEMBER", b"gone"],
18994 &[b"SPOP", b"one"],
18995 &[b"SPOP", b"one"],
18996 &[b"SPOP", b"gone", b"2"],
18997 &[b"SMOVE", b"s1", b"s2", b"a"],
18999 &[b"SMOVE", b"s1", b"s2", b"zzz"],
19000 &[b"SMOVE", b"gone", b"s2", b"a"],
19001 &[b"SMEMBERS", b"s1"],
19002 &[b"SMEMBERS", b"s2"],
19003 &[b"SINTER", b"s1", b"s2"],
19005 &[b"SUNION", b"s1", b"s2"],
19006 &[b"SDIFF", b"s2", b"s1"],
19007 &[b"SINTER", b"s1", b"gone"],
19008 &[b"SUNION", b"s1", b"gone"],
19009 &[b"SDIFF", b"gone", b"s1"],
19010 &[b"SINTER", b"ints", b"s1"],
19011 &[b"SINTERCARD", b"2", b"s1", b"s2"],
19012 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
19013 &[b"SUNIONCARD", b"2", b"s1", b"s2"],
19014 &[b"SDIFFCARD", b"2", b"s2", b"s1"],
19015 &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
19016 &[b"SMEMBERS", b"d1"],
19017 &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
19018 &[b"SCARD", b"d2"],
19019 &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
19020 &[b"SCARD", b"d3"],
19021 &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
19024 &[b"EXISTS", b"d4"],
19025 &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
19027 &[b"SCARD", b"s2"],
19028 &[b"SET", b"str", b"v"],
19030 &[b"SADD", b"str", b"a"],
19031 &[b"SINTER", b"s1", b"str"],
19032 &[b"SINTERSTORE", b"d5", b"s1", b"str"],
19033 &[b"EXISTS", b"d5"],
19034 &[b"SMOVE", b"str", b"s2", b"a"],
19035 &[b"SMOVE", b"s1", b"str", b"b"],
19036 &[b"SMOVE", b"gone", b"str", b"b"],
19037 &[b"SINTERCARD", b"0", b"s1"],
19038 &[b"SINTERCARD", b"3", b"s1", b"s2"],
19039 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
19040 &[b"SPOP", b"s1", b"-1"],
19041 ];
19042
19043 let mut one = Fixture::new();
19044 let mut many = Fixture::striped(8);
19045 for parts in script {
19046 let a = one.run(parts);
19047 let b = many.run(parts);
19048 let name = String::from_utf8_lossy(parts[0]).to_uppercase();
19049 if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
19050 assert_eq!(sorted(&a), sorted(&b), "{name}");
19051 } else {
19052 assert_eq!(a, b, "{name}");
19053 }
19054 }
19055 }
19056
19057 #[test]
19059 fn a_set_operation_across_stripes_reads_every_set() {
19060 let mut f = Fixture::striped(8);
19061 let second = apart(&mut f, "s1");
19062 let third = apart(&mut f, &second);
19063 let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
19064
19065 f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
19066 f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
19067 assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
19068 assert_eq!(
19069 sorted(&f.run(&[b"SUNION", s1, s2])),
19070 ["a", "b", "c", "d"],
19071 "a union of two stripes is both of them"
19072 );
19073 assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
19074 assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
19075 assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
19076 assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
19077
19078 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
19080 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
19081 assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
19082 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
19083 assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
19084 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
19085
19086 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
19089 assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
19090 f.run(&[b"SET", s3, b"v"]);
19091 assert_eq!(
19092 f.run(&[b"SINTER", s1, s3]),
19093 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19094 );
19095 assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
19096 }
19097
19098 #[test]
19100 fn a_move_across_stripes_takes_the_member_with_it() {
19101 let mut f = Fixture::striped(8);
19102 let other = apart(&mut f, "src");
19103 let (src, dst) = (b"src".as_slice(), other.as_bytes());
19104
19105 f.run(&[b"SADD", src, b"a", b"b"]);
19106 f.run(&[b"SADD", dst, b"c"]);
19107 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
19108 assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
19109 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
19110 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
19111
19112 f.run(&[b"DEL", dst]);
19115 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
19116 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
19117 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
19118
19119 f.run(&[b"SET", dst, b"v"]);
19123 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
19124 f.run(&[b"SADD", src, b"b"]);
19125 assert_eq!(
19126 f.run(&[b"SMOVE", src, dst, b"b"]),
19127 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19128 );
19129 }
19130
19131 #[test]
19133 fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
19134 let mut f = Fixture::striped(8);
19135 let other = apart(&mut f, "src");
19136 let (src, far) = (b"src".as_slice(), other.as_bytes());
19137
19138 for i in 0..150 {
19139 let ele = format!("e:{i}");
19140 f.run(&[b"PFADD", src, ele.as_bytes()]);
19141 }
19142 for i in 150..200 {
19143 let ele = format!("e:{i}");
19144 f.run(&[b"PFADD", far, ele.as_bytes()]);
19145 }
19146 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
19149 assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
19150 assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
19151
19152 let dest = apart(&mut f, &other);
19155 assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
19156 assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
19157 assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
19158 assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
19159 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
19160 }
19161
19162 #[test]
19169 fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
19170 let script: &[&[&[u8]]] = &[
19171 &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
19172 &[b"ZADD", b"z1", b"NX", b"9", b"a"],
19173 &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
19174 &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
19175 &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
19176 &[b"ZINCRBY", b"z1", b"1.5", b"b"],
19177 &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
19178 &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
19179 &[b"ZADD", b"one", b"1", b"m"],
19180 &[b"ZCARD", b"z1"],
19181 &[b"ZCARD", b"gone"],
19182 &[b"ZSCORE", b"z1", b"a"],
19183 &[b"ZSCORE", b"z1", b"zz"],
19184 &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
19185 &[b"ZRANK", b"z1", b"c"],
19186 &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
19187 &[b"ZREVRANK", b"z1", b"c"],
19188 &[b"ZRANK", b"z1", b"gone"],
19189 &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
19190 &[b"ZCOUNT", b"z1", b"(1", b"3"],
19191 &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
19192 &[b"ZRANGE", b"z1", b"0", b"-1"],
19194 &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
19195 &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
19196 &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
19197 &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
19198 &[b"ZREVRANGE", b"z1", b"0", b"-1"],
19199 &[
19200 b"ZRANGEBYSCORE",
19201 b"z1",
19202 b"-inf",
19203 b"+inf",
19204 b"LIMIT",
19205 b"1",
19206 b"1",
19207 ],
19208 &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
19209 &[b"ZSCAN", b"z1", b"0"],
19210 &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
19211 &[b"ZRANDMEMBER", b"one"],
19214 &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
19215 &[b"ZRANDMEMBER", b"gone"],
19216 &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
19218 &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
19219 &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
19220 &[b"EXISTS", b"d0"],
19221 &[b"ZUNION", b"2", b"z1", b"z2"],
19223 &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
19224 &[
19225 b"ZUNION",
19226 b"2",
19227 b"z1",
19228 b"z2",
19229 b"WEIGHTS",
19230 b"2",
19231 b"3",
19232 b"AGGREGATE",
19233 b"MAX",
19234 b"WITHSCORES",
19235 ],
19236 &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
19237 &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
19238 &[b"ZDIFF", b"2", b"gone", b"z1"],
19239 &[b"ZINTERCARD", b"2", b"z1", b"z2"],
19240 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
19241 &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
19242 &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
19243 &[
19244 b"ZINTERSTORE",
19245 b"d2",
19246 b"2",
19247 b"z1",
19248 b"z2",
19249 b"AGGREGATE",
19250 b"MIN",
19251 ],
19252 &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
19253 &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
19254 &[b"ZCARD", b"d3"],
19255 &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
19258 &[b"EXISTS", b"d4"],
19259 &[b"SADD", b"plain", b"a", b"x"],
19262 &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
19263 &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
19264 &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
19266 &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
19267 &[b"ZREM", b"d5", b"x", b"nothere"],
19269 &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
19270 &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
19271 &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
19272 &[b"ZPOPMIN", b"z1"],
19273 &[b"ZPOPMAX", b"z1", b"2"],
19274 &[b"ZPOPMIN", b"gone"],
19275 &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
19276 &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
19277 &[b"SET", b"str", b"v"],
19279 &[b"ZADD", b"str", b"1", b"a"],
19280 &[b"ZSCORE", b"str", b"a"],
19281 &[b"ZADD", b"z1", b"nan", b"a"],
19282 &[b"ZUNION", b"2", b"z1", b"str"],
19283 &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
19284 &[b"EXISTS", b"d6"],
19285 &[b"ZINTERCARD", b"0", b"z1"],
19286 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
19287 &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
19288 &[b"ZMPOP", b"1", b"str", b"MIN"],
19289 &[b"ZPOPMIN", b"z1", b"-1"],
19290 ];
19291
19292 let mut one = Fixture::new();
19293 let mut many = Fixture::striped(8);
19294 for parts in script {
19295 let a = one.run(parts);
19296 let b = many.run(parts);
19297 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19298 }
19299 }
19300
19301 #[test]
19303 fn a_sorted_set_operation_across_stripes_reads_every_input() {
19304 let mut f = Fixture::striped(8);
19305 let second = apart(&mut f, "z1");
19306 let third = apart(&mut f, &second);
19307 let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
19308
19309 f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
19310 f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
19311 assert_eq!(
19314 f.run(&[b"ZUNION", b"2", z1, z2]),
19315 "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
19316 );
19317 assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
19318 assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
19319 assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
19320 assert_eq!(
19321 f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
19322 ":1\r\n"
19323 );
19324
19325 assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
19328 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
19329 assert_eq!(
19330 f.run(&[
19331 b"ZUNIONSTORE",
19332 z3,
19333 b"2",
19334 z1,
19335 z2,
19336 b"WEIGHTS",
19337 b"2",
19338 b"3",
19339 b"AGGREGATE",
19340 b"MAX"
19341 ]),
19342 ":3\r\n"
19343 );
19344 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
19345 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
19346 assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
19347 assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
19348 assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
19349
19350 let popped = format!(
19353 "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
19354 second.len()
19355 );
19356 assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
19357 f.run(&[b"ZADD", z2, b"3", b"b"]);
19358
19359 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
19362 assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
19363 f.run(&[b"SET", z3, b"v"]);
19364 assert_eq!(
19365 f.run(&[b"ZUNION", b"2", z1, z3]),
19366 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19367 );
19368 assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
19369
19370 assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
19374 assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
19375 assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
19376 }
19377
19378 #[test]
19380 fn a_range_store_across_stripes_copies_the_window() {
19381 let mut f = Fixture::striped(8);
19382 let other = apart(&mut f, "src");
19383 let third = apart(&mut f, &other);
19384 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19385
19386 f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
19387 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
19388 assert_eq!(
19389 f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
19390 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
19391 );
19392 assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
19393
19394 assert_eq!(
19397 f.run(&[
19398 b"ZRANGESTORE",
19399 dst,
19400 src,
19401 b"+inf",
19402 b"-inf",
19403 b"BYSCORE",
19404 b"REV",
19405 b"LIMIT",
19406 b"0",
19407 b"2"
19408 ]),
19409 ":2\r\n"
19410 );
19411 assert_eq!(
19412 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19413 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19414 );
19415
19416 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
19419 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19420 f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
19421 f.run(&[b"SET", plain, b"v"]);
19422 assert_eq!(
19423 f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
19424 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19425 );
19426 assert_eq!(
19427 f.run(&[b"ZCARD", dst]),
19428 ":3\r\n",
19429 "and left the destination"
19430 );
19431 }
19432
19433 #[test]
19440 fn the_list_group_answers_the_same_however_many_stripes_there_are() {
19441 let script: &[&[&[u8]]] = &[
19442 &[b"RPUSH", b"l1", b"a", b"b", b"c"],
19443 &[b"LPUSH", b"l1", b"z"],
19444 &[b"RPUSHX", b"l1", b"d"],
19445 &[b"LPUSHX", b"gone", b"x"],
19446 &[b"RPUSHX", b"gone", b"x"],
19447 &[b"LLEN", b"l1"],
19448 &[b"LLEN", b"gone"],
19449 &[b"LRANGE", b"l1", b"0", b"-1"],
19450 &[b"LRANGE", b"l1", b"1", b"2"],
19451 &[b"LRANGE", b"l1", b"5", b"9"],
19452 &[b"LINDEX", b"l1", b"0"],
19453 &[b"LINDEX", b"l1", b"-1"],
19454 &[b"LINDEX", b"l1", b"99"],
19455 &[b"LSET", b"l1", b"0", b"y"],
19456 &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
19457 &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
19458 &[b"LPOS", b"l1", b"b"],
19459 &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
19460 &[b"LPOS", b"l1", b"nothere"],
19461 &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
19462 &[b"LREM", b"l1", b"1", b"aa"],
19463 &[b"LTRIM", b"l1", b"0", b"3"],
19464 &[b"LRANGE", b"l1", b"0", b"-1"],
19465 &[b"LPOP", b"l1"],
19466 &[b"RPOP", b"l1"],
19467 &[b"LPOP", b"l1", b"2"],
19468 &[b"LPOP", b"gone"],
19469 &[b"LPOP", b"gone", b"2"],
19470 &[b"EXISTS", b"l1"],
19471 &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
19474 &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
19475 &[b"RPOPLPUSH", b"src", b"dst"],
19476 &[b"LRANGE", b"dst", b"0", b"-1"],
19477 &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
19478 &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
19479 &[
19480 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
19481 ],
19482 &[
19483 b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
19484 ],
19485 &[b"LRANGE", b"dst", b"0", b"-1"],
19486 &[
19487 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
19488 ],
19489 &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19490 &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19491 &[b"LMPOP", b"1", b"gone", b"LEFT"],
19492 &[b"RPUSH", b"q", b"a", b"b", b"c"],
19495 &[b"BLPOP", b"gone", b"q", b"0"],
19496 &[b"BRPOP", b"q", b"0"],
19497 &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19498 &[b"RPUSH", b"q", b"x", b"y", b"z"],
19499 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19500 &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19501 &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19502 &[b"BLPOP", b"q", b"0"],
19503 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19504 &[b"SET", b"plain", b"v"],
19506 &[b"LPUSH", b"plain", b"a"],
19507 &[b"LLEN", b"plain"],
19508 &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19509 &[b"LRANGE", b"dst", b"0", b"-1"],
19510 &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19511 &[b"LSET", b"gone", b"0", b"v"],
19512 &[b"LSET", b"dst", b"99", b"v"],
19513 &[b"LPOP", b"dst", b"-1"],
19514 &[b"LMPOP", b"0", b"dst", b"LEFT"],
19515 &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19516 ];
19517
19518 let mut one = Fixture::new();
19519 let mut many = Fixture::striped(8);
19520 for parts in script {
19521 let a = one.run(parts);
19522 let b = many.run(parts);
19523 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19524 }
19525 }
19526
19527 #[test]
19529 fn a_list_move_across_stripes_takes_the_elements_with_it() {
19530 let mut f = Fixture::striped(8);
19531 let other = apart(&mut f, "src");
19532 let third = apart(&mut f, &other);
19533 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19534
19535 f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19536 assert_eq!(
19537 f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19538 "$1\r\na\r\n"
19539 );
19540 assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19541 assert_eq!(
19542 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19543 "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19544 "one went on each end of the destination"
19545 );
19546 assert_eq!(
19547 f.run(&[b"LRANGE", src, b"0", b"-1"]),
19548 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19549 );
19550
19551 assert_eq!(
19553 f.run(&[
19554 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19555 ]),
19556 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19557 );
19558 assert_eq!(
19559 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19560 "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19561 );
19562 assert_eq!(
19563 f.run(&[b"EXISTS", src]),
19564 ":0\r\n",
19565 "and the source is gone with its last element"
19566 );
19567
19568 f.run(&[b"RPUSH", src, b"e", b"f"]);
19571 assert_eq!(
19572 f.run(&[
19573 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19574 ]),
19575 "*-1\r\n"
19576 );
19577 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19578 assert_eq!(
19579 f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19580 "$-1\r\n"
19581 );
19582 assert_eq!(
19583 f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19584 "*-1\r\n"
19585 );
19586
19587 f.run(&[b"SET", plain, b"v"]);
19591 assert_eq!(
19592 f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19593 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19594 );
19595 assert_eq!(
19596 f.run(&[b"LLEN", src]),
19597 ":2\r\n",
19598 "and left the source alone"
19599 );
19600 assert_eq!(
19601 f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19602 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19603 );
19604 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19605 }
19606
19607 #[test]
19613 fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19614 let mut f = Fixture::striped(8);
19615 let other = apart(&mut f, "q");
19616 let (q, far) = (b"q".as_slice(), other.as_bytes());
19617
19618 assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19619 assert_eq!(f.server.parked(), 1);
19620 f.run(&[b"RPUSH", far, b"v"]);
19621 let mut out = Out::new(Proto::Resp2);
19622 assert!(f.server.serve_waiter(0, 0, &mut out));
19623 let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19624 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19625 assert_eq!(
19626 f.run(&[b"EXISTS", far]),
19627 ":0\r\n",
19628 "and it took the element with it"
19629 );
19630
19631 f.server.forget_waiters(7);
19634 assert_eq!(
19635 f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19636 Flow::Block
19637 );
19638 f.run(&[b"RPUSH", q, b"w"]);
19639 let mut out = Out::new(Proto::Resp2);
19640 assert!(f.server.serve_waiter(0, 0, &mut out));
19641 assert_eq!(
19642 core::str::from_utf8(out.as_slice()).expect("ascii"),
19643 "$1\r\nw\r\n"
19644 );
19645 assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19646 }
19647
19648 #[test]
19654 fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19655 let script: &[&[&[u8]]] = &[
19656 &[b"XADD", b"s", b"1-1", b"a", b"1"],
19657 &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19658 &[b"XADD", b"s", b"3-1", b"d", b"4"],
19659 &[b"XADD", b"s", b"1-1", b"e", b"5"],
19660 &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19661 &[b"XLEN", b"s"],
19662 &[b"XLEN", b"gone"],
19663 &[b"XRANGE", b"s", b"-", b"+"],
19664 &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19665 &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19666 &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19667 &[b"XREVRANGE", b"s", b"+", b"-"],
19668 &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19669 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19670 &[b"XREAD", b"STREAMS", b"s", b"$"],
19671 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19673 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19674 &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19675 &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19676 &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19677 &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19678 &[
19679 b"XREADGROUP",
19680 b"GROUP",
19681 b"g",
19682 b"c1",
19683 b"COUNT",
19684 b"1",
19685 b"STREAMS",
19686 b"s",
19687 b"0",
19688 ],
19689 &[
19690 b"XREADGROUP",
19691 b"GROUP",
19692 b"nope",
19693 b"c1",
19694 b"STREAMS",
19695 b"s",
19696 b">",
19697 ],
19698 &[b"XPENDING", b"s", b"g"],
19699 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19700 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19701 &[b"XPENDING", b"s", b"nope"],
19702 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19703 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19704 &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19705 &[b"XACK", b"s", b"g", b"1-1"],
19706 &[b"XACK", b"s", b"g", b"1-1"],
19707 &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19708 &[b"XPENDING", b"s", b"g"],
19709 &[b"XINFO", b"STREAM", b"s"],
19710 &[b"XINFO", b"GROUPS", b"s"],
19711 &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19712 &[b"XINFO", b"STREAM", b"gone"],
19713 &[b"XDEL", b"s", b"3-1"],
19715 &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19716 &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19717 &[b"XADD", b"s", b"9-1", b"z", b"9"],
19718 &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19719 &[b"XTRIM", b"s", b"MINID", b"9"],
19720 &[b"XSETID", b"s", b"99-1"],
19721 &[b"XSETID", b"s", b"1-1"],
19722 &[b"XLEN", b"s"],
19723 &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19724 &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19725 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19726 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19727 &[b"SET", b"plain", b"v"],
19729 &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19730 &[b"XLEN", b"plain"],
19731 &[b"XREAD", b"STREAMS", b"plain", b"0"],
19732 &[b"XRANGE", b"s", b"bogus", b"+"],
19733 &[b"XADD", b"s", b"1-1", b"a"],
19734 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19735 &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19736 ];
19737
19738 let mut one = Fixture::new();
19739 let mut many = Fixture::striped(8);
19740 for parts in script {
19741 let a = one.run(parts);
19742 let b = many.run(parts);
19743 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19744 }
19745 }
19746
19747 #[test]
19753 fn a_stream_read_across_stripes_reads_every_key() {
19754 let mut f = Fixture::striped(8);
19755 let other = apart(&mut f, "s1");
19756 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19757
19758 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19759 f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19760 let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19761 assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19762 assert!(got.contains("1-1"), "the first one is in there: {got}");
19763 assert!(got.contains("2-1"), "and so is the second: {got}");
19764
19765 f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19768 let got = f.run(&[
19769 b"XREADGROUP",
19770 b"GROUP",
19771 b"g",
19772 b"c",
19773 b"STREAMS",
19774 s1,
19775 s2,
19776 b">",
19777 b">",
19778 ]);
19779 assert!(got.starts_with("-NOGROUP"), "{got}");
19780 assert_eq!(
19781 f.run(&[b"XPENDING", s1, b"g"]),
19782 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19783 "and read nothing from the key that did have the group"
19784 );
19785
19786 f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19787 let got = f.run(&[
19788 b"XREADGROUP",
19789 b"GROUP",
19790 b"g",
19791 b"c",
19792 b"STREAMS",
19793 s1,
19794 s2,
19795 b">",
19796 b">",
19797 ]);
19798 assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19799 }
19800
19801 #[test]
19803 fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19804 let mut f = Fixture::striped(8);
19805 let other = apart(&mut f, "s1");
19806 let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19807 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19808 f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19809
19810 assert_eq!(
19811 f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19812 .0,
19813 Flow::Block
19814 );
19815 f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19816 let mut out = Out::new(Proto::Resp2);
19817 assert!(f.server.serve_waiter(0, 0, &mut out));
19818 let want = format!(
19819 "*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",
19820 other.len()
19821 );
19822 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19823 }
19824
19825 #[test]
19827 fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19828 let script: &[&[&[u8]]] = &[
19829 &[
19830 b"JSON.SET",
19831 b"d",
19832 b"$",
19833 br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19834 ],
19835 &[b"JSON.SET", b"d", b"$.a", b"2"],
19836 &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19837 &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19838 &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19839 &[b"JSON.GET", b"d"],
19840 &[b"JSON.GET", b"d", b"$.b"],
19841 &[b"JSON.GET", b"gone", b"$"],
19842 &[b"JSON.TYPE", b"d", b"$.b"],
19843 &[b"JSON.TYPE", b"d", b"$.s"],
19844 &[b"JSON.TOGGLE", b"d", b"$.t"],
19845 &[b"JSON.ARRLEN", b"d", b"$.b"],
19846 &[b"JSON.OBJLEN", b"d", b"$"],
19847 &[b"JSON.OBJKEYS", b"d", b"$"],
19848 &[b"JSON.STRLEN", b"d", b"$.s"],
19849 &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19850 &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19851 &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19852 &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19853 &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19854 &[b"JSON.ARRPOP", b"d", b"$.b"],
19855 &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19856 &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19857 &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19858 &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19859 &[b"JSON.RESP", b"d", b"$.b"],
19860 &[b"JSON.DEBUG", b"MEMORY", b"d"],
19861 &[b"JSON.CLEAR", b"d", b"$.b"],
19862 &[b"JSON.DEL", b"d", b"$.m"],
19863 &[b"JSON.FORGET", b"d", b"$.nothere"],
19864 &[
19866 b"JSON.MSET",
19867 b"m1",
19868 b"$",
19869 b"1",
19870 b"m2",
19871 b"$",
19872 b"2",
19873 b"m3",
19874 b"$",
19875 b"3",
19876 ],
19877 &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
19878 &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
19879 &[b"JSON.GET", b"m1", b"$"],
19880 &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
19881 &[b"JSON.GET", b"m2", b"$"],
19882 &[b"SET", b"plain", b"v"],
19884 &[b"JSON.GET", b"plain", b"$"],
19885 &[b"JSON.SET", b"plain", b"$", b"1"],
19886 &[b"JSON.MGET", b"m1", b"plain", b"$"],
19887 &[b"JSON.SET", b"d", b"$.b", b"["],
19888 &[b"JSON.DEL", b"plain"],
19889 ];
19890
19891 let mut one = Fixture::new();
19892 let mut many = Fixture::striped(8);
19893 for parts in script {
19894 let a = one.run(parts);
19895 let b = many.run(parts);
19896 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19897 }
19898 }
19899
19900 #[test]
19907 fn a_json_multi_write_across_stripes_reaches_every_key() {
19908 let mut f = Fixture::striped(8);
19909 let second = apart(&mut f, "m1");
19910 let third = apart(&mut f, &second);
19911 let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
19912
19913 assert_eq!(
19914 f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
19915 "+OK\r\n"
19916 );
19917 assert_eq!(
19918 f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
19919 "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
19920 );
19921
19922 assert_eq!(
19925 f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
19926 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
19927 );
19928 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
19929
19930 assert_eq!(
19934 f.run(&[
19935 b"JSON.MSET",
19936 m1,
19937 b"$",
19938 b"9",
19939 m2,
19940 b"$.deep",
19941 b"9",
19942 m3,
19943 b"$",
19944 b"7"
19945 ]),
19946 "$-1\r\n"
19947 );
19948 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
19949 assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
19950 assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
19951 }
19952
19953 #[test]
19955 fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
19956 let script: &[&[&[u8]]] = &[
19957 &[
19958 b"GEOADD",
19959 b"g",
19960 b"13.361389",
19961 b"38.115556",
19962 b"palermo",
19963 b"15.087269",
19964 b"37.502669",
19965 b"catania",
19966 ],
19967 &[
19968 b"GEOADD",
19969 b"g",
19970 b"NX",
19971 b"13.361389",
19972 b"38.115556",
19973 b"palermo",
19974 ],
19975 &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
19976 &[b"GEOPOS", b"g", b"palermo", b"nothere"],
19977 &[b"GEOHASH", b"g", b"palermo", b"catania"],
19978 &[b"GEODIST", b"g", b"palermo", b"catania"],
19979 &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
19980 &[b"GEODIST", b"g", b"palermo", b"nothere"],
19981 &[
19982 b"GEOSEARCH",
19983 b"g",
19984 b"FROMLONLAT",
19985 b"15",
19986 b"37",
19987 b"BYRADIUS",
19988 b"200",
19989 b"KM",
19990 b"ASC",
19991 b"WITHCOORD",
19992 b"WITHDIST",
19993 b"WITHHASH",
19994 ],
19995 &[
19996 b"GEOSEARCH",
19997 b"g",
19998 b"FROMMEMBER",
19999 b"palermo",
20000 b"BYBOX",
20001 b"400",
20002 b"400",
20003 b"KM",
20004 b"DESC",
20005 ],
20006 &[
20007 b"GEORADIUS",
20008 b"g",
20009 b"15",
20010 b"37",
20011 b"200",
20012 b"KM",
20013 b"COUNT",
20014 b"1",
20015 ],
20016 &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
20017 &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
20018 &[
20019 b"GEOSEARCHSTORE",
20020 b"dst",
20021 b"g",
20022 b"FROMLONLAT",
20023 b"15",
20024 b"37",
20025 b"BYRADIUS",
20026 b"200",
20027 b"KM",
20028 ],
20029 &[b"ZRANGE", b"dst", b"0", b"-1"],
20030 &[
20031 b"GEOSEARCHSTORE",
20032 b"dst",
20033 b"g",
20034 b"FROMLONLAT",
20035 b"15",
20036 b"37",
20037 b"BYRADIUS",
20038 b"1",
20039 b"M",
20040 b"STOREDIST",
20041 ],
20042 &[b"EXISTS", b"dst"],
20043 &[
20044 b"GEORADIUS",
20045 b"g",
20046 b"15",
20047 b"37",
20048 b"200",
20049 b"KM",
20050 b"STORE",
20051 b"dst",
20052 ],
20053 &[b"ZCARD", b"dst"],
20054 &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
20056 &[b"SET", b"plain", b"v"],
20057 &[b"GEOPOS", b"plain", b"a"],
20058 &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
20059 &[
20060 b"GEOSEARCHSTORE",
20061 b"dst",
20062 b"g",
20063 b"FROMLONLAT",
20064 b"15",
20065 b"37",
20066 b"BYRADIUS",
20067 b"200",
20068 b"KM",
20069 b"WITHCOORD",
20070 ],
20071 ];
20072
20073 let mut one = Fixture::new();
20074 let mut many = Fixture::striped(8);
20075 for parts in script {
20076 let a = one.run(parts);
20077 let b = many.run(parts);
20078 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20079 }
20080 }
20081
20082 #[test]
20084 fn a_geo_search_store_across_stripes_writes_what_it_found() {
20085 let mut f = Fixture::striped(8);
20086 let other = apart(&mut f, "g");
20087 let third = apart(&mut f, &other);
20088 let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
20089
20090 f.run(&[
20091 b"GEOADD",
20092 g,
20093 b"13.361389",
20094 b"38.115556",
20095 b"palermo",
20096 b"15.087269",
20097 b"37.502669",
20098 b"catania",
20099 ]);
20100 assert_eq!(
20101 f.run(&[
20102 b"GEOSEARCHSTORE",
20103 dst,
20104 g,
20105 b"FROMLONLAT",
20106 b"15",
20107 b"37",
20108 b"BYRADIUS",
20109 b"200",
20110 b"KM",
20111 b"ASC",
20112 ]),
20113 ":2\r\n"
20114 );
20115 assert_eq!(
20116 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
20117 "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
20118 "the geohash is the score, so the order is not the search order"
20119 );
20120 assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
20121
20122 assert_eq!(
20125 f.run(&[
20126 b"GEOSEARCHSTORE",
20127 dst,
20128 g,
20129 b"FROMMEMBER",
20130 b"palermo",
20131 b"BYRADIUS",
20132 b"200",
20133 b"KM",
20134 b"STOREDIST",
20135 ]),
20136 ":2\r\n"
20137 );
20138 assert_eq!(
20139 f.run(&[b"ZSCORE", dst, b"palermo"]),
20140 "$1\r\n0\r\n",
20141 "the centre is nought away from itself"
20142 );
20143
20144 assert_eq!(
20148 f.run(&[
20149 b"GEOSEARCHSTORE",
20150 dst,
20151 g,
20152 b"FROMLONLAT",
20153 b"0",
20154 b"0",
20155 b"BYRADIUS",
20156 b"1",
20157 b"M",
20158 ]),
20159 ":0\r\n"
20160 );
20161 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
20162 f.run(&[
20163 b"GEOSEARCHSTORE",
20164 dst,
20165 g,
20166 b"FROMLONLAT",
20167 b"15",
20168 b"37",
20169 b"BYRADIUS",
20170 b"200",
20171 b"KM",
20172 ]);
20173 f.run(&[b"SET", plain, b"v"]);
20174 assert_eq!(
20175 f.run(&[
20176 b"GEOSEARCHSTORE",
20177 dst,
20178 plain,
20179 b"FROMLONLAT",
20180 b"15",
20181 b"37",
20182 b"BYRADIUS",
20183 b"200",
20184 b"KM",
20185 ]),
20186 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
20187 );
20188 assert_eq!(
20189 f.run(&[b"ZCARD", dst]),
20190 ":2\r\n",
20191 "and left the destination"
20192 );
20193 }
20194
20195 #[test]
20201 fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
20202 let script: &[&[&[u8]]] = &[
20203 &[
20204 b"TS.CREATE",
20205 b"ts:a",
20206 b"LABELS",
20207 b"sensor",
20208 b"a",
20209 b"room",
20210 b"1",
20211 ],
20212 &[b"TS.CREATE", b"ts:a"],
20213 &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
20214 &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
20215 &[
20216 b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
20217 ],
20218 &[
20219 b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
20220 ],
20221 &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
20222 &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
20223 &[b"TS.GET", b"ts:a"],
20224 &[b"TS.GET", b"gone"],
20225 &[b"TS.RANGE", b"ts:a", b"-", b"+"],
20226 &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
20227 &[
20228 b"TS.RANGE",
20229 b"ts:a",
20230 b"-",
20231 b"+",
20232 b"AGGREGATION",
20233 b"avg",
20234 b"2000",
20235 ],
20236 &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
20237 &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20238 &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20239 &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
20240 &[b"TS.READ", b"ts:a", b"0"],
20241 &[b"TS.READ", b"ts:a", b"+"],
20242 &[b"TS.QUERYINDEX", b"sensor=a"],
20244 &[b"TS.QUERYINDEX", b"room=1"],
20245 &[b"TS.QUERYINDEX", b"room=9"],
20246 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
20247 &[
20248 b"TS.QUERYLABELS",
20249 b"VALUES",
20250 b"sensor",
20251 b"FILTER",
20252 b"room=1",
20253 ],
20254 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
20255 &[
20256 b"TS.MGET",
20257 b"SELECTED_LABELS",
20258 b"sensor",
20259 b"FILTER",
20260 b"sensor=a",
20261 ],
20262 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
20263 &[
20264 b"TS.MREVRANGE",
20265 b"-",
20266 b"+",
20267 b"WITHLABELS",
20268 b"FILTER",
20269 b"sensor=a",
20270 ],
20271 &[
20272 b"TS.MRANGE",
20273 b"-",
20274 b"+",
20275 b"FILTER",
20276 b"room=1",
20277 b"GROUPBY",
20278 b"room",
20279 b"REDUCE",
20280 b"max",
20281 ],
20282 &[b"TS.INFO", b"ts:a"],
20283 &[
20285 b"TS.CREATERULE",
20286 b"ts:a",
20287 b"ts:down",
20288 b"AGGREGATION",
20289 b"avg",
20290 b"1000",
20291 ],
20292 &[b"TS.CREATE", b"ts:down"],
20293 &[
20294 b"TS.CREATERULE",
20295 b"ts:a",
20296 b"ts:down",
20297 b"AGGREGATION",
20298 b"avg",
20299 b"1000",
20300 ],
20301 &[b"TS.ADD", b"ts:a", b"5000", b"4"],
20302 &[b"TS.ADD", b"ts:a", b"6000", b"5"],
20303 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20304 &[b"TS.GET", b"ts:down", b"LATEST"],
20305 &[b"TS.INFO", b"ts:down"],
20306 &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
20307 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20308 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20309 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20310 &[b"TS.DEL", b"ts:a", b"0", b"1000"],
20311 &[b"SET", b"plain", b"v"],
20313 &[b"TS.ADD", b"plain", b"1", b"1"],
20314 &[b"TS.GET", b"plain"],
20315 &[b"TS.READ", b"plain", b"0"],
20316 &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
20317 &[b"TS.RANGE", b"gone", b"-", b"+"],
20318 &[b"TS.INFO", b"gone"],
20319 ];
20320
20321 let mut one = Fixture::new();
20322 let mut many = Fixture::striped(8);
20323 for parts in script {
20324 let a = one.run(parts);
20325 let b = many.run(parts);
20326 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20327 }
20328 }
20329
20330 #[test]
20340 fn a_compaction_rule_across_stripes_reaches_both_ends() {
20341 let mut many = Fixture::striped(8);
20342 let other = apart(&mut many, "src");
20343 let (src, dst) = (b"src".as_slice(), other.as_bytes());
20344 let mut one = Fixture::new();
20345 let mut both = |parts: &[&[u8]]| {
20346 let a = one.run(parts);
20347 let b = many.run(parts);
20348 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20349 a
20350 };
20351
20352 both(&[b"TS.CREATE", src]);
20353 both(&[b"TS.CREATE", dst]);
20354 assert_eq!(
20355 both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
20356 "+OK\r\n"
20357 );
20358 both(&[b"TS.ADD", src, b"1000", b"1"]);
20359 both(&[b"TS.ADD", src, b"1500", b"3"]);
20360 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20363 let open = both(&[b"TS.GET", dst, b"LATEST"]);
20364 assert!(open.contains(":1000"), "the open bucket is folded: {open}");
20365
20366 both(&[b"TS.ADD", src, b"2000", b"5"]);
20369 let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
20370 assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
20371 assert!(got.contains(":1000"), "{got}");
20372
20373 both(&[b"TS.DEL", src, b"1000", b"1999"]);
20375 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20376
20377 assert!(
20380 both(&[b"TS.INFO", dst]).contains("src"),
20381 "the source is named"
20382 );
20383 assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
20384 assert_eq!(
20385 both(&[b"TS.DELETERULE", src, dst]),
20386 "-ERR TSDB: compaction rule does not exist\r\n"
20387 );
20388 }
20389
20390 #[test]
20392 fn a_label_query_across_stripes_finds_every_series() {
20393 let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
20394 let mut many = Fixture::striped(8);
20395 let mut homes: Vec<usize> = names
20396 .iter()
20397 .map(|name| many.server.striped(0).stripe_of(name))
20398 .collect();
20399 homes.sort_unstable();
20400 homes.dedup();
20401 assert!(homes.len() > 1, "the six keys are not all on one stripe");
20402
20403 let mut one = Fixture::new();
20404 let mut both = |parts: &[&[u8]]| {
20405 let a = one.run(parts);
20406 let b = many.run(parts);
20407 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20408 a
20409 };
20410 for name in &names {
20411 both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
20412 both(&[b"TS.ADD", name, b"1000", b"1"]);
20413 }
20414
20415 let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
20416 assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
20417 assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20418 assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20419 assert_eq!(
20420 both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
20421 "*1\r\n$4\r\nroom\r\n"
20422 );
20423 }
20424
20425 #[test]
20432 fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
20433 let script: &[&[&[u8]]] = &[
20434 &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
20435 &[b"HMSET", b"h", b"c", b"3"],
20436 &[b"HSETNX", b"h", b"a", b"9"],
20437 &[b"HSETNX", b"h", b"d", b"4"],
20438 &[b"HGET", b"h", b"a"],
20439 &[b"HGET", b"h", b"nope"],
20440 &[b"HMGET", b"h", b"a", b"nope"],
20441 &[b"HLEN", b"h"],
20442 &[b"HEXISTS", b"h", b"a"],
20443 &[b"HSTRLEN", b"h", b"a"],
20444 &[b"HGETALL", b"h"],
20445 &[b"HKEYS", b"h"],
20446 &[b"HVALS", b"h"],
20447 &[b"HINCRBY", b"h", b"a", b"5"],
20448 &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
20449 &[b"HSCAN", b"h", b"0"],
20450 &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
20451 &[b"HSCAN", b"h", b"0", b"NOVALUES"],
20452 &[b"HDEL", b"h", b"d"],
20453 &[b"HSET", b"one", b"f", b"v"],
20454 &[b"HRANDFIELD", b"one"],
20455 &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
20456 &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
20458 &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
20459 &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
20460 &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20461 &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20462 &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
20463 &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
20464 &[b"HGET", b"h", b"b"],
20465 &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
20467 &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
20468 &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
20469 &[b"HGET", b"h", b"e"],
20470 &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
20472 &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
20473 &[b"HGETALL", b"imp"],
20474 &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
20475 &[b"HIMPORT", b"DISCARD", b"fs"],
20476 &[b"SET", b"plain", b"v"],
20478 &[b"HSET", b"plain", b"a", b"1"],
20479 &[b"HGETALL", b"plain"],
20480 &[b"HGET", b"gone", b"a"],
20481 &[b"HINCRBY", b"h", b"a", b"nan"],
20482 ];
20483
20484 let mut one = Fixture::new();
20485 let mut many = Fixture::striped(8);
20486 one.server.set_clock_ms(1_700_000_000_000);
20490 many.server.set_clock_ms(1_700_000_000_000);
20491 for parts in script {
20492 let a = one.run(parts);
20493 let b = many.run(parts);
20494 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20495 }
20496 }
20497
20498 #[test]
20500 fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20501 let script: &[&[&[u8]]] = &[
20502 &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20503 &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20504 &[b"ARGET", b"a", b"1"],
20505 &[b"ARGET", b"a", b"99"],
20506 &[b"ARMGET", b"a", b"0", b"5", b"99"],
20507 &[b"ARGETRANGE", b"a", b"0", b"7"],
20508 &[b"ARLEN", b"a"],
20509 &[b"ARCOUNT", b"a"],
20510 &[b"ARINSERT", b"a", b"m", b"n"],
20511 &[b"ARSCAN", b"a", b"0", b"20"],
20512 &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20513 &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20514 &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20515 &[b"ARLASTITEMS", b"a", b"2"],
20516 &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20517 &[b"ARNEXT", b"a"],
20518 &[b"ARSEEK", b"a", b"3"],
20519 &[b"AROP", b"a", b"0", b"20", b"USED"],
20520 &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20521 &[b"ARINFO", b"a"],
20522 &[b"ARINFO", b"a", b"FULL"],
20523 &[b"ARDEL", b"a", b"0"],
20524 &[b"ARDELRANGE", b"a", b"1", b"2"],
20525 &[b"ARCOUNT", b"a"],
20526 &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20527 &[b"ARGETRANGE", b"r", b"0", b"9"],
20528 &[b"SET", b"plain", b"v"],
20530 &[b"ARGET", b"plain", b"0"],
20531 &[b"ARSET", b"plain", b"0", b"v"],
20532 &[b"ARGET", b"gone", b"0"],
20533 &[b"ARSET", b"a", b"bad", b"v"],
20534 ];
20535
20536 let mut one = Fixture::new();
20537 let mut many = Fixture::striped(8);
20538 for parts in script {
20539 let a = one.run(parts);
20540 let b = many.run(parts);
20541 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20542 }
20543 }
20544
20545 #[test]
20551 fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20552 let script: &[&[&[u8]]] = &[
20553 &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20554 &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20555 &[b"G.NADD", b"g", b"n3"],
20556 &[b"G.NGET", b"g", b"n1"],
20557 &[b"G.NGET", b"g", b"gone"],
20558 &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20559 &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20560 &[b"G.OUT", b"g", b"n1", b"knows"],
20561 &[b"G.IN", b"g", b"n2", b"knows"],
20562 &[b"G.DEG", b"g", b"n1", b"knows"],
20563 &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20564 &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20565 &[b"G.PATH", b"g", b"n1", b"n3"],
20566 &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20567 &[b"G.NDEL", b"g", b"n3"],
20568 &[b"G.NGET", b"g", b"n3"],
20569 &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20571 &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20572 &[b"VCARD", b"v"],
20573 &[b"VDIM", b"v"],
20574 &[b"VEMB", b"v", b"e1"],
20575 &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20576 &[b"VSIM", b"v", b"ELE", b"e1"],
20577 &[b"VISMEMBER", b"v", b"e1"],
20578 &[b"VISMEMBER", b"v", b"gone"],
20579 &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20580 &[b"VGETATTR", b"v", b"e1"],
20581 &[b"VRANGE", b"v", b"-", b"+"],
20582 &[b"VLINKS", b"v", b"e1"],
20583 &[b"VINFO", b"v"],
20584 &[b"VREM", b"v", b"e2"],
20585 &[b"VCARD", b"v"],
20586 &[b"SET", b"plain", b"v"],
20588 &[b"G.NGET", b"plain", b"n1"],
20589 &[b"VCARD", b"plain"],
20590 &[b"G.NADD", b"gone2", b"n"],
20591 &[b"VEMB", b"gone3", b"e"],
20592 ];
20593
20594 let mut one = Fixture::new();
20595 let mut many = Fixture::striped(8);
20596 for parts in script {
20597 let a = one.run(parts);
20598 let b = many.run(parts);
20599 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20600 }
20601 }
20602
20603 #[test]
20606 fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20607 let script: &[&[&[u8]]] = &[
20608 &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20610 &[b"BF.ADD", b"bf", b"a"],
20611 &[b"BF.ADD", b"bf", b"a"],
20612 &[b"BF.MADD", b"bf", b"b", b"c"],
20613 &[b"BF.EXISTS", b"bf", b"a"],
20614 &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20615 &[b"BF.CARD", b"bf"],
20616 &[b"BF.INFO", b"bf"],
20617 &[b"BF.INFO", b"bf", b"CAPACITY"],
20618 &[b"BF.DEBUG", b"bf"],
20619 &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20620 &[b"BF.EXISTS", b"made", b"x"],
20621 &[b"BF.SCANDUMP", b"bf", b"0"],
20622 &[b"CF.RESERVE", b"cf", b"100"],
20624 &[b"CF.ADD", b"cf", b"a"],
20625 &[b"CF.ADDNX", b"cf", b"a"],
20626 &[b"CF.COUNT", b"cf", b"a"],
20627 &[b"CF.EXISTS", b"cf", b"a"],
20628 &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20629 &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20630 &[b"CF.DEL", b"cf", b"a"],
20631 &[b"CF.COMPACT", b"cf"],
20632 &[b"CF.INFO", b"cf"],
20633 &[b"CF.DEBUG", b"cf"],
20634 &[b"CF.SCANDUMP", b"cf", b"0"],
20635 &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20637 &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20638 &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20639 &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20640 &[b"CMS.INFO", b"cms"],
20641 &[b"TOPK.RESERVE", b"tk", b"3"],
20643 &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20644 &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20645 &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20646 &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20647 &[b"TOPK.LIST", b"tk"],
20648 &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20649 &[b"TOPK.INFO", b"tk"],
20650 &[b"TDIGEST.CREATE", b"td"],
20652 &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20653 &[b"TDIGEST.MIN", b"td"],
20654 &[b"TDIGEST.MAX", b"td"],
20655 &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20656 &[b"TDIGEST.CDF", b"td", b"3"],
20657 &[b"TDIGEST.RANK", b"td", b"3"],
20658 &[b"TDIGEST.REVRANK", b"td", b"3"],
20659 &[b"TDIGEST.BYRANK", b"td", b"0"],
20660 &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20661 &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20662 &[b"TDIGEST.INFO", b"td"],
20663 &[b"TDIGEST.RESET", b"td"],
20664 &[b"TDIGEST.MIN", b"td"],
20665 &[b"SET", b"plain", b"v"],
20667 &[b"BF.ADD", b"plain", b"a"],
20668 &[b"CF.ADD", b"plain", b"a"],
20669 &[b"CMS.QUERY", b"plain", b"a"],
20670 &[b"TOPK.ADD", b"plain", b"a"],
20671 &[b"TDIGEST.ADD", b"plain", b"1"],
20672 &[b"CMS.INFO", b"gone"],
20673 &[b"TOPK.INFO", b"gone"],
20674 &[b"TDIGEST.INFO", b"gone"],
20675 ];
20676
20677 let mut one = Fixture::new();
20678 let mut many = Fixture::striped(8);
20679 for parts in script {
20680 let a = one.run(parts);
20681 let b = many.run(parts);
20682 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20683 }
20684 }
20685
20686 #[test]
20693 fn a_sketch_merge_across_stripes_reads_every_source() {
20694 let mut many = Fixture::striped(8);
20695 let other = apart(&mut many, "s1");
20696 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20697 let mut one = Fixture::new();
20698 let mut both = |parts: &[&[u8]]| {
20699 let a = one.run(parts);
20700 let b = many.run(parts);
20701 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20702 a
20703 };
20704
20705 for key in [b"cd".as_slice(), s1, s2] {
20709 both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20710 }
20711 both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20712 both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20713 assert_eq!(
20714 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20715 "+OK\r\n",
20716 "the merge took both sources"
20717 );
20718 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20719 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20721 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20722 both(&[b"SET", b"plain", b"v"]);
20724 assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20725 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20726
20727 both(&[b"FLUSHALL"]);
20730 both(&[b"TDIGEST.CREATE", b"td"]);
20731 both(&[b"TDIGEST.CREATE", s1]);
20732 both(&[b"TDIGEST.CREATE", s2]);
20733 both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20734 both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20735 assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20736 assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20737 assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20738 }
20739
20740 #[test]
20747 fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20748 let script: &[&[&[u8]]] = &[
20749 &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20750 &[b"SORT", b"l"],
20751 &[b"SORT", b"l", b"DESC"],
20752 &[b"SORT", b"l", b"ALPHA"],
20753 &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20754 &[b"SORT_RO", b"l"],
20755 &[
20758 b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20759 ],
20760 &[b"SORT", b"l", b"BY", b"w_*"],
20761 &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20762 &[b"DEL", b"w_2"],
20763 &[b"SORT", b"l", b"BY", b"w_*"],
20764 &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20767 &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20768 &[b"HSET", b"h_1", b"f", b"9"],
20770 &[b"HSET", b"h_2", b"f", b"8"],
20771 &[b"HSET", b"h_3", b"f", b"7"],
20772 &[b"HSET", b"h_10", b"f", b"6"],
20773 &[b"SORT", b"l", b"BY", b"h_*->f"],
20774 &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20775 &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20777 &[b"LRANGE", b"out", b"0", b"-1"],
20778 &[b"SORT", b"l", b"STORE", b"l"],
20779 &[b"LRANGE", b"l", b"0", b"-1"],
20780 &[b"SORT", b"missing", b"STORE", b"out"],
20783 &[b"EXISTS", b"out"],
20784 &[b"SADD", b"s", b"c", b"a", b"b"],
20787 &[b"SORT", b"s", b"ALPHA"],
20788 &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20789 &[b"LRANGE", b"out", b"0", b"-1"],
20790 &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20791 &[b"SORT", b"z", b"BY", b"nosort"],
20792 &[b"SORT", b"z", b"ALPHA", b"DESC"],
20793 &[b"SET", b"str", b"v"],
20796 &[b"SORT", b"str"],
20797 &[b"RPUSH", b"words", b"one", b"two"],
20798 &[b"SORT", b"words"],
20799 &[b"SORT_RO", b"l", b"STORE", b"out"],
20800 ];
20801
20802 let mut one = Fixture::new();
20803 let mut many = Fixture::striped(8);
20804 for parts in script {
20805 let a = one.run(parts);
20806 let b = many.run(parts);
20807 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20808 }
20809 }
20810
20811 #[test]
20818 fn a_sort_across_stripes_reads_every_pattern_key() {
20819 let mut f = Fixture::striped(8);
20820 let out = apart(&mut f, "l");
20821 let (list, dest) = (b"l".as_slice(), out.as_bytes());
20822
20823 f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20824 f.run(&[
20825 b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20826 ]);
20827 f.run(&[
20828 b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20829 ]);
20830
20831 let db = f.server.striped(0);
20834 let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20835 .iter()
20836 .map(|k| db.stripe_of(k.as_slice()))
20837 .collect();
20838 assert!(
20839 weights.iter().any(|s| *s != weights[0]),
20840 "the four weight keys all landed on one stripe, so this proves nothing"
20841 );
20842
20843 assert_eq!(
20844 f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
20845 "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
20846 "the order came off the weights and the answer off the data keys"
20847 );
20848 assert_eq!(
20849 f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20850 ":4\r\n"
20851 );
20852 assert_eq!(
20853 f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20854 "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20855 "the destination is on a stripe of its own and got the whole answer"
20856 );
20857 }
20858
20859 #[test]
20867 fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20868 let mut f = Fixture::striped(8);
20869 let other = apart(&mut f, "h");
20870 let (first, second) = (b"h".as_slice(), other.as_bytes());
20871
20872 assert_eq!(
20873 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
20874 "+OK\r\n"
20875 );
20876 assert_eq!(
20877 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
20878 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
20879 "the read comes off one stripe and has to answer for all of them"
20880 );
20881 for key in [first, second] {
20882 f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
20883 assert_eq!(
20884 f.run(&[b"OBJECT", b"ENCODING", key]),
20885 "$8\r\nlistpack\r\n",
20886 "two fields is still under the ladder"
20887 );
20888 f.run(&[b"HSET", key, b"c", b"3"]);
20889 assert_eq!(
20890 f.run(&[b"OBJECT", b"ENCODING", key]),
20891 "$9\r\nhashtable\r\n",
20892 "three fields is over it, on whichever stripe the key is on"
20893 );
20894 }
20895
20896 assert_eq!(
20899 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
20900 "+OK\r\n"
20901 );
20902 let db = f.server.striped(0);
20903 assert!(
20904 (0..db.width()).all(|i| db.hold_stripe(i).policy().name() == "allkeys-lru"),
20905 "a stripe kept the old policy"
20906 );
20907 }
20908
20909 fn held(f: &Fixture, name: &[u8]) -> (usize, u32) {
20915 let search = f.server.search.lock();
20916 let index = search.named(name).expect("the index is there");
20917 (index.held.docs.len(), index.held.docs.last())
20918 }
20919
20920 #[test]
20923 fn a_hash_that_is_written_reaches_the_index_that_follows_it() {
20924 let mut f = Fixture::new();
20925 f.run(&[
20926 b"FT.CREATE",
20927 b"ix",
20928 b"PREFIX",
20929 b"1",
20930 b"p:",
20931 b"SCHEMA",
20932 b"t",
20933 b"TEXT",
20934 ]);
20935 f.run(&[b"HSET", b"p:1", b"t", b"running dogs"]);
20936 assert_eq!(held(&f, b"ix"), (1, 1));
20937 f.run(&[b"HSET", b"other:1", b"t", b"running dogs"]);
20938 assert_eq!(held(&f, b"ix"), (1, 1));
20939
20940 f.run(&[b"HSET", b"p:1", b"u", b"beta"]);
20943 f.run(&[b"HDEL", b"p:1", b"u"]);
20944 assert_eq!(held(&f, b"ix"), (1, 3));
20945 let search = f.server.search.lock();
20946 let index = search.named(b"ix").expect("there");
20947 assert_eq!(index.held.docs.id(b"p:1"), Some(3));
20948 }
20949
20950 #[test]
20953 fn a_fresh_index_reads_the_keys_that_were_already_there() {
20954 let mut f = Fixture::new();
20955 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20956 f.run(&[b"SET", b"p:str", b"not a hash"]);
20957 f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
20958 f.run(&[
20959 b"FT.CREATE",
20960 b"ix",
20961 b"PREFIX",
20962 b"1",
20963 b"p:",
20964 b"SCHEMA",
20965 b"t",
20966 b"TEXT",
20967 ]);
20968
20969 assert_eq!(held(&f, b"ix"), (1, 1));
20970 let search = f.server.search.lock();
20971 let index = search.named(b"ix").expect("there");
20972 assert_eq!(index.trouble.whole().failures(), 0);
20973 }
20974
20975 #[test]
20978 fn an_index_that_skipped_the_scan_fills_up_on_the_next_write() {
20979 let mut f = Fixture::new();
20980 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20981 f.run(&[
20982 b"FT.CREATE",
20983 b"ix",
20984 b"PREFIX",
20985 b"1",
20986 b"p:",
20987 b"SKIPINITIALSCAN",
20988 b"SCHEMA",
20989 b"t",
20990 b"TEXT",
20991 ]);
20992 assert_eq!(held(&f, b"ix"), (0, 0));
20993 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20994 assert_eq!(held(&f, b"ix"), (1, 1));
20995 }
20996
20997 #[test]
21005 fn only_a_real_change_gives_the_document_a_new_number() {
21006 let mut f = Fixture::new();
21007 f.run(&[
21008 b"FT.CREATE",
21009 b"ix",
21010 b"PREFIX",
21011 b"1",
21012 b"p:",
21013 b"SCHEMA",
21014 b"t",
21015 b"TEXT",
21016 ]);
21017 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21018 assert_eq!(held(&f, b"ix"), (1, 1));
21019
21020 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21021 assert_eq!(held(&f, b"ix"), (1, 2), "the same value still rewrites");
21022
21023 for quiet in [
21024 vec![b"HSETNX".as_slice(), b"p:1", b"t", b"other"],
21025 vec![b"HDEL".as_slice(), b"p:1", b"nosuch"],
21026 vec![b"HGET".as_slice(), b"p:1", b"t"],
21027 vec![b"HGETALL".as_slice(), b"p:1"],
21028 vec![b"HEXPIRE".as_slice(), b"p:1", b"100", b"FIELDS", b"1", b"t"],
21029 vec![b"HPERSIST".as_slice(), b"p:1", b"FIELDS", b"1", b"t"],
21030 vec![
21031 b"HGETEX".as_slice(),
21032 b"p:1",
21033 b"EX",
21034 b"100",
21035 b"FIELDS",
21036 b"1",
21037 b"t",
21038 ],
21039 vec![b"HGETDEL".as_slice(), b"p:1", b"FIELDS", b"1", b"nosuch"],
21040 ] {
21041 f.run(&quiet);
21042 assert_eq!(held(&f, b"ix"), (1, 2), "{:?} moved the document", quiet[0]);
21043 }
21044
21045 f.run(&[b"HSET", b"p:2", b"n", b"1"]);
21047 f.run(&[b"HINCRBY", b"p:2", b"n", b"1"]);
21048 assert_eq!(held(&f, b"ix"), (2, 4));
21049 f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"n"]);
21055 assert_eq!(held(&f, b"ix"), (1, 5));
21056 }
21057
21058 #[test]
21064 fn a_key_emptied_by_a_deadline_spends_a_number_and_one_emptied_by_hdel_does_not() {
21065 fn refused(f: &Fixture, name: &[u8]) -> u64 {
21067 let search = f.server.search.lock();
21068 let index = search.named(name).expect("the index is there");
21069 index.trouble.whole().failures()
21070 }
21071
21072 let mut f = Fixture::new();
21073 f.run(&[
21074 b"FT.CREATE",
21075 b"ix",
21076 b"PREFIX",
21077 b"1",
21078 b"p:",
21079 b"SCHEMA",
21080 b"t",
21081 b"TEXT",
21082 ]);
21083 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21084 assert_eq!(held(&f, b"ix"), (1, 1));
21085 f.run(&[b"HDEL", b"p:1", b"t"]);
21086 assert_eq!(
21087 held(&f, b"ix"),
21088 (0, 1),
21089 "HDEL of the last field spends none"
21090 );
21091 assert_eq!(refused(&f, b"ix"), 1, "and is counted as a refusal");
21092
21093 f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21094 assert_eq!(held(&f, b"ix"), (1, 2));
21095 f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"t"]);
21096 assert_eq!(held(&f, b"ix"), (0, 3), "a deadline spends one");
21097 assert_eq!(refused(&f, b"ix"), 1, "and is counted as nothing");
21098
21099 f.run(&[b"HSET", b"p:3", b"t", b"alpha"]);
21100 assert_eq!(held(&f, b"ix"), (1, 4));
21101 f.run(&[b"HGETDEL", b"p:3", b"FIELDS", b"1", b"t"]);
21102 assert_eq!(held(&f, b"ix"), (0, 5), "and so does HGETDEL");
21103
21104 f.run(&[b"HSET", b"p:4", b"t", b"alpha", b"u", b"beta"]);
21107 assert_eq!(held(&f, b"ix"), (1, 6));
21108 f.run(&[b"HEXPIRE", b"p:4", b"0", b"FIELDS", b"2", b"t", b"u"]);
21109 assert_eq!(held(&f, b"ix"), (0, 7));
21110 assert_eq!(refused(&f, b"ix"), 1);
21111 }
21112
21113 #[test]
21117 fn a_field_written_already_past_its_deadline_moves_the_number_twice() {
21118 let mut f = Fixture::new();
21119 f.run(&[
21120 b"FT.CREATE",
21121 b"ix",
21122 b"PREFIX",
21123 b"1",
21124 b"p:",
21125 b"SCHEMA",
21126 b"t",
21127 b"TEXT",
21128 b"u",
21129 b"TEXT",
21130 ]);
21131 f.run(&[b"HSET", b"p:1", b"u", b"keepme"]);
21132 assert_eq!(held(&f, b"ix"), (1, 1));
21133 f.run(&[
21134 b"HSETEX", b"p:1", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21135 ]);
21136 assert_eq!(
21137 held(&f, b"ix"),
21138 (1, 3),
21139 "the key lived and the field did not"
21140 );
21141
21142 f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21144 assert_eq!(held(&f, b"ix"), (2, 4));
21145 f.run(&[
21146 b"HSETEX", b"p:2", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21147 ]);
21148 assert_eq!(held(&f, b"ix"), (1, 6));
21149 }
21150
21151 fn number(f: &Fixture, name: &[u8], key: &[u8]) -> Option<u32> {
21154 let search = f.server.search.lock();
21155 let index = search.named(name).expect("the index is there");
21156 index.held.docs.id(key)
21157 }
21158
21159 fn indexed() -> Fixture {
21162 let mut f = Fixture::new();
21163 f.run(&[
21164 b"FT.CREATE",
21165 b"ix",
21166 b"PREFIX",
21167 b"1",
21168 b"p:",
21169 b"SCHEMA",
21170 b"t",
21171 b"TEXT",
21172 ]);
21173 f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21174 f
21175 }
21176
21177 #[test]
21180 fn a_key_a_keyspace_command_takes_away_loses_its_document() {
21181 for take in [
21182 vec![b"DEL".as_slice(), b"p:1"],
21183 vec![b"UNLINK".as_slice(), b"p:1"],
21184 vec![b"PEXPIREAT".as_slice(), b"p:1", b"1"],
21185 vec![b"EXPIRE".as_slice(), b"p:1", b"-1"],
21186 ] {
21187 let mut f = indexed();
21188 assert_eq!(held(&f, b"ix"), (1, 1));
21189 f.run(&take);
21190 assert_eq!(held(&f, b"ix"), (0, 1), "{:?} left something", take[0]);
21191 let search = f.server.search.lock();
21192 let index = search.named(b"ix").expect("the index is there");
21193 assert_eq!(index.trouble.whole().failures(), 0, "{:?}", take[0]);
21194 }
21195
21196 let mut f = indexed();
21198 f.run(&[b"EXPIRE", b"p:1", b"1000"]);
21199 assert_eq!(held(&f, b"ix"), (1, 1));
21200 f.run(&[b"PERSIST", b"p:1"]);
21201 assert_eq!(held(&f, b"ix"), (1, 1));
21202 }
21203
21204 #[test]
21208 fn a_rename_inside_the_prefix_keeps_the_number_the_document_had() {
21209 let mut f = indexed();
21210 f.run(&[b"RENAME", b"p:1", b"p:2"]);
21211 assert_eq!(held(&f, b"ix"), (1, 1), "nothing was read again");
21212 assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
21213 assert_eq!(number(&f, b"ix", b"p:1"), None);
21214
21215 f.run(&[b"RENAME", b"p:2", b"q:1"]);
21216 assert_eq!(held(&f, b"ix"), (0, 1), "out of the prefix is an erase");
21217
21218 f.run(&[b"RENAME", b"q:1", b"p:3"]);
21219 assert_eq!(held(&f, b"ix"), (1, 2), "and into it is a reading");
21220 assert_eq!(number(&f, b"ix", b"p:3"), Some(2));
21221
21222 f.run(&[b"HSET", b"p:4", b"t", b"beta"]);
21225 assert_eq!(f.run(&[b"RENAMENX", b"p:3", b"p:4"]), ":0\r\n");
21226 assert_eq!(held(&f, b"ix"), (2, 3));
21227 f.run(&[b"RENAMENX", b"p:3", b"p:5"]);
21228 assert_eq!(number(&f, b"ix", b"p:5"), Some(2));
21229 }
21230
21231 #[test]
21234 fn a_rename_over_a_document_leaves_one_of_them() {
21235 let mut f = indexed();
21236 f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
21237 assert_eq!(held(&f, b"ix"), (2, 2));
21238 f.run(&[b"RENAME", b"p:1", b"p:2"]);
21239 assert_eq!(held(&f, b"ix"), (1, 2));
21240 assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
21241 }
21242
21243 #[test]
21247 fn a_key_that_arrives_under_the_prefix_is_read_and_one_overwritten_is_erased() {
21248 let mut f = indexed();
21249 f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
21250 f.run(&[b"COPY", b"q:1", b"p:2"]);
21251 assert_eq!(held(&f, b"ix"), (2, 2));
21252 assert_eq!(number(&f, b"ix", b"p:2"), Some(2));
21253
21254 f.run(&[b"COPY", b"p:1", b"q:2"]);
21256 assert_eq!(held(&f, b"ix"), (2, 2));
21257
21258 f.run(&[b"COPY", b"q:1", b"p:1", b"REPLACE"]);
21260 assert_eq!(held(&f, b"ix"), (2, 3));
21261 assert_eq!(number(&f, b"ix", b"p:1"), Some(3));
21262
21263 f.run(&[b"SET", b"s:1", b"plain"]);
21266 f.run(&[b"COPY", b"s:1", b"p:1", b"REPLACE"]);
21267 assert_eq!(held(&f, b"ix"), (1, 3));
21268 let dump = f.run(&[b"DUMP", b"q:1"]);
21269 assert!(dump.starts_with('$'), "{dump}");
21270 }
21271
21272 #[test]
21277 fn the_keyspace_group_reads_database_zero_whatever_database_it_ran_on() {
21278 let mut f = indexed();
21279 f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
21280 assert_eq!(held(&f, b"ix"), (2, 2));
21281 f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
21284 assert_eq!(held(&f, b"ix"), (2, 3));
21285 f.run(&[b"DEL", b"p:2"]);
21288 assert_eq!(held(&f, b"ix"), (1, 3));
21289 f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
21290 assert_eq!(held(&f, b"ix"), (1, 3), "the copy landed out of sight");
21291
21292 let dump = f.run(&[b"DUMP", b"p:1"]);
21294 assert!(dump.starts_with('$'), "{dump}");
21295 f.run(&[b"SELECT", b"1"]);
21296 f.run(&[b"HSET", b"q:1", b"t", b"gamma"]);
21297 f.run(&[b"RENAME", b"q:1", b"p:3"]);
21298 assert_eq!(held(&f, b"ix"), (1, 3), "and so is a rename");
21299 }
21300
21301 #[test]
21304 fn a_move_leaves_the_document_where_it_is() {
21305 let mut f = indexed();
21306 f.run(&[b"MOVE", b"p:1", b"1"]);
21307 assert_eq!(held(&f, b"ix"), (1, 1), "the key moved and nothing else");
21308 assert_eq!(number(&f, b"ix", b"p:1"), Some(1));
21309
21310 f.run(&[b"SELECT", b"1"]);
21311 f.run(&[b"HSET", b"p:1", b"t", b"beta"]);
21312 assert_eq!(held(&f, b"ix"), (1, 2), "and a write there still lands");
21313 f.run(&[b"DEL", b"p:1"]);
21314 assert_eq!(held(&f, b"ix"), (0, 2));
21315 }
21316
21317 #[test]
21319 fn a_flush_drops_the_indexes() {
21320 for flush in [b"FLUSHALL".as_slice(), b"FLUSHDB"] {
21321 let mut f = indexed();
21322 f.run(&[flush]);
21323 assert!(f.server.search.lock().is_empty(), "{flush:?} kept an index");
21324 assert_eq!(f.run(&[b"FT._LIST"]), "*0\r\n");
21325 }
21326
21327 let mut f = indexed();
21330 f.run(&[b"SELECT", b"9"]);
21331 f.run(&[b"FLUSHDB"]);
21332 assert!(f.server.search.lock().is_empty());
21333 }
21334
21335 #[test]
21338 fn a_hash_that_will_not_read_is_counted_where_ft_info_reports_it() {
21339 let mut f = Fixture::new();
21340 f.run(&[
21341 b"FT.CREATE",
21342 b"ix",
21343 b"PREFIX",
21344 b"1",
21345 b"p:",
21346 b"SCHEMA",
21347 b"n",
21348 b"NUMERIC",
21349 ]);
21350 f.run(&[b"HSET", b"p:1", b"n", b"notanumber"]);
21351 assert_eq!(held(&f, b"ix"), (0, 0));
21352
21353 let reply = f.run(&[b"FT.INFO", b"ix"]);
21354 assert!(
21355 reply.contains("SEARCH_NUMERIC_VALUE_INVALID Invalid numeric value: 'notanumber'"),
21356 "{reply}"
21357 );
21358 assert!(reply.contains("hash_indexing_failures"), "{reply}");
21359 }
21360
21361 #[test]
21364 fn an_index_can_only_be_made_on_database_zero() {
21365 let mut f = Fixture::new();
21366 f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]);
21367 f.run(&[b"SELECT", b"1"]);
21368 let refused = "-Cannot create index on db != 0\r\n";
21369 assert_eq!(
21370 f.run(&[b"FT.CREATE", b"jx", b"SCHEMA", b"t", b"TEXT"]),
21371 refused
21372 );
21373 assert_eq!(
21375 f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21376 refused
21377 );
21378 assert_eq!(
21380 f.run(&[b"FT.CREATE", b"zz", b"BOGUS", b"SCHEMA", b"t", b"TEXT"]),
21381 refused
21382 );
21383 assert_eq!(
21385 f.run(&[b"FT._CREATEIFNX", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21386 "+OK\r\n"
21387 );
21388 assert_eq!(f.server.search.lock().len(), 1);
21389 }
21390
21391 #[test]
21397 fn the_scan_is_one_database_and_the_following_is_all_of_them() {
21398 let mut f = Fixture::new();
21399 f.run(&[b"SELECT", b"1"]);
21400 f.run(&[b"HSET", b"p:9", b"t", b"on one"]);
21401 f.run(&[b"SELECT", b"0"]);
21402 f.run(&[b"HSET", b"p:0", b"t", b"on zero"]);
21403 f.run(&[
21404 b"FT.CREATE",
21405 b"ix",
21406 b"PREFIX",
21407 b"1",
21408 b"p:",
21409 b"SCHEMA",
21410 b"t",
21411 b"TEXT",
21412 ]);
21413 assert_eq!(held(&f, b"ix"), (1, 1), "the scan read database zero only");
21414
21415 f.run(&[b"SELECT", b"1"]);
21416 f.run(&[b"HSET", b"p:8", b"t", b"later"]);
21417 assert_eq!(
21418 held(&f, b"ix"),
21419 (2, 2),
21420 "and then it follows every database"
21421 );
21422 }
21423
21424 fn corpus(f: &mut Fixture) {
21427 f.run(&[
21428 b"FT.CREATE",
21429 b"sx",
21430 b"PREFIX",
21431 b"1",
21432 b"d:",
21433 b"SCHEMA",
21434 b"t",
21435 b"TEXT",
21436 b"g",
21437 b"TAG",
21438 b"n",
21439 b"NUMERIC",
21440 ]);
21441 for (key, text, tag, number) in [
21442 (b"d:1".as_slice(), "alpha beta", "aa,bb", "1"),
21443 (b"d:2", "alpha gamma", "bb", "2"),
21444 (b"d:3", "delta", "cc", "3"),
21445 (b"d:4", "alpha beta gamma", "aa,cc", "4"),
21446 ] {
21447 f.run(&[
21448 b"HSET",
21449 key,
21450 b"t",
21451 text.as_bytes(),
21452 b"g",
21453 tag.as_bytes(),
21454 b"n",
21455 number.as_bytes(),
21456 ]);
21457 }
21458 }
21459
21460 #[test]
21463 fn a_search_answers_a_total_and_then_the_rows() {
21464 let mut f = Fixture::new();
21465 corpus(&mut f);
21466 assert_eq!(
21467 f.run(&[b"FT.SEARCH", b"sx", b"delta"]),
21468 "*3\r\n:1\r\n$3\r\nd:3\r\n*6\r\n$1\r\nt\r\n$5\r\ndelta\r\n$1\r\ng\r\n$2\r\ncc\r\n$1\r\nn\r\n$1\r\n3\r\n"
21469 );
21470 f.run(&[b"HSET", b"d:3", b"extra", b"more"]);
21473 assert!(f.run(&[b"FT.SEARCH", b"sx", b"delta"]).contains("extra"));
21474 assert_eq!(
21477 f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
21478 "*2\r\n:1\r\n$3\r\nd:3\r\n"
21479 );
21480 assert_eq!(
21481 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
21482 "*1\r\n:3\r\n"
21483 );
21484 }
21485
21486 #[test]
21489 fn the_window_is_ten_rows_and_a_million_wide_at_most() {
21490 let mut f = Fixture::new();
21491 corpus(&mut f);
21492 assert_eq!(
21493 f.run(&[
21494 b"FT.SEARCH",
21495 b"sx",
21496 b"alpha",
21497 b"NOCONTENT",
21498 b"LIMIT",
21499 b"1",
21500 b"1"
21501 ]),
21502 "*2\r\n:3\r\n$3\r\nd:2\r\n"
21503 );
21504 assert_eq!(
21505 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0"]),
21506 "-SEARCH_PARSE_ARGS LIMIT requires two arguments\r\n"
21507 );
21508 assert_eq!(
21509 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"-1"]),
21510 "-SEARCH_PARSE_ARGS LIMIT needs two numeric arguments\r\n"
21511 );
21512 assert_eq!(
21513 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"1000001"]),
21514 "-SEARCH_LIMIT_OVER LIMIT exceeds maximum of 1000000\r\n"
21515 );
21516 assert_eq!(
21517 f.run(&[
21518 b"FT.SEARCH",
21519 b"sx",
21520 b"alpha",
21521 b"NOCONTENT",
21522 b"LIMIT",
21523 b"999999",
21524 b"1000000"
21525 ]),
21526 "*1\r\n:3\r\n"
21527 );
21528 }
21529
21530 #[test]
21534 fn a_return_of_nothing_is_not_the_same_as_nocontent() {
21535 let mut f = Fixture::new();
21536 corpus(&mut f);
21537 let bare = "*2\r\n:1\r\n$3\r\nd:3\r\n";
21538 assert_eq!(
21539 f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"0"]),
21540 bare
21541 );
21542 assert_eq!(
21543 f.run(&[
21544 b"FT.SEARCH",
21545 b"sx",
21546 b"delta",
21547 b"NOCONTENT",
21548 b"RETURN",
21549 b"1",
21550 b"t"
21551 ]),
21552 bare
21553 );
21554 assert_eq!(
21555 f.run(&[
21556 b"FT.SEARCH",
21557 b"sx",
21558 b"delta",
21559 b"RETURN",
21560 b"0",
21561 b"RETURN",
21562 b"1",
21563 b"t"
21564 ]),
21565 "*3\r\n:1\r\n$3\r\nd:3\r\n*2\r\n$1\r\nt\r\n$5\r\ndelta\r\n"
21566 );
21567 }
21568
21569 #[test]
21572 fn the_count_after_return_counts_words() {
21573 let mut f = Fixture::new();
21574 corpus(&mut f);
21575 assert_eq!(
21578 f.run(&[
21579 b"FT.SEARCH",
21580 b"sx",
21581 b"delta",
21582 b"RETURN",
21583 b"3",
21584 b"t",
21585 b"AS",
21586 b"x"
21587 ]),
21588 "*3\r\n:1\r\n$3\r\nd:3\r\n*2\r\n$1\r\nx\r\n$5\r\ndelta\r\n"
21589 );
21590 assert_eq!(
21593 f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"2", b"t", b"AS"]),
21594 "-SEARCH_PARSE_ARGS RETURN path AS name - must be accompanied with NAME\r\n"
21595 );
21596 assert_eq!(
21597 f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"3", b"t", b"AS"]),
21598 "-SEARCH_PARSE_ARGS Bad arguments for RETURN: Expected an argument, but none provided\r\n"
21599 );
21600 assert_eq!(
21604 f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"AS"]),
21605 "*3\r\n:1\r\n$3\r\nd:3\r\n*0\r\n"
21606 );
21607 }
21608
21609 #[test]
21612 fn a_filter_is_a_range_written_outside_the_query() {
21613 let mut f = Fixture::new();
21614 corpus(&mut f);
21615 assert_eq!(
21616 f.run(&[
21617 b"FT.SEARCH",
21618 b"sx",
21619 b"alpha",
21620 b"NOCONTENT",
21621 b"FILTER",
21622 b"n",
21623 b"2",
21624 b"4"
21625 ]),
21626 "*3\r\n:2\r\n$3\r\nd:2\r\n$3\r\nd:4\r\n"
21627 );
21628 assert_eq!(
21629 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2"]),
21630 "-SEARCH_PARSE_ARGS FILTER requires 3 arguments\r\n"
21631 );
21632 assert_eq!(
21633 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"x", b"1"]),
21634 "-SEARCH_PARSE_ARGS Bad lower range: x\r\n"
21635 );
21636 assert_eq!(
21637 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2", b"1"]),
21638 "-SEARCH_SYNTAX Invalid numeric range (min > max): @n:[2.000000 1.000000]\r\n"
21639 );
21640 for field in [b"g".as_slice(), b"nope"] {
21643 assert_eq!(
21644 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", field, b"2", b"1"]),
21645 "*1\r\n:0\r\n"
21646 );
21647 }
21648 }
21649
21650 #[test]
21653 fn the_index_is_found_before_the_arguments_are_read() {
21654 let mut f = Fixture::new();
21655 corpus(&mut f);
21656 let missing = "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n";
21657 assert_eq!(f.run(&[b"FT.SEARCH", b"nope", b"alpha", b"BOGUS"]), missing);
21658 assert_eq!(
21659 f.run(&[b"FT.EXPLAIN", b"nope", b"alpha", b"BOGUS"]),
21660 missing
21661 );
21662 assert_eq!(
21665 f.run(&[b"FT.SEARCH", b"sx", b"@@@", b"BOGUS"]),
21666 "-SEARCH_ARG_UNRECOGNIZED Unknown argument `BOGUS` at position 1 for <main>\r\n"
21667 );
21668 }
21669
21670 #[test]
21673 fn inkeys_comes_off_the_total() {
21674 let mut f = Fixture::new();
21675 corpus(&mut f);
21676 assert_eq!(
21677 f.run(&[
21678 b"FT.SEARCH",
21679 b"sx",
21680 b"alpha",
21681 b"NOCONTENT",
21682 b"INKEYS",
21683 b"1",
21684 b"d:1"
21685 ]),
21686 "*2\r\n:1\r\n$3\r\nd:1\r\n"
21687 );
21688 assert_eq!(
21689 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"NOCONTENT", b"INKEYS", b"0"]),
21690 "*1\r\n:0\r\n"
21691 );
21692 }
21693
21694 #[test]
21701 fn the_fields_are_read_from_the_session_database() {
21702 let mut f = Fixture::new();
21703 corpus(&mut f);
21704 f.run(&[b"SELECT", b"1"]);
21705 f.run(&[b"HSET", b"d:9", b"t", b"delta", b"n", b"9"]);
21706 assert_eq!(
21709 f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
21710 "*3\r\n:2\r\n$3\r\nd:3\r\n$3\r\nd:9\r\n"
21711 );
21712 assert_eq!(
21713 f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
21714 "*3\r\n:1\r\n$3\r\nd:9\r\n*2\r\n$1\r\nn\r\n$1\r\n9\r\n"
21715 );
21716 }
21717
21718 #[test]
21721 fn the_third_protocol_answers_a_map_of_five() {
21722 let mut f = Fixture::new();
21723 corpus(&mut f);
21724 f.out = Out::new(Proto::Resp3);
21725 assert_eq!(
21726 f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
21727 concat!(
21728 "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
21729 "%3\r\n+id\r\n$3\r\nd:3\r\n+extra_attributes\r\n%1\r\n$1\r\nn\r\n$1\r\n3\r\n",
21730 "+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
21731 )
21732 );
21733 }
21734
21735 #[test]
21739 fn a_window_of_nothing_has_to_start_at_the_top() {
21740 let mut f = Fixture::new();
21741 corpus(&mut f);
21742 let refused = "-SEARCH_LIMIT_OVER The `offset` of the LIMIT must be 0 when `num` is 0\r\n";
21743 assert_eq!(
21744 f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
21745 refused
21746 );
21747 assert_eq!(
21748 f.run(&[b"FT.EXPLAIN", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
21749 refused
21750 );
21751 assert_eq!(
21752 f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
21753 refused
21754 );
21755 assert_eq!(
21756 f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
21757 "*1\r\n:3\r\n"
21758 );
21759 }
21760
21761 #[test]
21764 fn an_aggregation_answers_a_count_and_then_the_properties() {
21765 let mut f = Fixture::new();
21766 corpus(&mut f);
21767 assert_eq!(
21768 f.run(&[b"FT.AGGREGATE", b"sx", b"alpha"]),
21769 "*4\r\n:1\r\n*0\r\n*0\r\n*0\r\n"
21770 );
21771 assert_eq!(
21773 f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"1", b"@t"]),
21774 concat!(
21775 "*4\r\n:3\r\n*2\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
21776 "*2\r\n$1\r\nt\r\n$11\r\nalpha gamma\r\n",
21777 "*2\r\n$1\r\nt\r\n$16\r\nalpha beta gamma\r\n"
21778 )
21779 );
21780 assert_eq!(
21784 f.run(&[
21785 b"FT.AGGREGATE",
21786 b"sx",
21787 b"alpha",
21788 b"LOAD",
21789 b"1",
21790 b"@n",
21791 b"LIMIT",
21792 b"1",
21793 b"2"
21794 ]),
21795 "*3\r\n:3\r\n*2\r\n$1\r\nn\r\n$1\r\n2\r\n*2\r\n$1\r\nn\r\n$1\r\n4\r\n"
21796 );
21797 assert_eq!(
21799 f.run(&[b"FT.AGGREGATE", b"sx", b"nope", b"LOAD", b"1", b"@t"]),
21800 "*1\r\n:0\r\n"
21801 );
21802 }
21803
21804 #[test]
21808 fn a_load_counts_words_and_can_rename_what_it_reads() {
21809 let mut f = Fixture::new();
21810 corpus(&mut f);
21811 assert_eq!(
21813 f.run(&[
21814 b"FT.AGGREGATE",
21815 b"sx",
21816 b"alpha",
21817 b"LOAD",
21818 b"3",
21819 b"@t",
21820 b"AS",
21821 b"text"
21822 ]),
21823 concat!(
21824 "*4\r\n:3\r\n*2\r\n$4\r\ntext\r\n$10\r\nalpha beta\r\n",
21825 "*2\r\n$4\r\ntext\r\n$11\r\nalpha gamma\r\n",
21826 "*2\r\n$4\r\ntext\r\n$16\r\nalpha beta gamma\r\n"
21827 )
21828 );
21829 assert_eq!(
21830 f.run(&[
21831 b"FT.AGGREGATE",
21832 b"sx",
21833 b"alpha",
21834 b"LOAD",
21835 b"*",
21836 b"LIMIT",
21837 b"0",
21838 b"1"
21839 ]),
21840 concat!(
21841 "*2\r\n:3\r\n*6\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
21842 "$1\r\ng\r\n$5\r\naa,bb\r\n$1\r\nn\r\n$1\r\n1\r\n"
21843 )
21844 );
21845 assert_eq!(
21847 f.run(&[
21848 b"FT.AGGREGATE",
21849 b"sx",
21850 b"alpha",
21851 b"LOAD",
21852 b"2",
21853 b"@n",
21854 b"@nope",
21855 b"LIMIT",
21856 b"0",
21857 b"2"
21858 ]),
21859 "*3\r\n:3\r\n*2\r\n$1\r\nn\r\n$1\r\n1\r\n*2\r\n$1\r\nn\r\n$1\r\n2\r\n"
21860 );
21861 }
21862
21863 #[test]
21866 fn a_load_refuses_a_count_it_cannot_use() {
21867 let mut f = Fixture::new();
21868 corpus(&mut f);
21869 let head = "-SEARCH_PARSE_ARGS Bad arguments for LOAD: ";
21870 assert_eq!(
21871 f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"x"]),
21872 format!("{head}Expected number of fields or `*`\r\n")
21873 );
21874 assert_eq!(
21875 f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"-1", b"@t"]),
21876 format!("{head}Value is outside acceptable bounds\r\n")
21877 );
21878 assert_eq!(
21879 f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"5", b"@t"]),
21880 format!("{head}Expected an argument, but none provided\r\n")
21881 );
21882 assert_eq!(
21883 f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD"]),
21884 format!("{head}Expected an argument, but none provided\r\n")
21885 );
21886 assert_eq!(
21890 f.run(&[
21891 b"FT.AGGREGATE",
21892 b"sx",
21893 b"alpha",
21894 b"LOAD",
21895 b"2",
21896 b"@t",
21897 b"AS"
21898 ]),
21899 "-SEARCH_PARSE_ARGS LOAD path AS name - must be accompanied with NAME\r\n"
21900 );
21901 assert_eq!(
21904 f.run(&[
21905 b"FT.AGGREGATE",
21906 b"sx",
21907 b"alpha",
21908 b"LOAD",
21909 b"2",
21910 b"@t",
21911 b"AS",
21912 b"VERBATIM"
21913 ]),
21914 "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 5 for <main>\r\n"
21915 );
21916 assert_eq!(
21920 f.run(&[
21921 b"FT.AGGREGATE",
21922 b"sx",
21923 b"alpha",
21924 b"LOAD",
21925 b"0",
21926 b"LIMIT",
21927 b"0",
21928 b"1"
21929 ]),
21930 "*2\r\n:1\r\n*0\r\n"
21931 );
21932 }
21933
21934 #[test]
21937 fn a_pipeline_step_closes_the_door_on_the_search_words() {
21938 let mut f = Fixture::new();
21939 corpus(&mut f);
21940 assert_eq!(
21941 f.run(&[
21942 b"FT.AGGREGATE",
21943 b"sx",
21944 b"alpha",
21945 b"LOAD",
21946 b"1",
21947 b"@t",
21948 b"VERBATIM"
21949 ]),
21950 "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 4 for <main>\r\n"
21951 );
21952 assert_eq!(
21953 f.run(&[
21954 b"FT.AGGREGATE",
21955 b"sx",
21956 b"alpha",
21957 b"LIMIT",
21958 b"0",
21959 b"1",
21960 b"VERBATIM"
21961 ]),
21962 "*2\r\n:1\r\n*0\r\n"
21963 );
21964 for word in [b"RETURN".as_slice(), b"SUMMARIZE", b"HIGHLIGHT"] {
21967 let name = core::str::from_utf8(word).expect("the three words are text");
21968 assert_eq!(
21969 f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", word]),
21970 format!("-SEARCH_PARSE_ARGS {name} is not supported on FT.AGGREGATE\r\n")
21971 );
21972 }
21973 }
21974
21975 #[test]
21978 fn addscores_writes_a_shorter_score_than_withscores() {
21979 let mut f = Fixture::new();
21980 corpus(&mut f);
21981 assert_eq!(
21982 f.run(&[
21983 b"FT.AGGREGATE",
21984 b"sx",
21985 b"alpha",
21986 b"ADDSCORES",
21987 b"LOAD",
21988 b"1",
21989 b"@n",
21990 b"LIMIT",
21991 b"0",
21992 b"2"
21993 ]),
21994 concat!(
21995 "*3\r\n:3\r\n",
21996 "*4\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n$1\r\nn\r\n$1\r\n1\r\n",
21997 "*4\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n$1\r\nn\r\n$1\r\n2\r\n"
21998 )
21999 );
22000 assert_eq!(
22004 f.run(&[
22005 b"FT.AGGREGATE",
22006 b"sx",
22007 b"alpha",
22008 b"NOCONTENT",
22009 b"WITHSCORES",
22010 b"LIMIT",
22011 b"0",
22012 b"2"
22013 ]),
22014 "*3\r\n:1\r\n$18\r\n0.3566749439387324\r\n$18\r\n0.3566749439387324\r\n"
22015 );
22016 assert_eq!(
22017 f.run(&[
22018 b"FT.AGGREGATE",
22019 b"sx",
22020 b"alpha",
22021 b"WITHSORTKEYS",
22022 b"LOAD",
22023 b"1",
22024 b"@n",
22025 b"LIMIT",
22026 b"0",
22027 b"1"
22028 ]),
22029 "*3\r\n:3\r\n$-1\r\n*2\r\n$1\r\nn\r\n$1\r\n1\r\n"
22030 );
22031 }
22032
22033 #[test]
22036 fn a_normalising_scorer_answers_the_rows_backwards() {
22037 let mut f = Fixture::new();
22038 corpus(&mut f);
22039 assert_eq!(
22040 f.run(&[
22041 b"FT.AGGREGATE",
22042 b"sx",
22043 b"alpha",
22044 b"SCORER",
22045 b"BM25STD.NORM",
22046 b"ADDSCORES",
22047 b"LOAD",
22048 b"1",
22049 b"@n",
22050 b"LIMIT",
22051 b"1",
22052 b"2"
22053 ]),
22054 concat!(
22055 "*3\r\n:3\r\n",
22056 "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n2\r\n",
22057 "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n1\r\n"
22058 )
22059 );
22060 assert_eq!(
22063 f.run(&[
22064 b"FT.AGGREGATE",
22065 b"sx",
22066 b"alpha",
22067 b"SCORER",
22068 b"BM25STD.NORM",
22069 b"LOAD",
22070 b"1",
22071 b"@n",
22072 b"LIMIT",
22073 b"1",
22074 b"2"
22075 ]),
22076 "*3\r\n:3\r\n*2\r\n$1\r\nn\r\n$1\r\n2\r\n*2\r\n$1\r\nn\r\n$1\r\n4\r\n"
22077 );
22078 }
22079
22080 #[test]
22083 fn an_aggregation_answers_a_map_of_five_as_well() {
22084 let mut f = Fixture::new();
22085 corpus(&mut f);
22086 f.out = Out::new(Proto::Resp3);
22087 assert_eq!(
22088 f.run(&[
22089 b"FT.AGGREGATE",
22090 b"sx",
22091 b"alpha",
22092 b"ADDSCORES",
22093 b"WITHSCORES",
22094 b"WITHSORTKEYS",
22095 b"LOAD",
22096 b"1",
22097 b"@n",
22098 b"LIMIT",
22099 b"0",
22100 b"1"
22101 ]),
22102 concat!(
22103 "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
22104 "%4\r\n+score\r\n,0.3566749439387324\r\n+sortkey\r\n_\r\n",
22105 "+extra_attributes\r\n%2\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n",
22106 "$1\r\nn\r\n$1\r\n1\r\n+values\r\n*0\r\n",
22107 "+total_results\r\n:3\r\n+warning\r\n*0\r\n"
22108 )
22109 );
22110 assert_eq!(
22113 f.run(&[
22114 b"FT.AGGREGATE",
22115 b"sx",
22116 b"alpha",
22117 b"NOCONTENT",
22118 b"LIMIT",
22119 b"0",
22120 b"1"
22121 ]),
22122 concat!(
22123 "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
22124 "%1\r\n+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
22125 )
22126 );
22127 }
22128}