1mod args;
55mod arrays;
56mod backup;
57mod bits;
58mod blocking;
59mod bloom;
60mod cms;
61mod cpu;
62mod cuckoo;
63mod geo;
64mod graph;
65mod hashes;
66mod himport;
67mod hll;
68mod json;
69mod keyspace;
70mod lists;
71mod migrate;
72mod scan;
73mod scripting;
74mod search;
75mod server;
76mod sets;
77mod streams;
78mod strings;
79pub mod table;
80mod tdigest;
81mod topk;
82mod ts;
83mod vectors;
84mod vfilter;
85mod zsets;
86
87pub use args::Args;
88pub use blocking::{Parked, Waiters};
89pub use server::parse_memory;
90pub use table::{COMMANDS, Spec, arity_ok, lookup};
91
92use crate::reply::Out;
93use std::cell::Cell;
94use std::path::{Path, PathBuf};
95use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
96use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize};
97use yo_common::lock::Held;
98use yo_common::{Code, Error};
99use yo_kv::cold::Blocks;
100use yo_kv::{Clock, Db, Keyspace};
101use yo_search::Registry;
102
103pub const DATABASES: usize = 16;
110
111const ALL_DATABASES: u64 = if DATABASES == 64 {
118 u64::MAX
119} else {
120 (1u64 << DATABASES) - 1
121};
122const _: () = assert!(DATABASES <= 64);
123
124const EVICT_BUDGET: usize = 64;
136
137const NO_MAXSTORE: u64 = u64::MAX;
145
146const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum Flow {
155 Continue,
157 Close,
159 Block,
166}
167
168#[derive(Debug, Default)]
178pub struct Counter(AtomicU64);
179
180impl Counter {
181 fn bump(&self) {
183 self.0.store(self.get().wrapping_add(1), Relaxed);
184 }
185
186 fn drop_one(&self) {
193 self.0.store(self.get().saturating_sub(1), Relaxed);
194 }
195
196 fn get(&self) -> u64 {
198 self.0.load(Relaxed)
199 }
200
201 fn zero(&self) {
203 self.0.store(0, Relaxed);
204 }
205}
206
207#[derive(Debug, Default)]
213pub struct Stats {
214 clients: Counter,
216 connections: Counter,
218 commands: Counter,
220}
221
222impl Stats {
223 pub fn opened(&self) {
225 self.clients.bump();
226 self.connections.bump();
227 }
228
229 pub fn closed(&self) {
231 self.clients.drop_one();
232 }
233}
234
235#[derive(Debug, Clone, Copy, Default)]
237pub struct Totals {
238 pub clients: u64,
240 pub connections: u64,
242 pub commands: u64,
244}
245
246thread_local! {
247 static SLOT: Cell<usize> = const { Cell::new(usize::MAX) };
255}
256
257#[derive(Debug, Default)]
268#[repr(align(64))]
269struct Local {
270 stats: Stats,
272 cmdstats: CommandStats,
274 dirty: AtomicU64,
283}
284
285impl Local {
286 fn mark(&self, dbs: u64) {
288 self.dirty.store(self.dirty.load(Relaxed) | dbs, Relaxed);
289 }
290}
291
292fn one_thread() -> Box<[Local]> {
294 slots(1)
295}
296
297fn slots(threads: usize) -> Box<[Local]> {
299 (0..threads.max(1)).map(|_| Local::default()).collect()
300}
301
302fn working_dir() -> PathBuf {
309 std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
310}
311
312#[derive(Debug, Clone, Copy, Default)]
321pub struct CommandStat {
322 pub calls: u64,
324 pub rejected: u64,
327 pub failed: u64,
329}
330
331impl CommandStat {
332 const fn seen(&self) -> bool {
338 self.calls != 0 || self.rejected != 0 || self.failed != 0
339 }
340}
341
342#[derive(Debug, Default)]
347struct Row {
348 calls: Counter,
350 rejected: Counter,
352 failed: Counter,
354}
355
356#[derive(Debug)]
363struct CommandStats(Box<[Row]>);
364
365impl Default for CommandStats {
366 fn default() -> CommandStats {
367 CommandStats((0..table::count()).map(|_| Row::default()).collect())
368 }
369}
370
371impl CommandStats {
372 fn at(&self, spec: &'static Spec) -> &Row {
374 &self.0[table::index_of(spec)]
375 }
376}
377
378pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
384
385pub struct Server {
392 dbs: Vec<Db>,
393 width: usize,
399 clock: Clock,
400 started_ms: u64,
401 next_db: usize,
404 dirty: u64,
419 conn_bytes: usize,
421 maxmemory: AtomicU64,
429 store: Option<Box<StoreSource>>,
440 maxstore: AtomicU64,
462 used: usize,
474 evict_db: usize,
480 expire_db: usize,
487 expire_ms: u64,
490 waiters: Waiters,
492 peers: migrate::Peers,
497 locals: Box<[Local]>,
503 claimed: AtomicUsize,
505 dir: PathBuf,
513 backup: backup::State,
518 search: Registry,
528 stopping: AtomicBool,
537}
538
539impl Server {
540 #[must_use]
542 pub fn new() -> Server {
543 let clock = Clock::system();
544 Server {
545 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
546 width: 1,
547 clock,
548 started_ms: clock.now_ms(),
549 next_db: 0,
550 dirty: ALL_DATABASES,
551 conn_bytes: 0,
552 maxmemory: AtomicU64::new(0),
553 store: None,
554 maxstore: AtomicU64::new(NO_MAXSTORE),
555 used: 0,
556 evict_db: 0,
557 expire_db: 0,
558 expire_ms: 0,
559 waiters: Waiters::default(),
560 peers: migrate::Peers::default(),
561 locals: one_thread(),
562 claimed: AtomicUsize::new(0),
563 dir: working_dir(),
564 backup: backup::State::default(),
565 search: Registry::new(),
566 stopping: AtomicBool::new(false),
567 }
568 }
569
570 #[must_use]
581 pub fn with_width(width: usize) -> Server {
582 let clock = Clock::system();
583 let mut server = Server::new();
584 server.dbs = (0..DATABASES)
585 .map(|_| Db::with_clock(clock, width))
586 .collect();
587 server.width = server.dbs[0].width();
588 server
589 }
590
591 #[must_use]
593 pub fn with_clock(clock: Clock) -> Server {
594 Server {
595 dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
596 width: 1,
597 clock,
598 started_ms: clock.now_ms(),
599 next_db: 0,
600 dirty: ALL_DATABASES,
601 conn_bytes: 0,
602 maxmemory: AtomicU64::new(0),
603 store: None,
604 maxstore: AtomicU64::new(NO_MAXSTORE),
605 used: 0,
606 evict_db: 0,
607 expire_db: 0,
608 expire_ms: 0,
609 waiters: Waiters::default(),
610 peers: migrate::Peers::default(),
611 locals: one_thread(),
612 claimed: AtomicUsize::new(0),
613 dir: working_dir(),
614 backup: backup::State::default(),
615 search: Registry::new(),
616 stopping: AtomicBool::new(false),
617 }
618 }
619
620 pub fn striped(&mut self, i: usize) -> &mut Db {
638 self.dirty |= 1u64 << i;
639 &mut self.dbs[i]
640 }
641
642 fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
649 self.dbs
650 .iter()
651 .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
652 }
653
654 fn keyspaces_mut(&mut self) -> impl Iterator<Item = &mut Keyspace> {
656 self.dbs.iter_mut().flat_map(Db::stripes_mut)
657 }
658
659 const fn slots(&self) -> usize {
665 DATABASES * self.width
666 }
667
668 const fn slot_db(&self, i: usize) -> usize {
670 i / self.width
671 }
672
673 fn slot_mut(&mut self, i: usize) -> &mut Keyspace {
675 let (db, stripe) = (i / self.width, i % self.width);
676 self.dbs[db].stripe_mut(stripe)
677 }
678
679 fn slot(&self, i: usize) -> Held<'_, Keyspace> {
681 let (db, stripe) = (i / self.width, i % self.width);
682 self.dbs[db].hold_stripe(stripe)
683 }
684
685 #[must_use]
687 pub fn dir(&self) -> &Path {
688 &self.dir
689 }
690
691 pub fn set_dir(&mut self, dir: PathBuf) {
698 self.dir = dir;
699 }
700
701 pub fn backup_expire(&mut self) {
707 backup::expire(self);
708 }
709
710 pub fn stop(&self) {
716 self.stopping.store(true, Release);
717 }
718
719 #[must_use]
725 pub fn stopping(&self) -> bool {
726 self.stopping.load(Acquire)
727 }
728
729 #[must_use]
736 pub fn striped_ref(&self, i: usize) -> &Db {
737 &self.dbs[i]
738 }
739
740 fn settings(&self) -> Held<'_, Keyspace> {
748 self.dbs[0].hold_stripe(0)
749 }
750
751 pub fn refresh_clock(&mut self) {
757 self.clock.refresh();
758 let now = self.clock.now_ms();
759 for db in &mut self.dbs {
760 db.set_clock_ms(now);
761 }
762 }
763
764 pub fn advance_clock_ms(&mut self, ms: u64) {
770 let now = self.clock.now_ms() + ms;
771 self.set_clock_ms(now);
772 }
773
774 pub fn set_clock_ms(&mut self, ms: u64) {
782 self.clock.set(ms);
783 for db in &mut self.dbs {
784 db.set_clock_ms(ms);
785 }
786 }
787
788 #[must_use]
790 pub fn uptime_secs(&self) -> u64 {
791 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
792 }
793
794 #[must_use]
802 pub fn memory_bytes(&self) -> usize {
803 self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes
804 }
805
806 #[must_use]
812 pub fn dataset_bytes(&self) -> usize {
813 self.keyspaces()
814 .map(|db| db.map().arena().live_bytes() as usize)
815 .sum()
816 }
817
818 #[must_use]
820 pub fn arena_bytes(&self) -> usize {
821 self.keyspaces()
822 .map(|db| db.map().arena().reserved_bytes() as usize)
823 .sum()
824 }
825
826 #[must_use]
828 pub fn index_bytes(&self) -> usize {
829 self.keyspaces()
830 .map(|db| db.map().index().memory_bytes())
831 .sum()
832 }
833
834 #[must_use]
841 pub fn compaction(&self) -> yo_kv::Compaction {
842 self.keyspaces().map(|db| db.map().compaction()).fold(
843 yo_kv::Compaction::default(),
844 |a, b| yo_kv::Compaction {
845 walked: a.walked + b.walked,
846 moved: a.moved + b.moved,
847 bytes: a.bytes + b.bytes,
848 },
849 )
850 }
851
852 #[must_use]
854 pub fn segment_count(&self) -> usize {
855 self.keyspaces()
856 .map(|db| db.map().arena().resident_segments())
857 .sum()
858 }
859
860 #[must_use]
862 pub const fn conn_bytes(&self) -> usize {
863 self.conn_bytes
864 }
865
866 pub fn note_conn_bytes(&mut self, delta: isize) {
874 self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
875 }
876
877 #[must_use]
879 pub fn expired_keys(&self) -> u64 {
880 self.keyspaces().map(|db| db.expired_keys()).sum()
881 }
882
883 #[must_use]
885 pub fn evicted_keys(&self) -> u64 {
886 self.keyspaces().map(|db| db.evicted_keys()).sum()
887 }
888
889 pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
895 (0..table::count())
896 .map(|at| (table::name_at(at), self.command_stat(at)))
897 .filter(|(_, row)| row.seen())
898 }
899
900 fn command_stat(&self, at: usize) -> CommandStat {
902 let mut sum = CommandStat::default();
903 for thread in &self.locals {
904 let row = &thread.cmdstats.0[at];
905 sum.calls += row.calls.get();
906 sum.rejected += row.rejected.get();
907 sum.failed += row.failed.get();
908 }
909 sum
910 }
911
912 pub fn counted(&self) -> &Stats {
921 &self.mine().stats
922 }
923
924 fn mine(&self) -> &Local {
926 let mut slot = SLOT.get();
927 if slot == usize::MAX {
928 slot = self.claimed.fetch_add(1, Relaxed);
929 SLOT.set(slot);
930 }
931 &self.locals[slot % self.locals.len()]
932 }
933
934 #[must_use]
936 pub fn totals(&self) -> Totals {
937 let mut sum = Totals::default();
938 for thread in &self.locals {
939 sum.clients += thread.stats.clients.get();
940 sum.connections += thread.stats.connections.get();
941 sum.commands += thread.stats.commands.get();
942 }
943 sum
944 }
945
946 pub fn reset_stats(&self) {
953 for thread in &self.locals {
954 thread.stats.connections.zero();
955 thread.stats.commands.zero();
956 }
957 }
958
959 pub fn set_threads(&mut self, threads: usize) {
966 self.locals = slots(threads);
967 self.claimed = AtomicUsize::new(0);
968 }
969
970 #[must_use]
972 pub fn maxmemory(&self) -> u64 {
973 self.maxmemory.load(Relaxed)
974 }
975
976 pub fn set_maxmemory(&mut self, bytes: u64) {
990 self.maxmemory.store(bytes, Relaxed);
991 for db in &mut self.dbs {
992 db.track_memory(bytes != 0);
993 }
994 self.used = self.settled_memory();
995 }
996
997 pub fn set_store_source(
1007 &mut self,
1008 source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
1009 ) {
1010 self.store = Some(Box::new(source));
1011 }
1012
1013 #[must_use]
1015 pub const fn has_store_source(&self) -> bool {
1016 self.store.is_some()
1017 }
1018
1019 fn attach_store(&mut self, at: usize) {
1026 if self.slot(at).store_bytes().is_some() {
1027 return;
1028 }
1029 let Some(source) = self.store.as_mut() else {
1030 return;
1031 };
1032 if let Some(blocks) = source(at) {
1033 self.slot_mut(at).attach(blocks);
1034 }
1035 }
1036
1037 #[must_use]
1039 pub fn maxstore(&self) -> Option<u64> {
1040 match self.maxstore.load(Relaxed) {
1041 NO_MAXSTORE => None,
1042 bytes => Some(bytes),
1043 }
1044 }
1045
1046 pub fn set_maxstore(&self, bytes: Option<u64>) {
1052 self.maxstore.store(bytes.unwrap_or(NO_MAXSTORE), Relaxed);
1053 }
1054
1055 #[must_use]
1061 pub fn store_bytes(&self) -> u64 {
1062 self.keyspaces().filter_map(|db| db.store_bytes()).sum()
1063 }
1064
1065 #[must_use]
1080 pub fn cold_stats(&self) -> yo_kv::tier::Stats {
1081 let mut total = yo_kv::tier::Stats::default();
1082 for db in self.keyspaces() {
1083 let Some(tier) = db.tier() else { continue };
1084 let s = tier.stats();
1085 total.demoted += s.demoted;
1086 total.promoted += s.promoted;
1087 total.faults += s.faults;
1088 total.served += s.served;
1089 total.bytes_out += s.bytes_out;
1090 total.bytes_in += s.bytes_in;
1091 }
1092 total
1093 }
1094
1095 #[must_use]
1102 pub fn regime(&self) -> &'static str {
1103 if (0..self.slots()).any(|at| self.migrates(at)) {
1104 "migrate"
1105 } else {
1106 "evict"
1107 }
1108 }
1109
1110 fn migrates(&self, at: usize) -> bool {
1122 let cap = self.maxstore();
1123 if cap == Some(0) {
1124 return false;
1125 }
1126 let bytes = self.slot(at).store_bytes();
1130 match bytes {
1131 Some(held) => cap.is_none_or(|cap| held < cap),
1132 None => self.store.is_some(),
1136 }
1137 }
1138
1139 pub fn refresh_memory(&mut self) {
1144 if self.maxmemory() != 0 {
1145 self.used = self.settled_memory();
1146 }
1147 }
1148
1149 fn settled_memory(&mut self) -> usize {
1156 self.keyspaces_mut()
1157 .map(Keyspace::settled_memory_bytes)
1158 .sum::<usize>()
1159 + self.conn_bytes
1160 }
1161
1162 pub fn make_room(&mut self) -> bool {
1196 let limit = self.maxmemory();
1197 if limit == 0 || self.used as u64 <= limit {
1198 return true;
1199 }
1200 self.used = self.settled_memory();
1205 let mut budget = EVICT_BUDGET;
1206 while self.used as u64 > limit {
1207 let over = self.used - limit as usize;
1208 if !self.relieve_step(over) {
1209 return false;
1210 }
1211 self.compact_hard_step();
1212 self.used = self.settled_memory();
1213 budget -= 1;
1214 if budget == 0 {
1215 break;
1216 }
1217 }
1218 true
1219 }
1220
1221 fn relieve_step(&mut self, over: usize) -> bool {
1238 for turn in 0..self.slots() {
1239 let i = (self.evict_db + turn) % self.slots();
1240 let used = !self.slot(i).is_empty();
1243 let gave = if used && self.migrates(i) {
1244 self.attach_store(i);
1245 self.slot_mut(i)
1250 .relieve(over)
1251 .is_ok_and(yo_kv::tier::Relief::made_room)
1252 } else {
1253 self.slot_mut(i).evict_one()
1254 };
1255 if gave {
1256 self.evict_db = (i + 1) % self.slots();
1257 self.dirty |= 1u64 << self.slot_db(i);
1258 return true;
1259 }
1260 }
1261 false
1262 }
1263
1264 pub fn expire_slice(&mut self, budget: usize) -> usize {
1279 let now = self.clock.now_ms();
1280 if now == self.expire_ms {
1281 return 0;
1282 }
1283 self.expire_ms = now;
1284 self.expire_step(budget)
1285 }
1286
1287 pub fn expire_step(&mut self, budget: usize) -> usize {
1303 let mut spent = 0;
1304 for turn in 0..self.slots() {
1305 if spent >= budget {
1306 break;
1307 }
1308 let i = (self.expire_db + turn) % self.slots();
1309 let c = self.slot_mut(i).expire_cycle(budget - spent);
1310 spent += c.examined;
1311 if c.expired > 0 {
1312 self.expire_db = (i + 1) % self.slots();
1313 self.dirty |= 1u64 << self.slot_db(i);
1314 }
1315 }
1316 spent
1317 }
1318
1319 fn compact_hard_step(&mut self) -> Option<usize> {
1325 for turn in 0..self.slots() {
1326 let i = (self.next_db + turn) % self.slots();
1327 if let Some(moved) = self.slot_mut(i).compact_hard() {
1328 self.next_db = (i + 1) % self.slots();
1329 return Some(moved);
1330 }
1331 }
1332 None
1333 }
1334
1335 fn collect_marks(&mut self) {
1344 let mut marked = 0;
1345 for thread in &self.locals {
1346 marked |= thread.dirty.swap(0, Relaxed);
1347 }
1348 self.dirty |= marked;
1349 }
1350
1351 pub fn compact_step(&mut self) -> Option<usize> {
1364 self.collect_marks();
1365 for turn in 0..self.slots() {
1366 let i = (self.next_db + turn) % self.slots();
1367 let at = self.slot_db(i);
1371 if self.dirty & (1 << at) == 0 {
1372 continue;
1373 }
1374 if let Some(moved) = self.slot_mut(i).compact_step() {
1375 self.next_db = (i + 1) % self.slots();
1376 return Some(moved);
1377 }
1378 if i % self.width == self.width - 1 {
1382 self.dirty &= !(1u64 << at);
1383 }
1384 }
1385 None
1386 }
1387}
1388
1389impl Default for Server {
1390 fn default() -> Server {
1391 Server::new()
1392 }
1393}
1394
1395pub struct Session {
1397 db: usize,
1398 id: u64,
1399 name: Vec<u8>,
1400 sets: himport::Fieldsets,
1406}
1407
1408impl Session {
1409 #[must_use]
1411 pub fn new(id: u64) -> Session {
1412 Session {
1413 db: 0,
1414 id,
1415 name: Vec::new(),
1416 sets: himport::Fieldsets::default(),
1417 }
1418 }
1419
1420 #[must_use]
1422 pub const fn id(&self) -> u64 {
1423 self.id
1424 }
1425
1426 #[must_use]
1428 pub const fn db(&self) -> usize {
1429 self.db
1430 }
1431
1432 #[must_use]
1434 pub fn name(&self) -> &[u8] {
1435 &self.name
1436 }
1437
1438 pub fn reset(&mut self) {
1443 self.db = 0;
1444 self.name.clear();
1445 self.sets.clear();
1449 }
1450
1451 fn set_name(&mut self, name: &[u8]) {
1453 yo_alloc::allow(|| {
1454 self.name.clear();
1455 self.name.extend_from_slice(name);
1456 });
1457 }
1458}
1459
1460pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1465 if args.is_empty() {
1468 return Flow::Continue;
1469 }
1470 resolved(server, session, lookup(args.name()), args, out)
1471}
1472
1473pub fn resolved(
1485 server: &mut Server,
1486 session: &mut Session,
1487 spec: Option<&'static Spec>,
1488 args: Args<'_>,
1489 out: &mut Out,
1490) -> Flow {
1491 if args.is_empty() {
1492 return Flow::Continue;
1493 }
1494 server.mine().stats.commands.bump();
1495
1496 let Some(spec) = spec else {
1497 write_error(out, &args::unknown_command(args));
1498 return Flow::Continue;
1499 };
1500 if !arity_ok(spec, args.len()) {
1501 server.mine().cmdstats.at(spec).rejected.bump();
1502 write_error(out, &args::wrong_arity(spec.name));
1503 return Flow::Continue;
1504 }
1505
1506 if server.maxmemory() != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1516 server.mine().cmdstats.at(spec).rejected.bump();
1517 out.error_line(b"OOM ", OOM);
1518 return Flow::Continue;
1519 }
1520
1521 server.mine().mark(match spec.group {
1528 "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1529 | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1530 1u64 << session.db
1531 }
1532 _ => ALL_DATABASES,
1533 });
1534
1535 let mark = out.len();
1536 let done = if spec.flags.contains(&"blocking") {
1543 blocking::execute(server, session, spec, args, out)
1544 } else {
1545 match spec.group {
1546 "string" => {
1547 let db = session.db;
1548 strings::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1549 }
1550 "bitmap" => {
1554 let db = session.db;
1555 bits::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1556 }
1557 "hyperloglog" => {
1560 let db = session.db;
1561 hll::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1562 }
1563 "set" => {
1564 let db = session.db;
1565 sets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1566 }
1567 "hash" if spec.name == "himport" => {
1572 let db = session.db;
1573 himport::execute(&server.dbs[db], &mut session.sets, args, out)
1574 .map(|()| Flow::Continue)
1575 }
1576 "hash" => {
1577 let db = session.db;
1578 hashes::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1579 }
1580 "list" => {
1581 let db = session.db;
1582 lists::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1583 }
1584 "zset" => {
1585 let db = session.db;
1586 zsets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1587 }
1588 "geo" => {
1592 let db = session.db;
1593 geo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1594 }
1595 "array" => {
1596 let db = session.db;
1597 arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1598 }
1599 "graph" => {
1600 let db = session.db;
1601 graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1602 }
1603 "json" => {
1607 let db = session.db;
1608 json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1609 }
1610 "vector" => {
1611 let db = session.db;
1612 vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1613 }
1614 "bloom" => {
1615 let db = session.db;
1616 bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1617 }
1618 "cuckoo" => {
1619 let db = session.db;
1620 cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1621 }
1622 "cms" => {
1623 let db = session.db;
1624 cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1625 }
1626 "topk" => {
1627 let db = session.db;
1628 topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1629 }
1630 "tdigest" => {
1631 let db = session.db;
1632 tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1633 }
1634 "ts" => {
1635 let db = session.db;
1636 ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1637 }
1638 "stream" => {
1643 let db = session.db;
1644 let now = server.now_ms();
1645 streams::execute(&server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1646 }
1647 "keyspace" if spec.name == "migrate" => {
1651 migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1652 }
1653 "keyspace" => {
1656 keyspace::execute(&server.dbs, session.db, spec, args, out).map(|()| Flow::Continue)
1657 }
1658 "search" => {
1661 search::execute(&mut server.search, spec, args, out).map(|()| Flow::Continue)
1662 }
1663 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1664 _ => server::execute(server, session, spec, args, out),
1665 }
1666 };
1667 let flow = match done {
1668 Ok(flow) => flow,
1669 Err(e) => {
1670 out.truncate(mark);
1671 write_error(out, &e);
1672 Flow::Continue
1673 }
1674 };
1675
1676 let row = server.mine().cmdstats.at(spec);
1687 row.calls.bump();
1688 if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1689 row.failed.bump();
1690 }
1691 flow
1692}
1693
1694fn write_error(out: &mut Out, e: &Error) {
1704 let prefix: &[u8] = match e.code() {
1705 Code::WrongType => b"WRONGTYPE ",
1706 Code::Corrupt => b"INVALIDOBJ ",
1710 _ => b"ERR ",
1711 };
1712 out.error_line(prefix, e.message().as_bytes());
1713}
1714
1715#[cfg(test)]
1716mod tests {
1717 use super::*;
1718 use crate::proto::{Limits, Proto};
1719 use crate::request::Argv;
1720
1721 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1726 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1727 for p in parts {
1728 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1729 wire.extend_from_slice(p);
1730 wire.extend_from_slice(b"\r\n");
1731 }
1732 wire
1733 }
1734
1735 struct Fixture {
1737 server: Server,
1738 session: Session,
1739 argv: Argv,
1740 out: Out,
1741 }
1742
1743 impl Fixture {
1744 fn new() -> Fixture {
1745 Fixture::on(Server::new())
1746 }
1747
1748 fn striped(width: usize) -> Fixture {
1750 Fixture::on(Server::with_width(width))
1751 }
1752
1753 fn on(server: Server) -> Fixture {
1754 Fixture {
1755 server,
1756 session: Session::new(7),
1757 argv: Argv::new(),
1758 out: Out::new(Proto::Resp2),
1759 }
1760 }
1761
1762 fn run(&mut self, parts: &[&[u8]]) -> String {
1764 self.flow(parts).1
1765 }
1766
1767 fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1773 let wire = encode(parts);
1774 self.argv.decode(&wire, &Limits::default()).unwrap();
1775 self.out.clear();
1776 execute(
1777 &mut self.server,
1778 &mut self.session,
1779 Args::new(&self.argv, &wire),
1780 &mut self.out,
1781 );
1782 self.out.as_slice().to_vec()
1783 }
1784
1785 fn advance(&mut self, ms: u64) {
1787 self.server.advance_clock_ms(ms);
1788 }
1789
1790 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1792 let wire = encode(parts);
1793 self.argv.decode(&wire, &Limits::default()).unwrap();
1794 self.out.clear();
1795 let flow = execute(
1796 &mut self.server,
1797 &mut self.session,
1798 Args::new(&self.argv, &wire),
1799 &mut self.out,
1800 );
1801 (
1802 flow,
1803 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1804 )
1805 }
1806 }
1807
1808 #[test]
1812 fn rewriting_the_same_keys_does_not_grow_the_server() {
1813 let mut f = Fixture::new();
1814 let val = vec![b'v'; 1024];
1815 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1816
1817 for k in &keys {
1818 f.run(&[b"SET", k, &val]);
1819 }
1820 f.server.compact_step();
1821 let after_first = f.server.memory_bytes();
1822
1823 for _ in 0..500 {
1828 for k in &keys {
1829 f.run(&[b"SET", k, &val]);
1830 }
1831 f.server.compact_step();
1832 }
1833
1834 assert!(
1835 f.server.memory_bytes() <= after_first * 2,
1836 "held {} after five hundred passes against {after_first} after one",
1837 f.server.memory_bytes()
1838 );
1839 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1840 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1841 }
1842
1843 #[test]
1857 fn a_database_nobody_started_on_is_still_collected() {
1858 let mut f = Fixture::new();
1859 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1860 let val = vec![b'v'; 1024];
1861 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1862
1863 for k in &keys {
1864 f.run(&[b"SET", k, &val]);
1865 }
1866 while f.server.compact_step().is_some() {}
1867 assert_eq!(
1868 f.server.dirty & (1 << 9),
1869 0,
1870 "database nine was drained and should not be asked again until it is written to"
1871 );
1872 let after_first = f.server.memory_bytes();
1873
1874 for _ in 0..500 {
1875 for k in &keys {
1876 f.run(&[b"SET", k, &val]);
1877 }
1878 f.server.compact_step();
1879 }
1880
1881 assert!(
1882 f.server.memory_bytes() <= after_first * 2,
1883 "held {} after five hundred passes against {after_first} after one",
1884 f.server.memory_bytes()
1885 );
1886 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1887 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1888 f.run(&[b"SELECT", b"0"]);
1890 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1891 }
1892
1893 #[test]
1894 fn a_command_goes_from_bytes_to_bytes() {
1895 let mut f = Fixture::new();
1896 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1897 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1898 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1899 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1900 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1902 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1903 }
1904
1905 #[test]
1906 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1907 let mut f = Fixture::new();
1908 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1909 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1912 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1913 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1914 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1916 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1917 }
1918
1919 #[test]
1920 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1921 let mut f = Fixture::new();
1922 f.run(&[b"SET", b"k", b"v"]);
1923 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1926 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1927 }
1928
1929 #[test]
1930 fn touch_counts_the_way_exists_counts() {
1931 let mut f = Fixture::new();
1932 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1933 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1934 assert_eq!(
1935 f.run(&[b"TOUCH", b"a", b"a"]),
1936 ":2\r\n",
1937 "twice counts twice"
1938 );
1939 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1940 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1941 }
1942
1943 #[test]
1944 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1945 let mut f = Fixture::new();
1946 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1947 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1948
1949 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1950 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1951 assert_eq!(
1952 f.run(&[b"TTL", b"b"]),
1953 ":100\r\n",
1954 "the source's and not b's"
1955 );
1956 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1957 }
1958
1959 #[test]
1960 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1961 let mut f = Fixture::new();
1962 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1963 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1966 }
1967
1968 #[test]
1969 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1970 let mut f = Fixture::new();
1971 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1972
1973 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1974 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1975 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1978 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1979 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1980 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1981 }
1982
1983 #[test]
1984 fn renaming_a_set_does_not_touch_a_member() {
1985 let mut f = Fixture::new();
1986 for i in 0..300 {
1987 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1988 }
1989 let before = f.server.memory_bytes();
1990
1991 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1992 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1993 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1994 assert!(
1995 f.server.memory_bytes().abs_diff(before) < 256,
1996 "the members were copied: {} against {before}",
1997 f.server.memory_bytes()
1998 );
1999 }
2000
2001 #[test]
2002 fn a_copy_is_a_second_value_and_not_a_second_name() {
2003 let mut f = Fixture::new();
2004 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
2005
2006 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
2007 f.run(&[b"SADD", b"t", b"m3"]);
2008 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
2009 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
2010 }
2011
2012 #[test]
2022 fn every_type_can_be_copied() {
2023 let mut f = Fixture::new();
2024 f.run(&[b"SET", b"str", b"v1"]);
2025 f.run(&[b"SADD", b"set", b"m1"]);
2026 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2027 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
2028 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
2029
2030 for name in [
2031 &b"str"[..],
2032 &b"set"[..],
2033 &b"hash"[..],
2034 &b"list"[..],
2035 &b"zset"[..],
2036 ] {
2037 let dst = [name, b":copy"].concat();
2038 assert_eq!(
2039 f.run(&[b"COPY", name, &dst]),
2040 ":1\r\n",
2041 "copying {}",
2042 String::from_utf8_lossy(name)
2043 );
2044 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
2045 }
2046
2047 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
2048 let mut want = String::from("*2\r\n");
2049 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
2050 want
2051 });
2052 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
2053
2054 f.run(&[b"RPUSH", b"list:copy", b"c"]);
2056 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
2057 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
2058 }
2059
2060 #[test]
2061 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
2062 let mut f = Fixture::new();
2063 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2064 f.run(&[b"SET", b"b", b"v2"]);
2065
2066 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
2067 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2068 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
2069 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2070 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
2071 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
2072 }
2073
2074 #[test]
2075 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
2076 let mut f = Fixture::new();
2077 f.run(&[b"SET", b"a", b"v1"]);
2078
2079 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
2082 f.run(&[b"SELECT", b"1"]);
2083 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
2084 assert_eq!(
2085 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
2086 ":0\r\n",
2087 "taken"
2088 );
2089 assert_eq!(
2090 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
2091 ":1\r\n"
2092 );
2093 }
2094
2095 #[test]
2096 fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
2097 let mut f = Fixture::new();
2098 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2099 assert_eq!(
2100 f.run(&[b"SORT", b"l"]),
2101 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2102 );
2103 assert_eq!(
2105 f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
2106 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2107 );
2108 assert_eq!(
2109 f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
2110 "*1\r\n$1\r\n2\r\n"
2111 );
2112 }
2113
2114 #[test]
2115 fn sort_reads_a_key_per_element_for_by_and_for_get() {
2116 let mut f = Fixture::new();
2117 f.run(&[b"RPUSH", b"l", b"a", b"b"]);
2118 f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
2119 assert_eq!(
2122 f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
2123 "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
2124 );
2125 }
2126
2127 #[test]
2128 fn sort_store_writes_a_list_and_answers_its_length() {
2129 let mut f = Fixture::new();
2130 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2131 assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
2132 assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
2133 assert_eq!(
2134 f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
2135 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2136 );
2137 assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
2140 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
2141 }
2142
2143 #[test]
2144 fn sort_ro_does_not_know_the_word_store() {
2145 let mut f = Fixture::new();
2146 f.run(&[b"RPUSH", b"l", b"2", b"1"]);
2147 assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
2148 assert_eq!(
2149 f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
2150 "-ERR syntax error\r\n"
2151 );
2152 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2153 }
2154
2155 #[test]
2156 fn sort_refuses_what_it_cannot_sort() {
2157 let mut f = Fixture::new();
2158 assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
2159 f.run(&[b"SET", b"s", b"x"]);
2160 assert_eq!(
2161 f.run(&[b"SORT", b"s"]),
2162 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
2163 );
2164 f.run(&[b"RPUSH", b"words", b"one", b"two"]);
2165 assert_eq!(
2166 f.run(&[b"SORT", b"words"]),
2167 "-ERR One or more scores can't be converted into double\r\n"
2168 );
2169 assert_eq!(
2170 f.run(&[b"SORT", b"words", b"ALPHA"]),
2171 "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
2172 );
2173 assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
2174 }
2175
2176 #[test]
2177 fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
2178 let mut f = Fixture::new();
2179 assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
2180 assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
2181 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2182 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2183 assert_eq!(
2184 f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
2185 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2186 );
2187 assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
2190 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2191 }
2192
2193 #[test]
2194 fn move_answers_zero_when_either_end_says_no() {
2195 let mut f = Fixture::new();
2196 assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
2197 assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
2198 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2199 assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
2200 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2201 assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
2204 assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
2205 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2206 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
2207 }
2208
2209 #[test]
2210 fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
2211 let mut f = Fixture::new();
2212 assert_eq!(
2213 f.run(&[b"MOVE", b"a", b"0"]),
2214 "-ERR source and destination objects are the same\r\n"
2215 );
2216 assert_eq!(
2217 f.run(&[b"MOVE", b"a", b"99"]),
2218 "-ERR DB index is out of range\r\n"
2219 );
2220 assert_eq!(
2221 f.run(&[b"MOVE", b"a", b"-1"]),
2222 "-ERR DB index is out of range\r\n"
2223 );
2224 assert_eq!(
2225 f.run(&[b"MOVE", b"a", b"x"]),
2226 "-ERR value is not an integer or out of range\r\n"
2227 );
2228 }
2229
2230 #[test]
2231 fn swapdb_swaps_what_two_connections_would_see() {
2232 let mut f = Fixture::new();
2233 assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
2234 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2235 assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
2236 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2237
2238 assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
2239 assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
2241 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2242 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2243 assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
2245 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2246 }
2247
2248 #[test]
2249 fn swapdb_says_which_index_it_could_not_read() {
2250 let mut f = Fixture::new();
2251 assert_eq!(
2252 f.run(&[b"SWAPDB", b"x", b"1"]),
2253 "-ERR invalid first DB index\r\n"
2254 );
2255 assert_eq!(
2256 f.run(&[b"SWAPDB", b"0", b"y"]),
2257 "-ERR invalid second DB index\r\n"
2258 );
2259 assert_eq!(
2263 f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2264 "-ERR invalid first DB index\r\n"
2265 );
2266 assert_eq!(
2267 f.run(&[b"SWAPDB", b"0", b"99"]),
2268 "-ERR DB index is out of range\r\n"
2269 );
2270 assert_eq!(
2271 f.run(&[b"SWAPDB", b"-1", b"0"]),
2272 "-ERR DB index is out of range\r\n"
2273 );
2274 }
2275
2276 #[test]
2277 fn wait_answers_zero_replicas_without_waiting() {
2278 let mut f = Fixture::new();
2279 assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2280 assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2281 assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2284 assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2287 assert_eq!(
2288 f.run(&[b"WAIT", b"x", b"0"]),
2289 "-ERR value is not an integer or out of range\r\n"
2290 );
2291 assert_eq!(
2292 f.run(&[b"WAIT", b"0", b"-1"]),
2293 "-ERR timeout is negative\r\n"
2294 );
2295 assert_eq!(
2296 f.run(&[b"WAIT", b"0", b"1.5"]),
2297 "-ERR timeout is not an integer or out of range\r\n"
2298 );
2299 }
2300
2301 #[test]
2302 fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2303 let mut f = Fixture::new();
2304 assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2305 assert_eq!(
2306 f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2307 "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2308 );
2309 assert_eq!(
2310 f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2311 "-ERR value is out of range, value must between 0 and 1\r\n"
2312 );
2313 assert_eq!(
2314 f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2315 "-ERR value is out of range, must be positive\r\n"
2316 );
2317 assert_eq!(
2320 f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2321 "-ERR timeout is negative\r\n"
2322 );
2323 }
2324
2325 fn payload(reply: &[u8]) -> Vec<u8> {
2329 let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2330 reply[head + 2..reply.len() - 2].to_vec()
2331 }
2332
2333 #[test]
2334 fn a_value_survives_a_dump_and_a_restore() {
2335 let mut f = Fixture::new();
2336 f.run(&[b"SET", b"s", b"hello"]);
2337 f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2338 f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2339 f.run(&[b"SADD", b"u", b"x", b"y"]);
2340 f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2341 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2342
2343 for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2344 let mut copy = key.to_vec();
2345 copy.push(b'2');
2346 let bytes = payload(&f.raw(&[b"DUMP", key]));
2347 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
2348 assert_eq!(f.run(&[b"TYPE", ©]), f.run(&[b"TYPE", key]));
2349 }
2350
2351 assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2352 assert_eq!(
2353 f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2354 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2355 );
2356 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2357 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2358 assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2359 assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2360 assert_eq!(
2363 f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2364 f.run(&[b"OBJECT", b"ENCODING", b"t"])
2365 );
2366 }
2367
2368 #[test]
2369 fn a_dumped_hash_keeps_its_field_deadlines() {
2370 let mut f = Fixture::new();
2371 f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2372 assert_eq!(
2373 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2374 "*1\r\n:1\r\n"
2375 );
2376 let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2377 assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2378 assert_eq!(
2379 f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2380 "*2\r\n:-1\r\n:100\r\n"
2381 );
2382 }
2383
2384 #[test]
2385 fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2386 let mut f = Fixture::new();
2387 f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2388 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2389 assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2390 assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2391 assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2392 assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2393 assert_eq!(
2396 f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2397 "+OK\r\n"
2398 );
2399 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2400 }
2401
2402 #[test]
2403 fn dump_answers_nothing_for_a_key_that_is_not_there() {
2404 let mut f = Fixture::new();
2405 assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2406 f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2407 f.advance(50);
2408 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2409 }
2410
2411 #[test]
2412 fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2413 let mut f = Fixture::new();
2414 f.run(&[b"SET", b"a", b"first"]);
2415 f.run(&[b"SET", b"b", b"second"]);
2416 let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2417 assert_eq!(
2418 f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2419 "-BUSYKEY Target key name already exists.\r\n"
2420 );
2421 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2422 assert_eq!(
2423 f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2424 "+OK\r\n"
2425 );
2426 assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2427 }
2428
2429 #[test]
2433 fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2434 let mut f = Fixture::new();
2435 f.run(&[b"SET", b"a", b"v"]);
2436 assert_eq!(
2437 f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2438 "-BUSYKEY Target key name already exists.\r\n"
2439 );
2440 assert_eq!(
2443 f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2444 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2445 );
2446 }
2447
2448 #[test]
2449 fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2450 let mut f = Fixture::new();
2451 f.run(&[b"SET", b"a", b"hello"]);
2452 let good = payload(&f.raw(&[b"DUMP", b"a"]));
2453
2454 let mut flipped = good.clone();
2455 flipped[2] ^= 0x40;
2456 assert_eq!(
2457 f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2458 "-ERR DUMP payload version or checksum are wrong\r\n"
2459 );
2460 assert_eq!(
2461 f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2462 "-ERR DUMP payload version or checksum are wrong\r\n"
2463 );
2464 let mut truncated = good[..1].to_vec();
2468 truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2469 let crc = yo_common::crc::crc64(0, &truncated);
2470 truncated.extend_from_slice(&crc.to_le_bytes());
2471 assert_eq!(
2472 f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2473 "-ERR Bad data format\r\n"
2474 );
2475 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2476 }
2477
2478 #[test]
2479 fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2480 let mut f = Fixture::new();
2481 f.run(&[b"SET", b"a", b"v"]);
2482 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2483 assert_eq!(
2484 f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2485 "-ERR Invalid TTL value, must be >= 0\r\n"
2486 );
2487 assert_eq!(
2488 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2489 "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2490 );
2491 assert_eq!(
2492 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2493 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2494 );
2495 assert_eq!(
2497 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2498 "+OK\r\n"
2499 );
2500 assert_eq!(
2501 f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2502 "+OK\r\n"
2503 );
2504 }
2505
2506 #[test]
2510 fn restore_takes_idletime_or_freq_and_not_both() {
2511 let mut f = Fixture::new();
2512 f.run(&[b"SET", b"a", b"v"]);
2513 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2514 assert_eq!(
2515 f.run(&[
2516 b"RESTORE",
2517 b"b",
2518 b"0",
2519 &bytes,
2520 b"IDLETIME",
2521 b"1",
2522 b"FREQ",
2523 b"2"
2524 ]),
2525 "-ERR syntax error\r\n"
2526 );
2527 assert_eq!(
2528 f.run(&[
2529 b"RESTORE",
2530 b"b",
2531 b"0",
2532 &bytes,
2533 b"FREQ",
2534 b"2",
2535 b"IDLETIME",
2536 b"1"
2537 ]),
2538 "-ERR syntax error\r\n"
2539 );
2540 assert_eq!(
2541 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2542 "-ERR syntax error\r\n"
2543 );
2544 assert_eq!(
2545 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2546 "-ERR syntax error\r\n"
2547 );
2548 }
2549
2550 #[test]
2551 fn copy_checks_its_options_before_it_looks_for_anything() {
2552 let mut f = Fixture::new();
2553 assert_eq!(
2556 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2557 "-ERR DB index is out of range\r\n"
2558 );
2559 assert_eq!(
2560 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2561 "-ERR DB index is out of range\r\n"
2562 );
2563 assert_eq!(
2564 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2565 "-ERR value is not an integer or out of range\r\n"
2566 );
2567 assert_eq!(
2568 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2569 "-ERR syntax error\r\n"
2570 );
2571 assert_eq!(
2572 f.run(&[b"COPY", b"a", b"a"]),
2573 "-ERR source and destination objects are the same\r\n"
2574 );
2575 assert_eq!(
2577 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2578 ":0\r\n"
2579 );
2580 }
2581
2582 #[test]
2583 fn time_is_two_bulk_strings_and_moves() {
2584 let mut f = Fixture::new();
2585 let first = f.run(&[b"TIME"]);
2586 assert!(first.starts_with("*2\r\n$"), "got {first}");
2587 let parts: Vec<&str> = first.split("\r\n").collect();
2588 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2589 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2590 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2591 assert!((0..1_000_000).contains(µs), "got {micros}");
2592 assert_ne!(first, f.run(&[b"TIME"]));
2596 }
2597
2598 #[test]
2599 fn a_keyspace_scan_walks_every_key_once() {
2600 let mut f = Fixture::new();
2601 for i in 0..500 {
2602 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2603 }
2604
2605 let mut seen: Vec<String> = Vec::new();
2606 let mut cursor = "0".to_owned();
2607 let mut calls = 0;
2608 loop {
2609 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2610 seen.extend(keys);
2611 cursor = next;
2612 calls += 1;
2613 assert!(calls < 10_000, "the cursor is not advancing");
2614 if cursor == "0" {
2615 break;
2616 }
2617 }
2618
2619 seen.sort();
2620 seen.dedup();
2621 assert_eq!(seen.len(), 500, "every key once and only once");
2622 assert!(calls > 1, "500 keys came back in one batch");
2625 }
2626
2627 #[test]
2628 fn a_scan_narrows_by_pattern_and_by_type() {
2629 let mut f = Fixture::new();
2630 f.run(&[b"SET", b"str", b"v"]);
2631 f.run(&[b"SADD", b"members", b"a"]);
2632 f.run(&[b"HSET", b"fields", b"f", b"v"]);
2633
2634 let all = |f: &mut Fixture, args: &[&[u8]]| {
2635 let mut out: Vec<String> = Vec::new();
2636 let mut cursor = "0".to_owned();
2637 loop {
2638 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2639 line.extend_from_slice(args);
2640 let (next, keys) = scan_reply(&f.run(&line));
2641 out.extend(keys);
2642 cursor = next;
2643 if cursor == "0" {
2644 break;
2645 }
2646 }
2647 out.sort();
2648 out
2649 };
2650
2651 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2652 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2653 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2654 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2656 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2658 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2659 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2661 }
2662
2663 #[test]
2664 fn a_scan_says_what_is_wrong_with_it() {
2665 let mut f = Fixture::new();
2666 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2667 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2668 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2669 assert_eq!(
2670 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2671 "-ERR syntax error\r\n"
2672 );
2673 assert_eq!(
2674 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2675 "-ERR value is not an integer or out of range\r\n"
2676 );
2677 assert_eq!(
2678 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2679 "-ERR syntax error\r\n"
2680 );
2681 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2686 }
2687
2688 #[test]
2689 fn keys_and_randomkey_look_at_the_whole_database() {
2690 let mut f = Fixture::new();
2691 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2692 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2693
2694 for name in ["one", "two", "three"] {
2695 f.run(&[b"SET", name.as_bytes(), b"v"]);
2696 }
2697 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2698 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2699 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2700
2701 for _ in 0..50 {
2702 let got = f.run(&[b"RANDOMKEY"]);
2703 assert!(
2704 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2705 "got {got}"
2706 );
2707 }
2708 }
2709
2710 #[test]
2711 fn a_walk_does_not_answer_keys_that_have_expired() {
2712 let mut f = Fixture::new();
2713 f.run(&[b"SET", b"alive", b"v"]);
2714 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2715 f.server.advance_clock_ms(2);
2716 assert_eq!(
2717 f.run(&[b"DBSIZE"]),
2718 ":2\r\n",
2719 "nothing has collected it yet"
2720 );
2721
2722 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2723 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2724 assert_eq!(keys, ["alive"]);
2725 for _ in 0..20 {
2726 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2727 }
2728 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2731 }
2732
2733 #[test]
2734 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2735 let mut f = Fixture::new();
2736 f.run(&[b"SET", b"k", b"v"]);
2737 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2738 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2739
2740 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2741 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2742 let ms = int(&f.run(&[b"PTTL", b"k"]));
2743 assert!((99_000..=100_000).contains(&ms), "got {ms}");
2744
2745 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2747 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2748 assert_eq!(at, (at_ms + 500) / 1000);
2749 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2750
2751 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2752 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2753 assert_eq!(
2754 f.run(&[b"PERSIST", b"k"]),
2755 ":0\r\n",
2756 "nothing to take off the second time"
2757 );
2758 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2759 assert_eq!(
2760 f.run(&[b"GET", b"k"]),
2761 "$1\r\nv\r\n",
2762 "and the value went through all of that untouched"
2763 );
2764 }
2765
2766 #[test]
2767 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2768 let mut f = Fixture::new();
2769 f.run(&[b"SET", b"str", b"v"]);
2770 f.run(&[b"SADD", b"set", b"a", b"b"]);
2771 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2772
2773 for key in [b"str".as_slice(), b"set", b"hash"] {
2774 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2775 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2776 }
2777 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2780 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2781 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2782 }
2783
2784 #[test]
2785 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2786 let mut f = Fixture::new();
2787 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2788 f.run(&[b"SET", key, b"v"]);
2789 }
2790 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2794 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2795 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2796 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2797 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2798 assert_eq!(
2799 f.run(&[b"EXPIRE", b"a", b"100"]),
2800 ":0\r\n",
2801 "and the key really went, so there is nothing to put a deadline on"
2802 );
2803 }
2804
2805 #[test]
2806 fn the_four_conditions_decide_whether_the_deadline_moves() {
2807 let mut f = Fixture::new();
2808 f.run(&[b"SET", b"k", b"v"]);
2809
2810 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2811 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2812 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2813 assert_eq!(
2814 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2815 ":1\r\n",
2816 "no deadline reads as infinitely far away, so LT passes where GT fails"
2817 );
2818
2819 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2820 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2821 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2822 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2823 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2824 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2825
2826 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2829 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2830 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2831 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2832 }
2833
2834 #[test]
2835 fn the_conditions_are_a_set_and_not_a_keyword() {
2836 let mut f = Fixture::new();
2837 f.run(&[b"SET", b"k", b"v"]);
2838
2839 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2840 assert_eq!(
2841 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2842 ":0\r\n",
2843 "the same keyword twice means it once, and NX now has a deadline to fail on"
2844 );
2845
2846 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2849 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2850 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2851 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2852 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2853 f.run(&[b"PERSIST", b"k"]);
2854 assert_eq!(
2855 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2856 ":0\r\n",
2857 "where LT on its own would have taken it"
2858 );
2859 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2860 }
2861
2862 #[test]
2863 fn a_key_is_gone_once_its_moment_passes() {
2864 let mut f = Fixture::new();
2865 f.run(&[b"SET", b"k", b"v"]);
2866 f.run(&[b"EXPIRE", b"k", b"100"]);
2867
2868 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2869 f.server.set_clock_ms(at as u64 + 1);
2870 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2871 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2872 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2873 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2874 }
2875
2876 #[test]
2877 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2878 let mut f = Fixture::new();
2879 f.run(&[b"SET", b"k", b"v"]);
2880 for (bad, want) in [
2881 (
2882 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2883 "-ERR value is not an integer or out of range\r\n",
2884 ),
2885 (
2886 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2887 "-ERR Unsupported option MAYBE\r\n",
2888 ),
2889 (
2890 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2891 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2892 ),
2893 (
2894 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2895 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2896 ),
2897 (
2898 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2899 "-ERR GT and LT options at the same time are not compatible\r\n",
2900 ),
2901 (
2904 &[b"EXPIRE", b"k", b"9223372036854775807"],
2905 "-ERR invalid expire time in 'expire' command\r\n",
2906 ),
2907 (
2908 &[b"EXPIREAT", b"k", b"9223372036854775807"],
2909 "-ERR invalid expire time in 'expireat' command\r\n",
2910 ),
2911 (
2912 &[b"PEXPIRE", b"k", b"9223372036854775807"],
2913 "-ERR invalid expire time in 'pexpire' command\r\n",
2914 ),
2915 ] {
2916 assert_eq!(f.run(bad), want, "for {bad:?}");
2917 }
2918 assert_eq!(
2919 f.run(&[b"TTL", b"k"]),
2920 ":-1\r\n",
2921 "and none of those put a deadline on anything"
2922 );
2923
2924 assert_eq!(
2928 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2929 ":1\r\n"
2930 );
2931 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2932 }
2933
2934 #[test]
2935 fn flushing_empties_this_database_or_every_one_of_them() {
2936 let mut f = Fixture::new();
2937 f.run(&[b"SELECT", b"0"]);
2938 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2939 f.run(&[b"SELECT", b"1"]);
2940 f.run(&[b"SET", b"c", b"3"]);
2941 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2942 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2945 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2946 f.run(&[b"SELECT", b"0"]);
2948 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2949 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2950 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2951 f.run(&[b"SELECT", b"1"]);
2952 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2953 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2956 assert_eq!(
2957 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2958 "-ERR syntax error\r\n"
2959 );
2960 }
2961
2962 #[test]
2963 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2964 let mut f = Fixture::new();
2965 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2966 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2967 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2968 assert_eq!(
2971 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2972 "*2\r\n:0\r\n:0\r\n"
2973 );
2974 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2975 assert_eq!(
2976 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2977 "*0\r\n"
2978 );
2979 assert_eq!(
2980 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2981 "-ERR Library not found\r\n"
2982 );
2983
2984 assert_eq!(
2987 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2988 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2989 );
2990 assert_eq!(
2991 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2992 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2993 );
2994 assert_eq!(
2997 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2998 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2999 );
3000 assert_eq!(
3001 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
3002 "-ERR Unknown argument bogus\r\n"
3003 );
3004 assert_eq!(
3005 f.run(&[b"SCRIPT", b"EXISTS"]),
3006 "-ERR wrong number of arguments for 'script|exists' command\r\n"
3007 );
3008
3009 assert_eq!(
3012 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
3013 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
3014 );
3015 assert_eq!(
3016 f.run(&[b"FUNCTION", b"STATS"]),
3017 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
3018 );
3019 }
3020
3021 #[test]
3022 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
3023 let mut f = Fixture::new();
3024 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
3025 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
3026 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
3027 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
3030 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
3031 f.run(&[b"SET", b"k", b"hello"]);
3034 assert_eq!(
3035 f.run(&[b"INCR", b"k"]),
3036 "-ERR value is not an integer or out of range\r\n"
3037 );
3038 assert_eq!(
3039 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
3040 "-ERR increment would produce NaN or Infinity\r\n"
3041 );
3042 }
3043
3044 #[test]
3049 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
3050 let mut f = Fixture::new();
3051 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
3052 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
3055 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
3056 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
3057 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
3058 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
3059 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
3060 assert_eq!(
3061 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
3062 "*2\r\n:1\r\n:0\r\n",
3063 "a refused increment reports the value it left alone and applied nothing"
3064 );
3065 assert_eq!(
3066 f.run(&[
3067 b"INCREX",
3068 b"n",
3069 b"BYINT",
3070 b"5",
3071 b"UBOUND",
3072 b"3",
3073 b"SATURATE"
3074 ]),
3075 "*2\r\n:3\r\n:2\r\n"
3076 );
3077 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
3078 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
3079 }
3080
3081 #[test]
3082 fn the_same_answers_come_out_in_resp3_spelling() {
3083 let mut f = Fixture::new();
3084 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
3085 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
3086 assert_eq!(
3089 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
3090 "*2\r\n,1.5\r\n,1.5\r\n"
3091 );
3092 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
3093 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
3096 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
3097 }
3098
3099 #[test]
3100 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
3101 let mut f = Fixture::new();
3102 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
3103 assert_eq!(flow, Flow::Continue);
3104 assert_eq!(
3105 reply,
3106 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
3107 );
3108 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
3111 assert_eq!(reply.matches("\r\n").count(), 1);
3112 }
3113
3114 #[test]
3115 fn arity_is_checked_before_the_command_is() {
3116 let mut f = Fixture::new();
3117 assert_eq!(
3118 f.run(&[b"GET"]),
3119 "-ERR wrong number of arguments for 'get' command\r\n"
3120 );
3121 assert_eq!(
3122 f.run(&[b"MSET", b"k"]),
3123 "-ERR wrong number of arguments for 'mset' command\r\n"
3124 );
3125 assert_eq!(
3129 f.run(&[b"PING", b"a", b"b"]),
3130 "-ERR wrong number of arguments for 'ping' command\r\n"
3131 );
3132 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
3133 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
3134 assert_eq!(
3136 f.run(&[b"DELEX", b"k", b"IFEQ"]),
3137 "-ERR wrong number of arguments for 'delex' command\r\n"
3138 );
3139 }
3140
3141 #[test]
3145 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
3146 let mut f = Fixture::new();
3147 let syntax = "-ERR syntax error\r\n";
3148 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
3149 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
3150 assert_eq!(
3151 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
3152 syntax
3153 );
3154 assert_eq!(
3155 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
3156 syntax
3157 );
3158 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
3159 assert_eq!(
3161 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
3162 "+OK\r\n"
3163 );
3164 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
3165 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
3166 assert_eq!(
3168 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
3169 syntax
3170 );
3171 assert_eq!(
3172 f.run(&[b"INCREX", b"n", b"ENX"]),
3173 "-ERR ENX flag requires an expiration\r\n"
3174 );
3175 assert_eq!(
3176 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
3177 "-ERR UBOUND is not an integer or out of range\r\n"
3178 );
3179 assert_eq!(
3180 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
3181 "-ERR LBOUND can't be greater than UBOUND\r\n"
3182 );
3183 assert_eq!(
3184 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
3185 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
3186 );
3187 }
3188
3189 #[test]
3193 fn the_expiry_rules_are_redis_own() {
3194 let mut f = Fixture::new();
3195 let bad = "-ERR invalid expire time in 'set' command\r\n";
3196 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
3197 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
3198 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
3199 assert_eq!(
3200 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
3201 bad
3202 );
3203 assert_eq!(
3204 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
3205 "-ERR value is not an integer or out of range\r\n"
3206 );
3207 assert_eq!(
3208 f.run(&[b"SETEX", b"k", b"0", b"v"]),
3209 "-ERR invalid expire time in 'setex' command\r\n"
3210 );
3211 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
3212 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
3213 assert_eq!(
3214 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
3215 "-ERR syntax error\r\n",
3216 "the option list is still checked before the key is looked up"
3217 );
3218 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3220 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
3221 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3222 }
3223
3224 #[test]
3225 fn mset_takes_its_pairs_from_the_read_buffer() {
3226 let mut f = Fixture::new();
3227 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
3228 assert_eq!(
3229 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
3230 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
3231 );
3232 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
3233 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
3234 assert_eq!(
3235 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
3236 "-ERR wrong number of key-value pairs\r\n"
3237 );
3238 assert_eq!(
3239 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
3240 "-ERR invalid numkeys value\r\n"
3241 );
3242 assert_eq!(
3243 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
3244 "-ERR invalid numkeys value\r\n"
3245 );
3246 }
3247
3248 #[test]
3249 fn lcs_answers_the_length_the_string_and_the_runs() {
3250 let mut f = Fixture::new();
3251 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
3252 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
3253 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
3254 assert_eq!(
3255 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
3256 "*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"
3257 );
3258 assert_eq!(
3261 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3262 "$6\r\nmytext\r\n"
3263 );
3264 }
3265
3266 #[test]
3267 fn select_moves_the_connection_and_the_databases_stay_apart() {
3268 let mut f = Fixture::new();
3269 f.run(&[b"SET", b"k", b"zero"]);
3270 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3271 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3272 f.run(&[b"SET", b"k", b"four"]);
3273 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3274 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3275 assert_eq!(
3276 f.run(&[b"SELECT", b"99"]),
3277 "-ERR DB index is out of range\r\n"
3278 );
3279 assert_eq!(
3280 f.run(&[b"SELECT", b"-1"]),
3281 "-ERR DB index is out of range\r\n"
3282 );
3283 assert_eq!(
3284 f.run(&[b"SELECT", b"abc"]),
3285 "-ERR value is not an integer or out of range\r\n"
3286 );
3287 f.run(&[b"SELECT", b"4"]);
3289 f.run(&[b"RESET"]);
3290 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3291 }
3292
3293 #[test]
3294 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3295 let mut f = Fixture::new();
3296 let reply = f.run(&[b"HELLO"]);
3297 assert!(reply.starts_with("*14\r\n"), "{reply}");
3298 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3299 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3300 assert!(
3301 reply.contains(":7\r\n"),
3302 "the connection id is in there: {reply}"
3303 );
3304 assert_eq!(
3305 f.run(&[b"HELLO", b"4"]),
3306 "-NOPROTO unsupported protocol version\r\n"
3307 );
3308 assert_eq!(
3309 f.run(&[b"HELLO", b"abc"]),
3310 "-ERR Protocol version is not an integer or out of range\r\n"
3311 );
3312 assert_eq!(
3313 f.run(&[b"HELLO", b"3", b"SETNAME"]),
3314 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3315 );
3316 assert!(
3317 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3318 .starts_with("%7\r\n")
3319 );
3320 assert_eq!(f.session.name(), b"bob");
3321 f.run(&[b"RESET"]);
3322 assert_eq!(f.session.name(), b"");
3323 }
3324
3325 #[test]
3326 fn command_describes_this_server_in_the_shape_a_driver_reads() {
3327 let mut f = Fixture::new();
3328 let count = format!(":{}\r\n", COMMANDS.len());
3329 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3330 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3331 assert_eq!(
3332 info,
3333 "*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\
3334 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3335 );
3336 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3338 assert_eq!(
3339 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3340 "*1\r\n$8\r\ngetrange\r\n"
3341 );
3342 assert_eq!(
3343 f.run(&[b"COMMAND", b"NOPE"]),
3344 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3345 );
3346 }
3347
3348 #[test]
3352 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3353 let mut f = Fixture::new();
3354 assert_eq!(
3355 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3356 "*1\r\n$1\r\nk\r\n"
3357 );
3358 assert_eq!(
3359 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3360 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3361 );
3362 assert_eq!(
3363 f.run(&[
3364 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3365 ]),
3366 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3367 );
3368 assert_eq!(
3369 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3370 "-ERR The command has no key arguments\r\n"
3371 );
3372 assert_eq!(
3373 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3374 "-ERR Invalid number of arguments specified for command\r\n"
3375 );
3376 }
3377
3378 #[test]
3379 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3380 let mut f = Fixture::new();
3381 assert_eq!(
3382 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3383 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3384 );
3385 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3388 assert!(both.starts_with("*6\r\n"), "{both}");
3389 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3390 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3391 assert_eq!(
3392 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3393 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3394 );
3395 assert_eq!(
3396 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3397 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3398 );
3399 assert_eq!(
3400 f.run(&[b"CONFIG", b"GET"]),
3401 "-ERR wrong number of arguments for 'config|get' command\r\n"
3402 );
3403 assert_eq!(
3407 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3408 "-ERR wrong number of arguments for 'config|set' command\r\n"
3409 );
3410 assert_eq!(
3411 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3412 "-ERR syntax error\r\n"
3413 );
3414 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3415 assert_eq!(
3416 f.run(&[b"CONFIG", b"REWRITE"]),
3417 "-ERR The server is running without a config file\r\n"
3418 );
3419 }
3420
3421 #[test]
3422 fn the_eviction_policy_reads_back_what_was_written_to_it() {
3423 let mut f = Fixture::new();
3424 assert_eq!(
3425 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3426 "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3427 );
3428 assert_eq!(
3429 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3430 "+OK\r\n",
3431 "the name is matched without regard to case, like every other one"
3432 );
3433 assert_eq!(
3434 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3435 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3436 );
3437 assert!(
3439 f.run(&[b"INFO", b"memory"])
3440 .contains("maxmemory_policy:allkeys-lfu"),
3441 "INFO and CONFIG disagree about the policy"
3442 );
3443 assert_eq!(
3447 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3448 "-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"
3449 );
3450 assert_eq!(
3453 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3454 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3455 );
3456 f.run(&[
3457 b"CONFIG",
3458 b"SET",
3459 b"hash-max-listpack-entries",
3460 b"7",
3461 b"maxmemory-policy",
3462 b"nonsense",
3463 ]);
3464 assert_eq!(
3465 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3466 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3467 );
3468 }
3469
3470 #[test]
3471 fn the_three_eviction_numbers_read_back_too() {
3472 let mut f = Fixture::new();
3473 for (name, default, set) in [
3474 ("maxmemory-samples", "5", "12"),
3475 ("lfu-log-factor", "10", "3"),
3476 ("lfu-decay-time", "1", "60"),
3477 ] {
3478 let get = || {
3479 format!(
3480 "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3481 name.len(),
3482 default.len()
3483 )
3484 };
3485 assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3486 assert_eq!(
3487 f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3488 "+OK\r\n"
3489 );
3490 assert_eq!(
3491 f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3492 format!(
3493 "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3494 name.len(),
3495 set.len()
3496 )
3497 );
3498 assert_eq!(
3501 f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3502 format!(
3503 "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3504 )
3505 );
3506 }
3507 }
3508
3509 #[test]
3510 fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3511 let mut f = Fixture::new();
3512 assert_eq!(
3513 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3514 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3515 "no limit is the default"
3516 );
3517 for (typed, bytes) in [
3520 (&b"1024"[..], "1024"),
3521 (b"1k", "1000"),
3522 (b"1kb", "1024"),
3523 (b"1M", "1000000"),
3524 (b"1Mb", "1048576"),
3525 (b"1gb", "1073741824"),
3526 (b"100mb", "104857600"),
3527 ] {
3528 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3529 assert_eq!(
3530 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3531 format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3532 "set {}",
3533 String::from_utf8_lossy(typed)
3534 );
3535 }
3536 assert!(
3537 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3538 "the report agrees with the setting"
3539 );
3540
3541 for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3544 assert_eq!(
3545 f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3546 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3547 "refused {}",
3548 String::from_utf8_lossy(bad)
3549 );
3550 }
3551 assert!(
3552 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3553 "and the refusal left the old one alone"
3554 );
3555 }
3556
3557 #[test]
3558 fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3559 let mut f = Fixture::new();
3560 f.run(&[b"SET", b"here", b"already"]);
3561 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3565 assert_eq!(
3566 f.run(&[b"SET", b"k", b"v"]),
3567 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3568 );
3569 assert_eq!(
3570 f.run(&[b"LPUSH", b"l", b"v"]),
3571 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3572 );
3573 assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3575 assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3576 assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3577
3578 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3580 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3581 }
3582
3583 #[test]
3584 fn an_allkeys_policy_makes_room_instead_of_refusing() {
3585 let mut f = Fixture::new();
3586 let val = vec![b'v'; 256];
3587 for i in 0..24000u32 {
3588 let k = format!("key:{i:08}");
3589 f.run(&[b"SET", k.as_bytes(), &val]);
3590 }
3591 let full = f.server.memory_bytes();
3592 assert!(
3593 full > 3 * 1024 * 1024,
3594 "the arena is several segments: {full}"
3595 );
3596
3597 let limit = full - 2 * 1024 * 1024;
3601 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3602 f.run(&[
3603 b"CONFIG",
3604 b"SET",
3605 b"maxmemory",
3606 limit.to_string().as_bytes(),
3607 ]);
3608
3609 for i in 0..2000u32 {
3613 let k = format!("new:{i:08}");
3614 assert_eq!(
3615 f.run(&[b"SET", k.as_bytes(), &val]),
3616 "+OK\r\n",
3617 "write {i} was refused"
3618 );
3619 f.server.refresh_memory();
3620 if f.server.memory_bytes() <= limit {
3621 break;
3622 }
3623 }
3624 assert!(
3625 f.server.memory_bytes() <= limit,
3626 "it never got under: {} against {limit}",
3627 f.server.memory_bytes()
3628 );
3629 let info = f.run(&[b"INFO", b"stats"]);
3630 assert!(!info.contains("evicted_keys:0"), "{info}");
3631 assert!(
3632 f.run(&[b"DBSIZE"]) != ":0\r\n",
3633 "and it did not empty the database to get there"
3634 );
3635 }
3636
3637 #[test]
3638 fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3639 let mut f = Fixture::new();
3646 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3647 let big = vec![b'v'; 200];
3648
3649 for i in 0..400u32 {
3650 let n = i.to_string();
3651 let n = n.as_bytes();
3652 f.run(&[b"SADD", b"s", n]);
3653 f.run(&[b"SADD", b"s2", &big]);
3654 f.run(&[b"HSET", b"h", n, &big]);
3655 f.run(&[b"RPUSH", b"l", &big]);
3656 f.run(&[b"ZADD", b"z", n, n]);
3657 f.run(&[b"ARSET", b"a", n, &big]);
3658 if i % 7 == 0 {
3659 f.run(&[b"SREM", b"s", n]);
3660 f.run(&[b"HDEL", b"h", n]);
3661 f.run(&[b"LPOP", b"l"]);
3662 f.run(&[b"ZREM", b"z", n]);
3663 f.run(&[b"ARDEL", b"a", n]);
3664 }
3665 if i % 53 == 0 {
3666 f.run(&[b"DEL", b"s2"]);
3669 }
3670 assert_eq!(
3671 f.server.settled_memory(),
3672 f.server.memory_bytes(),
3673 "after round {i}"
3674 );
3675 }
3676
3677 assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3680 assert!(
3681 f.server.memory_bytes() > 512 * 1024,
3682 "{}",
3683 f.server.memory_bytes()
3684 );
3685
3686 f.run(&[b"FLUSHALL"]);
3688 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3689 }
3690
3691 #[test]
3692 fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3693 let mut f = Fixture::new();
3698 for i in 0..200u32 {
3699 let n = i.to_string();
3700 f.run(&[b"SADD", b"s", n.as_bytes()]);
3701 f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3702 }
3703 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3704 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3705
3706 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3707 for i in 200..400u32 {
3708 let n = i.to_string();
3709 f.run(&[b"SADD", b"s", n.as_bytes()]);
3710 }
3711 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3712 assert_eq!(
3713 f.server.settled_memory(),
3714 f.server.memory_bytes(),
3715 "the writes it was not watching are in the number it started from"
3716 );
3717 }
3718
3719 #[test]
3720 fn evicted_keys_and_expired_keys_are_different_numbers() {
3721 let mut f = Fixture::new();
3722 f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3725 f.server.advance_clock_ms(20);
3726 f.run(&[b"GET", b"gone"]);
3727 let info = f.run(&[b"INFO", b"stats"]);
3728 assert!(info.contains("expired_keys:1"), "{info}");
3729 assert!(info.contains("evicted_keys:0"), "{info}");
3730 }
3731
3732 #[test]
3733 fn the_object_subcommands_follow_the_policy() {
3734 let mut f = Fixture::new();
3735 f.run(&[b"SET", b"s", b"v"]);
3736 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3740 assert!(
3741 f.run(&[b"OBJECT", b"FREQ", b"s"])
3742 .starts_with("-ERR An LFU maxmemory policy is not selected"),
3743 );
3744
3745 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3746 assert!(
3747 f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3748 .starts_with("-ERR An LFU maxmemory policy is selected"),
3749 );
3750 assert!(
3755 f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3756 "FREQ should answer under an LFU policy"
3757 );
3758 }
3759
3760 #[test]
3761 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3762 let mut f = Fixture::new();
3763 f.run(&[b"SET", b"s", b"hello"]);
3764 f.run(&[b"SET", b"n", b"123"]);
3765 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3766 f.run(&[b"SADD", b"ss", b"a", b"b"]);
3767 f.run(&[b"HSET", b"h", b"f", b"v"]);
3768 for (key, want) in [
3769 (b"s".as_slice(), "embstr"),
3770 (b"n", "int"),
3771 (b"si", "intset"),
3772 (b"ss", "listpack"),
3773 (b"h", "listpack"),
3774 ] {
3775 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3776 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3777 }
3778
3779 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3782 assert_eq!(
3783 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3784 "$10\r\nlistpackex\r\n"
3785 );
3786
3787 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3788 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3789 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3790 }
3791
3792 #[test]
3793 fn object_answers_nil_for_a_key_that_is_not_there() {
3794 let mut f = Fixture::new();
3795 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3796 assert_eq!(
3797 f.run(&[b"OBJECT", sub, b"nokey"]),
3798 "$-1\r\n",
3799 "a nil and not an error, which is what 8.10.1 does"
3800 );
3801 }
3802 f.run(&[b"SET", b"s", b"v"]);
3805 assert!(
3806 f.run(&[b"OBJECT", b"FREQ", b"s"])
3807 .starts_with("-ERR An LFU maxmemory policy is not"),
3808 );
3809 assert_eq!(
3810 f.run(&[b"OBJECT", b"NOPE", b"s"]),
3811 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3812 );
3813 assert_eq!(
3814 f.run(&[b"OBJECT", b"ENCODING"]),
3815 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3816 );
3817 assert_eq!(
3818 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3819 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3820 );
3821 assert_eq!(
3822 f.run(&[b"OBJECT"]),
3823 "-ERR wrong number of arguments for 'object' command\r\n"
3824 );
3825 }
3826
3827 #[test]
3828 fn config_moves_the_ladder_and_object_encoding_agrees() {
3829 let mut f = Fixture::new();
3830 assert_eq!(
3831 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3832 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3833 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3834 );
3835 assert_eq!(
3838 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3839 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3840 );
3841 assert!(
3842 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3843 .starts_with("*8\r\n")
3844 );
3845 assert!(
3846 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3847 .starts_with("*6\r\n")
3848 );
3849
3850 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3851 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3852
3853 assert_eq!(
3854 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3855 "+OK\r\n",
3856 "written under the old name and read back under the new one"
3857 );
3858 assert_eq!(
3859 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3860 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3861 );
3862 assert_eq!(
3863 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3864 "$8\r\nlistpack\r\n",
3865 "the hash that already exists is left exactly where it was"
3866 );
3867 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3868 assert_eq!(
3869 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3870 "$9\r\nhashtable\r\n",
3871 "and the next one built goes straight to a table"
3872 );
3873
3874 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3876 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3877 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3878 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3879 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3880 assert_eq!(
3881 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3882 "$9\r\nhashtable\r\n"
3883 );
3884 }
3885
3886 #[test]
3887 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3888 let mut f = Fixture::new();
3889 assert_eq!(
3890 f.run(&[
3891 b"CONFIG",
3892 b"SET",
3893 b"hash-max-listpack-entries",
3894 b"7",
3895 b"set-max-listpack-entries",
3896 b"abc"
3897 ]),
3898 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3899 );
3900 assert_eq!(
3901 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3902 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3903 "the pair in front of the bad one did not go in"
3904 );
3905 assert_eq!(
3908 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3909 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3910 );
3911 assert_eq!(
3912 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3913 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3914 );
3915 assert_eq!(
3918 f.run(&[
3919 b"CONFIG",
3920 b"SET",
3921 b"set-max-intset-entries",
3922 b"99999999999999999999"
3923 ]),
3924 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3925 );
3926 assert_eq!(
3927 f.run(&[
3928 b"CONFIG",
3929 b"SET",
3930 b"set-max-intset-entries",
3931 b"9223372036854775807"
3932 ]),
3933 "+OK\r\n"
3934 );
3935 }
3936
3937 #[test]
3938 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3939 let mut f = Fixture::new();
3940 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3941 f.run(&[b"SELECT", b"3"]);
3942 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3943 assert_eq!(
3944 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3945 "$9\r\nhashtable\r\n",
3946 "these are one server wide number in Redis, whatever a Keyspace carries"
3947 );
3948 }
3949
3950 #[test]
3951 fn info_reports_the_numbers_it_can_stand_behind() {
3952 let mut f = Fixture::new();
3953 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3954 let all = f.run(&[b"INFO"]);
3955 assert!(all.contains("redis_version:8.8.0"), "{all}");
3956 assert!(
3957 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3958 "{all}"
3959 );
3960 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3961 assert!(all.contains("role:master"), "{all}");
3962 let clients = f.run(&[b"INFO", b"clients"]);
3964 assert!(clients.contains("connected_clients:0"), "{clients}");
3965 assert!(!clients.contains("redis_version"), "{clients}");
3966 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3967 }
3968
3969 #[test]
3976 fn commandstats_is_asked_for_and_replication_is_not() {
3977 let mut f = Fixture::new();
3978 for arg in ["", "all", "default", "everything"] {
3979 let info = if arg.is_empty() {
3980 f.run(&[b"INFO"])
3981 } else {
3982 f.run(&[b"INFO", arg.as_bytes()])
3983 };
3984 assert!(info.contains("redis_version"), "{arg}: {info}");
3985 assert!(info.contains("used_cpu_user"), "{arg}: {info}");
3986 assert!(info.contains("used_memory"), "{arg}: {info}");
3987 assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
3988 let asked = arg == "all" || arg == "everything";
3989 assert_eq!(
3990 info.contains("rejected_calls"),
3991 asked,
3992 "{arg} should{} carry the command counters: {info}",
3993 if asked { "" } else { " not" }
3994 );
3995 }
3996
3997 let cpu = f.run(&[b"INFO", b"cpu"]);
3998 assert!(cpu.contains("used_cpu_user"), "{cpu}");
3999 assert!(!cpu.contains("used_memory"), "{cpu}");
4000
4001 let stats = f.run(&[b"INFO", b"commandSTATS"]);
4004 assert!(!stats.contains("used_memory"), "{stats}");
4005 assert!(stats.contains("rejected_calls"), "{stats}");
4006
4007 let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
4009 assert!(pair.contains("used_cpu_user"), "{pair}");
4010 assert!(!pair.contains("master_repl_offset"), "{pair}");
4011
4012 let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
4013 assert!(with_all.contains("used_memory"), "{with_all}");
4014 assert!(with_all.contains("master_repl_offset"), "{with_all}");
4015 assert!(with_all.contains("rejected_calls"), "{with_all}");
4016 assert_eq!(
4018 with_all.matches("used_cpu_user_children").count(),
4019 1,
4020 "{with_all}"
4021 );
4022
4023 let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
4024 assert!(with_default.contains("used_memory"), "{with_default}");
4025 assert!(
4026 with_default.contains("master_repl_offset"),
4027 "{with_default}"
4028 );
4029 assert!(!with_default.contains("rejected_calls"), "{with_default}");
4030 assert_eq!(
4031 with_default.matches("used_cpu_user_children").count(),
4032 1,
4033 "{with_default}"
4034 );
4035 }
4036
4037 #[test]
4046 fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
4047 let mut f = Fixture::new();
4048 let info = f.run(&[b"INFO", b"memory"]);
4049 for field in [
4050 "total_system_memory:",
4051 "mem_cgroup_limit:",
4052 "mem_limit:",
4053 "mem_budget:",
4054 ] {
4055 assert!(info.contains(field), "no {field} in {info}");
4056 }
4057
4058 let field = |name: &str| -> u64 {
4059 info.lines()
4060 .find_map(|l| l.strip_prefix(name))
4061 .unwrap_or_else(|| panic!("no {name} in {info}"))
4062 .trim()
4063 .parse()
4064 .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
4065 };
4066 let limit = field("mem_limit:");
4067 assert_eq!(field("mem_budget:"), limit / 4, "{info}");
4068 if limit != 0 {
4071 let host = field("total_system_memory:");
4072 let cgroup = field("mem_cgroup_limit:");
4073 assert!(
4074 limit == host || limit == cgroup,
4075 "the limit came from neither number: {info}"
4076 );
4077 }
4078 }
4079
4080 #[test]
4089 fn a_command_counts_what_it_did_separately_from_what_it_refused() {
4090 let mut f = Fixture::new();
4091 f.run(&[b"SET", b"k", b"v"]);
4092 f.run(&[b"SET", b"k", b"w"]);
4093 f.run(&[b"LPUSH", b"k", b"x"]);
4095 f.run(&[b"LPUSH", b"k"]);
4097
4098 let stats = f.run(&[b"INFO", b"commandstats"]);
4099 assert!(
4100 stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
4101 "{stats}"
4102 );
4103 assert!(
4104 stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
4105 "{stats}"
4106 );
4107 assert!(
4108 !stats.contains("cmdstat_zadd"),
4109 "a command nobody has sent has no row: {stats}"
4110 );
4111 }
4112
4113 #[test]
4117 fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
4118 let mut f = Fixture::new();
4119 for i in 0..3_000u32 {
4120 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4121 }
4122 for i in 0..1_000u32 {
4123 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4124 }
4125 assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
4126 f.advance(100);
4127 assert_eq!(
4128 f.run(&[b"DBSIZE"]),
4129 ":4000\r\n",
4130 "DBSIZE counts records and nothing has read past the dead ones yet"
4131 );
4132
4133 let mut spent = 0;
4135 for _ in 0..2_000 {
4136 spent += f.server.expire_step(4096);
4137 if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
4138 break;
4139 }
4140 }
4141 assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
4142 assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
4143 for i in 0..1_000u32 {
4144 assert_eq!(
4145 f.run(&[b"GET", format!("k{i}").as_bytes()]),
4146 "$1\r\nv\r\n",
4147 "it took a key that had no deadline"
4148 );
4149 }
4150 }
4151
4152 #[test]
4153 fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
4154 let mut f = Fixture::new();
4155 for i in 0..2_000u32 {
4156 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4157 }
4158 assert_eq!(f.server.expire_step(4096), 0);
4159 f.run(&[b"SELECT", b"3"]);
4161 f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
4162 f.advance(100);
4163 for _ in 0..64 {
4164 f.server.expire_step(4096);
4165 }
4166 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4167 f.run(&[b"SELECT", b"0"]);
4168 assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
4169 assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
4170 }
4171
4172 #[test]
4175 fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
4176 let mut f = Fixture::new();
4177 for i in 0..500u32 {
4178 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4179 }
4180 f.advance(100);
4181 let at = f.server.striped(0).now_ms();
4182 f.server.set_clock_ms(at);
4183 assert!(f.server.expire_slice(8) > 0, "the first one works");
4187 for _ in 0..1_000 {
4188 assert_eq!(
4189 f.server.expire_slice(8),
4190 0,
4191 "the millisecond has not moved and neither should this"
4192 );
4193 }
4194 assert!(
4195 f.server.striped(0).expires() > 400,
4196 "there is plenty left to take"
4197 );
4198 f.server.set_clock_ms(at + 1);
4199 assert!(f.server.expire_slice(8) > 0, "and then it goes again");
4200 }
4201
4202 #[test]
4205 fn info_keyspace_counts_the_keys_that_have_a_deadline() {
4206 let mut f = Fixture::new();
4207 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
4208 assert!(
4209 f.run(&[b"INFO", b"keyspace"])
4210 .contains("db0:keys=3,expires=0"),
4211 "none of them has one yet"
4212 );
4213 f.run(&[b"EXPIRE", b"a", b"1000"]);
4214 f.run(&[b"EXPIRE", b"b", b"1000"]);
4215 let two = f.run(&[b"INFO", b"keyspace"]);
4216 assert!(two.contains("db0:keys=3,expires=2"), "{two}");
4217 f.run(&[b"PERSIST", b"a"]);
4218 f.run(&[b"DEL", b"b"]);
4219 let none = f.run(&[b"INFO", b"keyspace"]);
4220 assert!(none.contains("db0:keys=2,expires=0"), "{none}");
4221
4222 f.run(&[b"SELECT", b"1"]);
4224 f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
4225 let both = f.run(&[b"INFO", b"keyspace"]);
4226 assert!(both.contains("db0:keys=2,expires=0"), "{both}");
4227 assert!(both.contains("db1:keys=1,expires=1"), "{both}");
4228 }
4229
4230 #[cfg(unix)]
4231 #[test]
4232 fn info_cpu_reports_processor_time_that_was_really_measured() {
4233 let mut f = Fixture::new();
4234 let cpu = f.run(&[b"INFO", b"cpu"]);
4235 assert!(cpu.contains("# CPU"), "{cpu}");
4236 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
4238 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
4239 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
4240 assert!(!cpu.contains("redis_version"), "{cpu}");
4241
4242 let before = used_cpu_user(&cpu);
4246 let mut n = 0u64;
4247 let mut rounds = 0;
4248 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
4249 for i in 0..1_000_000u64 {
4250 n = n.wrapping_add(i.wrapping_mul(i));
4251 }
4252 rounds += 1;
4253 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
4257 }
4258 }
4259
4260 #[cfg(unix)]
4262 fn used_cpu_user(info: &str) -> f64 {
4263 info.lines()
4264 .find_map(|l| l.strip_prefix("used_cpu_user:"))
4265 .expect("no used_cpu_user in the reply")
4266 .trim()
4267 .parse()
4268 .expect("used_cpu_user is not a number")
4269 }
4270
4271 #[test]
4277 fn a_command_that_fails_leaves_nothing_half_written() {
4278 let mut f = Fixture::new();
4279 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4280 assert_eq!(reply, "-ERR offset is out of range\r\n");
4281 assert!(!reply.contains(':'), "no integer went out in front of it");
4282 }
4283
4284 #[test]
4285 fn quit_answers_first_and_closes_after() {
4286 let mut f = Fixture::new();
4287 let (flow, reply) = f.flow(&[b"QUIT"]);
4288 assert_eq!(reply, "+OK\r\n");
4289 assert_eq!(flow, Flow::Close);
4290 }
4291
4292 #[test]
4299 fn shutdown_writes_nothing_and_sets_the_flag() {
4300 let mut f = Fixture::new();
4301 assert!(!f.server.stopping(), "nobody has asked yet");
4302
4303 let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4304 assert_eq!(reply, "");
4305 assert_eq!(flow, Flow::Close);
4306 assert!(f.server.stopping());
4307 }
4308
4309 #[test]
4317 fn shutdown_takes_the_flags_redis_takes() {
4318 for flags in [
4319 &[b"NOSAVE".as_slice()][..],
4320 &[b"SAVE"],
4321 &[b"NOW"],
4322 &[b"FORCE"],
4323 &[b"nosave"],
4324 &[b"NOW", b"NOW"],
4325 &[b"SAVE", b"SAVE"],
4326 &[b"NOSAVE", b"NOW", b"FORCE"],
4327 ] {
4328 let mut f = Fixture::new();
4329 let mut parts = vec![b"SHUTDOWN".as_slice()];
4330 parts.extend_from_slice(flags);
4331 let (flow, reply) = f.flow(&parts);
4332 assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4333 assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4334 assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4335 }
4336
4337 for flags in [
4338 &[b"BOGUS".as_slice()][..],
4339 &[b"SAVE", b"NOSAVE"],
4340 &[b"NOSAVE", b"SAVE"],
4341 &[b"ABORT", b"NOW"],
4342 &[b"NOSAVE", b"ABORT"],
4343 &[b"NOW", b"FORCE", b"ABORT"],
4344 ] {
4345 let mut f = Fixture::new();
4346 let mut parts = vec![b"SHUTDOWN".as_slice()];
4347 parts.extend_from_slice(flags);
4348 assert_eq!(
4349 f.run(&parts),
4350 "-ERR syntax error\r\n",
4351 "SHUTDOWN {flags:?} was accepted"
4352 );
4353 assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4354 }
4355 }
4356
4357 #[test]
4365 fn shutdown_abort_never_has_anything_to_abort() {
4366 let mut f = Fixture::new();
4367 for parts in [
4368 &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4369 &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4370 ] {
4371 assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4372 assert!(!f.server.stopping(), "an abort stopped the server");
4373 }
4374 }
4375
4376 struct Backups {
4384 f: Fixture,
4385 dir: PathBuf,
4386 }
4387
4388 impl Backups {
4389 fn new(name: &str) -> Backups {
4390 let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4391 let _ = std::fs::remove_dir_all(&dir);
4392 std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4393 let mut f = Fixture::new();
4394 f.server.set_dir(dir.clone());
4395 Backups { f, dir }
4396 }
4397
4398 fn run(&mut self, parts: &[&[u8]]) -> String {
4399 self.f.run(parts)
4400 }
4401
4402 fn files(&self) -> Vec<String> {
4404 let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4405 Ok(entries) => entries
4406 .filter_map(|e| e.ok())
4407 .map(|e| e.file_name().to_string_lossy().into_owned())
4408 .collect(),
4409 Err(_) => Vec::new(),
4410 };
4411 names.sort();
4412 names
4413 }
4414
4415 fn read(&self, name: &str) -> Vec<u8> {
4416 std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4417 }
4418 }
4419
4420 impl Drop for Backups {
4421 fn drop(&mut self) {
4422 let _ = std::fs::remove_dir_all(&self.dir);
4423 }
4424 }
4425
4426 #[test]
4429 fn backup_walks_the_states_the_reference_walks() {
4430 let mut b = Backups::new("states");
4431 let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4432
4433 assert!(status(&mut b).contains("idle"));
4434 assert!(b.files().is_empty(), "an idle server has written a backup");
4435
4436 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4437 assert!(status(&mut b).contains("incrementing"));
4438 assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4439
4440 assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4441 assert!(status(&mut b).contains("sealed"));
4442 assert_eq!(
4443 b.files(),
4444 [
4445 "appendonly.aof.1.base.rdb",
4446 "appendonly.aof.1.incr.aof",
4447 "appendonly.aof.manifest",
4448 ]
4449 );
4450
4451 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4452 assert!(status(&mut b).contains("idle"));
4453 assert!(b.files().is_empty(), "cleanup left something behind");
4454 }
4455
4456 #[test]
4458 fn backup_refuses_the_moves_the_reference_refuses() {
4459 let mut b = Backups::new("refusals");
4460
4461 assert_eq!(
4462 b.run(&[b"BACKUP", b"SEAL"]),
4463 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4464 );
4465 assert_eq!(
4466 b.run(&[b"BACKUP", b"ABORT"]),
4467 "-ERR No backup in progress\r\n"
4468 );
4469 assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4472
4473 b.run(&[b"BACKUP", b"START"]);
4474 assert_eq!(
4475 b.run(&[b"BACKUP", b"START"]),
4476 "-ERR A backup is already in progress, ABORT it first\r\n"
4477 );
4478 assert_eq!(
4479 b.run(&[b"BACKUP", b"CLEANUP"]),
4480 "-ERR Backup is in progress\r\n"
4481 );
4482
4483 b.run(&[b"BACKUP", b"SEAL"]);
4484 assert_eq!(
4485 b.run(&[b"BACKUP", b"START"]),
4486 "-ERR A sealed backup exists, CLEANUP it first\r\n"
4487 );
4488 assert_eq!(
4489 b.run(&[b"BACKUP", b"SEAL"]),
4490 "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4491 );
4492 assert_eq!(
4493 b.run(&[b"BACKUP", b"ABORT"]),
4494 "-ERR No backup in progress\r\n"
4495 );
4496 }
4497
4498 #[test]
4505 fn backup_abort_removes_the_file_and_says_who_did_it() {
4506 let mut b = Backups::new("abort");
4507 b.run(&[b"BACKUP", b"START"]);
4508 assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4509
4510 let status = b.run(&[b"BACKUP", b"STATUS"]);
4511 assert!(status.contains("failed"), "{status}");
4512 assert!(status.contains("aborted by user"), "{status}");
4513 assert!(b.files().is_empty(), "abort left the base file behind");
4514 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4515
4516 assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4518 assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4519 let status = b.run(&[b"BACKUP", b"STATUS"]);
4520 assert!(status.contains("incrementing"), "{status}");
4521 assert!(!status.contains("aborted"), "the old error was kept");
4522 }
4523
4524 #[test]
4526 fn backup_list_names_the_files_that_are_pinned_so_far() {
4527 let mut b = Backups::new("list");
4528 assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4529
4530 b.run(&[b"BACKUP", b"START"]);
4531 let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4532 let base = base.to_string_lossy().into_owned();
4533 assert_eq!(
4534 b.run(&[b"BACKUP", b"LIST"]),
4535 format!("*1\r\n${}\r\n{base}\r\n", base.len())
4536 );
4537
4538 b.run(&[b"BACKUP", b"SEAL"]);
4539 let listed = b.run(&[b"BACKUP", b"LIST"]);
4540 assert!(listed.starts_with("*3\r\n"), "{listed}");
4541 let names: Vec<&str> = listed
4544 .lines()
4545 .filter(|l| l.starts_with('/') || l.contains(":\\"))
4546 .collect();
4547 assert_eq!(names.len(), 3, "{listed}");
4548 assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4549 assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4550 assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4551 }
4552
4553 #[test]
4561 fn a_backup_holds_the_dataset_as_it_was_at_start() {
4562 let mut b = Backups::new("contents");
4563 b.run(&[b"SET", b"bk", b"v1"]);
4564 b.run(&[b"BACKUP", b"START"]);
4565 b.run(&[b"SET", b"bk", b"v2"]);
4566 b.run(&[b"BACKUP", b"SEAL"]);
4567
4568 let base = b.read("appendonly.aof.1.base.rdb");
4569 assert!(base.starts_with(b"REDIS"), "not an RDB file");
4570 assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4571 assert!(
4572 !base.windows(2).any(|w| w == b"v2"),
4573 "the base file moved on after START"
4574 );
4575 let at = base
4580 .windows(8)
4581 .position(|w| w == b"aof-base")
4582 .expect("no aof-base aux field");
4583 assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4584
4585 assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4586 assert_eq!(
4587 String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4588 "file appendonly.aof.1.base.rdb seq 1 type b\n\
4589 file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4590 );
4591 }
4592
4593 #[test]
4596 fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4597 let mut b = Backups::new("status");
4598 b.f.server.set_clock_ms(1_700_000_000_000);
4599
4600 assert_eq!(
4601 b.run(&[b"BACKUP", b"STATUS"]),
4602 "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4603 $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4604 );
4605
4606 b.f.out = Out::new(Proto::Resp3);
4607 b.run(&[b"BACKUP", b"START"]);
4608 assert_eq!(
4609 b.run(&[b"BACKUP", b"STATUS"]),
4610 "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4611 $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4612 );
4613
4614 b.run(&[b"BACKUP", b"SEAL"]);
4615 let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4616 assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4617 }
4618
4619 #[test]
4622 fn a_sealed_backup_is_swept_away_after_the_timeout() {
4623 let mut b = Backups::new("ttl");
4624 b.f.server.set_clock_ms(1_000_000);
4625 assert_eq!(
4626 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4627 "+OK\r\n"
4628 );
4629 b.run(&[b"BACKUP", b"START"]);
4630 b.run(&[b"BACKUP", b"SEAL"]);
4631
4632 b.f.server.set_clock_ms(1_000_000 + 59_000);
4634 b.f.server.backup_expire();
4635 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4636 assert_eq!(b.files().len(), 3);
4637
4638 b.f.server.set_clock_ms(1_000_000 + 60_000);
4639 b.f.server.backup_expire();
4640 let status = b.run(&[b"BACKUP", b"STATUS"]);
4641 assert!(status.contains("idle"), "{status}");
4642 assert!(b.files().is_empty(), "the timeout left the files behind");
4643
4644 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4646 b.run(&[b"BACKUP", b"START"]);
4647 b.run(&[b"BACKUP", b"SEAL"]);
4648 b.f.server.set_clock_ms(9_000_000_000);
4649 b.f.server.backup_expire();
4650 assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4651 }
4652
4653 #[test]
4656 fn the_backup_settings_behave_the_way_the_reference_does() {
4657 let mut b = Backups::new("config");
4658 let dir = b.dir.to_string_lossy().into_owned();
4659
4660 assert_eq!(
4661 b.run(&[b"CONFIG", b"GET", b"dir"]),
4662 format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4663 );
4664 assert_eq!(
4665 b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4666 "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4667 );
4668 assert_eq!(
4669 b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4670 "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4671 );
4672
4673 assert_eq!(
4676 b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4677 "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4678 );
4679 assert_eq!(
4680 b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4681 "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4682 );
4683 assert!(
4684 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4685 .contains("argument couldn't be parsed into an integer")
4686 );
4687 assert!(
4688 b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4689 .contains("argument must be between 0 and 9223372036854775807 inclusive")
4690 );
4691 }
4692
4693 #[test]
4695 fn backup_help_is_the_text_the_reference_sends() {
4696 let mut f = Fixture::new();
4697 let help = f.run(&[b"BACKUP", b"HELP"]);
4698 assert!(help.starts_with("*17\r\n"), "{help}");
4699 assert!(
4700 help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4701 );
4702 assert!(help.contains("+ Start a new backup into the configured 'backupdirname'.\r\n"));
4703 assert!(help.contains("+ Freeze the current backup (BASE + INCR + manifest).\r\n"));
4704 assert!(help.contains("+ Return this help.\r\n+HELP\r\n+ Print this help.\r\n"));
4705 }
4706
4707 #[test]
4715 fn backup_refuses_what_it_cannot_read() {
4716 let mut f = Fixture::new();
4717 assert_eq!(
4718 f.run(&[b"BACKUP"]),
4719 "-ERR wrong number of arguments for 'backup' command\r\n"
4720 );
4721 assert_eq!(
4722 f.run(&[b"BACKUP", b"START", b"x"]),
4723 "-ERR wrong number of arguments for 'backup' command\r\n"
4724 );
4725 assert_eq!(
4726 f.run(&[b"BACKUP", b"NOPE"]),
4727 "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4728 );
4729 }
4730
4731 #[test]
4732 fn the_command_counter_counts_every_command_including_the_bad_ones() {
4733 let mut f = Fixture::new();
4734 f.run(&[b"PING"]);
4735 f.run(&[b"NOPE"]);
4736 f.run(&[b"GET"]);
4737 assert_eq!(f.server.totals().commands, 3);
4738 }
4739
4740 #[test]
4741 fn what_a_thread_marked_is_taken_by_the_maintenance_turn() {
4742 let mut server = Server::new();
4743 server.set_threads(2);
4744 server.dirty = 0;
4747 server.locals[1].mark(1 << 9);
4748 server.collect_marks();
4749 assert_ne!(server.dirty & (1 << 9), 0);
4750 assert_eq!(server.locals[1].dirty.load(Relaxed), 0);
4752 }
4753
4754 #[test]
4755 fn what_two_threads_counted_is_added_up_when_info_asks() {
4756 let mut server = Server::new();
4757 server.set_threads(2);
4758 let ping = lookup(b"PING").expect("PING is a command");
4762 for (at, calls) in [(0, 2), (1, 3)] {
4763 let counters = &server.locals[at];
4764 for _ in 0..calls {
4765 counters.stats.commands.bump();
4766 counters.cmdstats.at(ping).calls.bump();
4767 }
4768 counters.stats.opened();
4769 }
4770 assert_eq!(server.totals().commands, 5);
4771 assert_eq!(server.totals().clients, 2);
4772 assert_eq!(server.totals().connections, 2);
4773 let rows: Vec<_> = server.command_stats().collect();
4774 assert_eq!(rows.len(), 1);
4775 assert_eq!(rows[0].0, "ping");
4776 assert_eq!(rows[0].1.calls, 5);
4777 server.reset_stats();
4780 assert_eq!(server.totals().commands, 0);
4781 assert_eq!(server.totals().connections, 0);
4782 assert_eq!(server.totals().clients, 2);
4783 }
4784
4785 #[test]
4786 fn a_set_goes_from_bytes_to_bytes() {
4787 let mut f = Fixture::new();
4788 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4789 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4790 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4791 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4792 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4793 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4794 assert_eq!(
4795 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4796 "*3\r\n:1\r\n:0\r\n:1\r\n"
4797 );
4798 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4799 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4800 }
4801
4802 #[test]
4803 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4804 let mut f = Fixture::new();
4805 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4806 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4807 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4808 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4809 assert_eq!(
4810 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4811 "*2\r\n:0\r\n:0\r\n"
4812 );
4813 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4814 }
4815
4816 #[test]
4817 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
4818 let mut f = Fixture::new();
4822 f.run(&[b"SADD", b"s", b"one"]);
4823 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
4824
4825 f.run(&[b"HELLO", b"3"]);
4826 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
4827 }
4828
4829 #[test]
4830 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
4831 let mut f = Fixture::new();
4834 f.run(&[b"SADD", b"s", b"42"]);
4835 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
4836 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
4837 assert_eq!(
4838 f.run(&[b"SISMEMBER", b"s", b"042"]),
4839 ":0\r\n",
4840 "the member is the bytes and not the number they parse to"
4841 );
4842 }
4843
4844 #[test]
4845 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
4846 let mut f = Fixture::new();
4847 f.run(&[b"SET", b"str", b"v"]);
4848 f.run(&[b"SADD", b"set", b"a"]);
4849
4850 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4851 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
4852 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
4853 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
4854 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
4855 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
4856 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
4857 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
4858 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
4859
4860 assert_eq!(
4863 f.run(&[b"MGET", b"str", b"set", b"nope"]),
4864 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
4865 );
4866 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
4868 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
4869 }
4870
4871 #[test]
4872 fn a_wrongtype_leaves_nothing_half_written() {
4873 let mut f = Fixture::new();
4877 f.run(&[b"SET", b"k", b"v"]);
4878 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
4879 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
4880 assert!(!reply.contains('*'), "an array header went out in front");
4881 }
4882
4883 #[test]
4884 fn emptying_a_set_takes_the_key_with_it() {
4885 let mut f = Fixture::new();
4886 f.run(&[b"SADD", b"s", b"a", b"b"]);
4887 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4888 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
4889 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4890 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
4891 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4892 }
4893
4894 fn split_scan(reply: &str) -> (String, Vec<String>) {
4900 let mut lines = reply.split("\r\n");
4901 assert_eq!(lines.next(), Some("*2"), "got {reply}");
4902 lines.next().expect("the cursor header");
4903 let cursor = lines.next().expect("the cursor").to_owned();
4904 let header = lines.next().expect("the member header");
4905 let n: usize = header[1..].parse().expect("a member count");
4906 let mut members = Vec::with_capacity(n);
4907 for _ in 0..n {
4908 lines.next().expect("a member header");
4909 members.push(lines.next().expect("a member").to_owned());
4910 }
4911 (cursor, members)
4912 }
4913
4914 #[test]
4915 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
4916 let mut f = Fixture::new();
4917 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
4918
4919 let one = f.run(&[b"SPOP", b"s"]);
4920 assert!(
4921 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
4922 "got {one}"
4923 );
4924 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4925
4926 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
4928 assert!(rest.starts_with("*3\r\n"), "got {rest}");
4929 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4930 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
4932 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
4933 }
4934
4935 #[test]
4936 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
4937 let mut f = Fixture::new();
4942 f.run(&[b"HELLO", b"3"]);
4943 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
4944
4945 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
4946 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
4948
4949 f.run(&[b"SADD", b"one", b"z"]);
4953 assert_eq!(
4954 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
4955 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
4956 );
4957 }
4958
4959 #[test]
4960 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
4961 let mut f = Fixture::new();
4962 f.run(&[b"SADD", b"s", b"only"]);
4963 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4964 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4965 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
4966
4967 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
4968 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
4971 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
4972 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
4974 }
4975
4976 #[test]
4977 fn a_pop_count_that_is_not_a_positive_number_says_so() {
4978 let mut f = Fixture::new();
4979 f.run(&[b"SADD", b"s", b"a"]);
4980 let bad = "-ERR value is out of range, must be positive\r\n";
4981 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
4982 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
4983 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
4984 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
4986 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
4987 }
4988
4989 #[test]
4990 fn a_scan_walks_a_set_of_any_size_exactly_once() {
4991 let mut f = Fixture::new();
4992 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
4993 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
4994 .into_iter()
4995 .chain(members.iter().map(Vec::as_slice))
4996 .collect();
4997 f.run(&args);
4998
4999 let mut seen = Vec::new();
5000 let mut cursor = "0".to_owned();
5001 loop {
5002 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
5003 let (next, got) = split_scan(&reply);
5004 seen.extend(got);
5005 cursor = next;
5006 if cursor == "0" {
5007 break;
5008 }
5009 }
5010 seen.sort();
5011 seen.dedup();
5012 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
5013
5014 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
5017 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
5018 assert_eq!(cursor, "0");
5019 assert_eq!(got.len(), 3);
5020 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
5022 }
5023
5024 #[test]
5025 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
5026 let mut f = Fixture::new();
5027 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
5028
5029 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
5030 let mut got = got;
5031 got.sort();
5032 assert_eq!(got, ["aa", "ab"]);
5033
5034 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
5037 let mut got = got;
5038 got.sort();
5039 assert_eq!(got, ["12", "13"]);
5040
5041 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
5042 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
5043 assert_eq!(
5044 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
5045 "-ERR syntax error\r\n"
5046 );
5047 assert_eq!(
5050 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
5051 "-ERR syntax error\r\n"
5052 );
5053 }
5054
5055 #[test]
5056 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
5057 let mut f = Fixture::new();
5058 f.run(&[b"SADD", b"src", b"a", b"b"]);
5059 f.run(&[b"SADD", b"dst", b"c"]);
5060
5061 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
5062 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
5063 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
5064 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
5066 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
5067
5068 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
5071 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
5072 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
5073 }
5074
5075 #[test]
5076 fn moving_checks_the_types_in_the_order_redis_checks_them() {
5077 let mut f = Fixture::new();
5081 f.run(&[b"SET", b"str", b"v"]);
5082 f.run(&[b"SADD", b"set", b"a"]);
5083
5084 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5085 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
5086 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
5087 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
5088 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
5089 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
5090 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
5091 assert_eq!(
5092 f.run(&[b"SISMEMBER", b"set", b"a"]),
5093 ":1\r\n",
5094 "and none of that moved anything"
5095 );
5096 }
5097
5098 #[test]
5099 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5100 let mut f = Fixture::new();
5103 f.run(&[b"SADD", b"s", b"a"]);
5104 for bad in [
5105 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
5106 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
5107 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
5108 ] {
5109 let reply = f.run(bad);
5110 assert!(reply.starts_with("-ERR"), "got {reply}");
5111 assert!(!reply.contains('*'), "an array header went out in front");
5112 }
5113 }
5114
5115 #[test]
5116 fn a_hash_writes_reads_and_deletes_its_fields() {
5117 let mut f = Fixture::new();
5118 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
5119 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
5120 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5121 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
5122 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
5123 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
5124 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
5125 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
5126 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
5127 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
5128
5129 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
5132
5133 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
5134 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
5135 assert_eq!(
5136 f.run(&[b"EXISTS", b"h"]),
5137 ":0\r\n",
5138 "and losing the last field lost the key"
5139 );
5140 }
5141
5142 #[test]
5143 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
5144 let mut f = Fixture::new();
5145 f.run(&[b"HSET", b"h", b"a", b"1"]);
5146 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
5147 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
5148 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
5149 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
5150 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
5151
5152 f.run(&[b"HELLO", b"3"]);
5153 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
5154 assert_eq!(
5155 f.run(&[b"HGETALL", b"nokey"]),
5156 "%0\r\n",
5157 "a missing key is the empty hash and never a nil"
5158 );
5159 assert_eq!(
5160 f.run(&[b"HKEYS", b"h"]),
5161 "*1\r\n$1\r\na\r\n",
5162 "and the two that answer one side stay arrays"
5163 );
5164 }
5165
5166 #[test]
5167 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
5168 let mut f = Fixture::new();
5169 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
5170 assert_eq!(
5171 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
5172 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
5173 "the reply is positional, so b is a nil and not a gap"
5174 );
5175 assert_eq!(
5176 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
5177 "*2\r\n$-1\r\n$-1\r\n",
5178 "and a missing key is all nils rather than an empty array"
5179 );
5180
5181 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
5182 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
5183 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5184 }
5185
5186 #[test]
5187 fn a_hash_counts_up_and_says_so_when_it_cannot() {
5188 let mut f = Fixture::new();
5189 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
5190 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
5191 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
5192 assert_eq!(
5193 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
5194 "$4\r\n10.5\r\n",
5195 "a bulk string and not a double, on both protocols"
5196 );
5197
5198 f.run(&[b"HSET", b"h", b"s", b"words"]);
5199 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
5200 assert!(
5201 bad.starts_with("-ERR hash value is not an integer"),
5202 "{bad}"
5203 );
5204 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
5205 assert!(
5206 bad.starts_with("-ERR value is not an integer"),
5207 "a bad argument is not yet a hash value, {bad}"
5208 );
5209 assert_eq!(
5210 f.run(&[b"HGET", b"h", b"s"]),
5211 "$5\r\nwords\r\n",
5212 "and neither of them wrote anything"
5213 );
5214 }
5215
5216 #[test]
5217 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
5218 let mut f = Fixture::new();
5219 for i in 0..500 {
5220 let field = format!("field-{i}");
5221 let value = format!("value-{i}");
5222 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
5223 }
5224
5225 let mut seen: Vec<String> = Vec::new();
5226 let mut cursor = "0".to_owned();
5227 loop {
5228 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
5229 let (next, items) = scan_reply(&reply);
5230 assert_eq!(items.len() % 2, 0, "a pair went out half written");
5231 for pair in items.chunks(2) {
5232 assert_eq!(
5233 pair[0].strip_prefix("field-"),
5234 pair[1].strip_prefix("value-"),
5235 "a field came back with someone else's value"
5236 );
5237 seen.push(pair[0].clone());
5238 }
5239 cursor = next;
5240 if cursor == "0" {
5241 break;
5242 }
5243 }
5244 seen.sort();
5245 seen.dedup();
5246 assert_eq!(seen.len(), 500, "every field once and only once");
5247
5248 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
5249 assert!(
5250 items.iter().all(|s| s.starts_with("field-")),
5251 "NOVALUES still sent the values"
5252 );
5253
5254 let (_, one) = scan_reply(&f.run(&[
5255 b"HSCAN",
5256 b"h",
5257 b"0",
5258 b"MATCH",
5259 b"field-499",
5260 b"COUNT",
5261 b"1000",
5262 ]));
5263 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
5264 }
5265
5266 #[test]
5267 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
5268 let mut f = Fixture::new();
5269 f.run(&[b"HSET", b"h", b"a", b"1"]);
5270 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
5271 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
5272 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
5273 assert_eq!(
5274 f.run(&[b"HRANDFIELD", b"h", b"3"]),
5275 "*1\r\n$1\r\na\r\n",
5276 "a positive count is capped at the size of the hash"
5277 );
5278 assert_eq!(
5279 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
5280 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
5281 "and a negative one repeats itself"
5282 );
5283 assert_eq!(
5284 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5285 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5286 "flat on RESP2"
5287 );
5288
5289 f.run(&[b"HELLO", b"3"]);
5290 assert_eq!(
5291 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5292 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5293 "and nested on RESP3, but still an array and never a map"
5294 );
5295 }
5296
5297 #[test]
5298 fn every_hash_command_says_wrongtype_and_writes_nothing() {
5299 let mut f = Fixture::new();
5300 f.run(&[b"SET", b"str", b"v"]);
5301 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5302
5303 for cmd in [
5304 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
5305 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
5306 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5307 &[b"HGET".as_slice(), b"str", b"f"][..],
5308 &[b"HMGET".as_slice(), b"str", b"f"][..],
5309 &[b"HDEL".as_slice(), b"str", b"f"][..],
5310 &[b"HLEN".as_slice(), b"str"][..],
5311 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5312 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5313 &[b"HGETALL".as_slice(), b"str"][..],
5314 &[b"HKEYS".as_slice(), b"str"][..],
5315 &[b"HVALS".as_slice(), b"str"][..],
5316 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5317 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5318 &[b"HRANDFIELD".as_slice(), b"str"][..],
5319 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5320 &[b"HSCAN".as_slice(), b"str", b"0"][..],
5321 ] {
5322 let reply = f.run(cmd);
5323 assert_eq!(reply, wrong, "{:?}", cmd[0]);
5324 }
5325 assert_eq!(
5326 f.run(&[b"GET", b"str"]),
5327 "$1\r\nv\r\n",
5328 "and none of them touched the value"
5329 );
5330 }
5331
5332 #[test]
5333 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5334 let mut f = Fixture::new();
5335 f.run(&[b"HSET", b"h", b"f", b"v"]);
5336 for bad in [
5337 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5338 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5339 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5340 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5341 ] {
5342 let reply = f.run(bad);
5343 assert!(reply.starts_with("-ERR"), "got {reply}");
5344 assert!(!reply.contains('*'), "an array header went out in front");
5345 }
5346 }
5347
5348 #[test]
5349 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5350 let mut f = Fixture::new();
5351 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5352 assert_eq!(
5353 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5354 "*1\r\n:1\r\n"
5355 );
5356 assert_eq!(
5357 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5358 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5359 "one answer per field, and the two sentinels are TTL's own"
5360 );
5361
5362 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5365 assert!((99_000..=100_000).contains(&ms), "got {ms}");
5366 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5367 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5368 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5369 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5370
5371 assert_eq!(
5372 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5373 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5374 "one for the deadline taken off, and it does not say what it was"
5375 );
5376 assert_eq!(
5377 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5378 "*1\r\n:-1\r\n"
5379 );
5380 assert_eq!(
5381 f.run(&[b"HGET", b"h", b"a"]),
5382 "$1\r\n1\r\n",
5383 "and the field is still there with the value it had"
5384 );
5385 }
5386
5387 #[test]
5388 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5389 let mut f = Fixture::new();
5390 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5391 assert_eq!(
5392 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5393 "*1\r\n:2\r\n",
5394 "two, and not one, because nothing was stored"
5395 );
5396 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5397 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5398
5399 assert_eq!(
5400 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5401 "*1\r\n:2\r\n"
5402 );
5403 assert_eq!(
5404 f.run(&[b"EXISTS", b"h"]),
5405 ":0\r\n",
5406 "and the last field going took the key with it"
5407 );
5408
5409 f.run(&[b"HSET", b"h", b"a", b"1"]);
5412 assert_eq!(
5413 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5414 "*1\r\n:2\r\n"
5415 );
5416 }
5417
5418 #[test]
5419 fn a_field_is_gone_once_its_moment_passes() {
5420 let mut f = Fixture::new();
5421 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5422 assert_eq!(
5423 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5424 "*1\r\n:1\r\n"
5425 );
5426 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5427
5428 f.server.advance_clock_ms(60);
5432 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5433 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5434 assert_eq!(
5435 f.run(&[b"HGETALL", b"h"]),
5436 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5437 "and the walks do not hand back a field that has expired"
5438 );
5439 }
5440
5441 #[test]
5442 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5443 let mut f = Fixture::new();
5444 for cmd in [
5445 &[
5446 b"HEXPIRE".as_slice(),
5447 b"nokey",
5448 b"100",
5449 b"FIELDS",
5450 b"2",
5451 b"a",
5452 b"b",
5453 ][..],
5454 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5455 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5456 &[
5457 b"HEXPIRETIME".as_slice(),
5458 b"nokey",
5459 b"FIELDS",
5460 b"2",
5461 b"a",
5462 b"b",
5463 ][..],
5464 &[
5465 b"HPERSIST".as_slice(),
5466 b"nokey",
5467 b"FIELDS",
5468 b"2",
5469 b"a",
5470 b"b",
5471 ][..],
5472 ] {
5473 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5474 }
5475 }
5476
5477 #[test]
5478 fn writing_a_field_clears_the_deadline_that_was_on_it() {
5479 let mut f = Fixture::new();
5480 f.run(&[b"HSET", b"h", b"a", b"1"]);
5481 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5482 f.run(&[b"HSET", b"h", b"a", b"2"]);
5483 assert_eq!(
5484 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5485 "*1\r\n:-1\r\n",
5486 "Redis has done this since 7.4, and it is why HGETEX exists"
5487 );
5488 }
5489
5490 #[test]
5491 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5492 let mut f = Fixture::new();
5493 f.run(&[b"HSET", b"h", b"a", b"1"]);
5494 assert_eq!(
5495 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5496 "*1\r\n:0\r\n",
5497 "XX on a field with no deadline changes nothing"
5498 );
5499 assert_eq!(
5500 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5501 "*1\r\n:1\r\n"
5502 );
5503 assert_eq!(
5504 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5505 "*1\r\n:0\r\n",
5506 "and NX will not move one that is already there"
5507 );
5508 assert_eq!(
5509 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5510 "*1\r\n:0\r\n"
5511 );
5512 assert_eq!(
5513 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5514 "*1\r\n:1\r\n"
5515 );
5516 assert_eq!(
5517 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5518 "*1\r\n:1\r\n"
5519 );
5520 assert_eq!(
5521 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5522 "*1\r\n:50\r\n"
5523 );
5524 }
5525
5526 #[test]
5527 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5528 let mut f = Fixture::new();
5529 f.run(&[b"HSET", b"h", b"a", b"1"]);
5530 for (bad, want) in [
5531 (
5532 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5533 "-ERR invalid expire time, must be >= 0",
5534 ),
5535 (
5536 &[
5537 b"HEXPIRE".as_slice(),
5538 b"h",
5539 b"9999999999999999",
5540 b"FIELDS",
5541 b"1",
5542 b"a",
5543 ][..],
5544 "-ERR invalid expire time in 'hexpire' command",
5545 ),
5546 (
5547 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5548 "-ERR wrong number of arguments for 'hexpire' command",
5549 ),
5550 (
5551 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5552 "-ERR Parameter `numFields` should be greater than 0",
5553 ),
5554 (
5555 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5556 "-ERR wrong number of arguments",
5557 ),
5558 (
5559 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5560 "-ERR wrong number of arguments",
5561 ),
5562 ] {
5563 let reply = f.run(bad);
5564 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5565 assert!(!reply.contains('*'), "an array header went out in front");
5566 }
5567 assert_eq!(
5568 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5569 "*1\r\n:-1\r\n",
5570 "and not one of them put a deadline on anything"
5571 );
5572 }
5573
5574 #[test]
5575 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5576 let mut f = Fixture::new();
5577 f.run(&[b"SET", b"str", b"v"]);
5578 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5579
5580 for cmd in [
5581 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5582 &[
5583 b"HPEXPIRE".as_slice(),
5584 b"str",
5585 b"100",
5586 b"FIELDS",
5587 b"1",
5588 b"f",
5589 ][..],
5590 &[
5591 b"HEXPIREAT".as_slice(),
5592 b"str",
5593 b"9999999999",
5594 b"FIELDS",
5595 b"1",
5596 b"f",
5597 ][..],
5598 &[
5599 b"HPEXPIREAT".as_slice(),
5600 b"str",
5601 b"9999999999999",
5602 b"FIELDS",
5603 b"1",
5604 b"f",
5605 ][..],
5606 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5607 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5608 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5609 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5610 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5611 ] {
5612 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5613 }
5614 assert_eq!(
5615 f.run(&[b"GET", b"str"]),
5616 "$1\r\nv\r\n",
5617 "and none of them touched the value"
5618 );
5619 }
5620
5621 #[test]
5622 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5623 let mut f = Fixture::new();
5624 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5625 assert_eq!(
5626 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5627 "*2\r\n$1\r\n1\r\n$-1\r\n",
5628 "positional, so the field that was not there is a nil in its place"
5629 );
5630 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5631 assert_eq!(
5632 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5633 "*1\r\n$-1\r\n"
5634 );
5635 assert_eq!(
5636 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5637 "*1\r\n$1\r\n2\r\n"
5638 );
5639 assert_eq!(
5640 f.run(&[b"EXISTS", b"h"]),
5641 ":0\r\n",
5642 "and the last field took the key"
5643 );
5644 }
5645
5646 #[test]
5647 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5648 let mut f = Fixture::new();
5649 f.run(&[b"HSET", b"h", b"a", b"1"]);
5650 assert_eq!(
5651 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5652 "*1\r\n$1\r\n1\r\n"
5653 );
5654 assert_eq!(
5655 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5656 "*1\r\n:-1\r\n",
5657 "no option means leave it alone, which is the one place this is not GETEX"
5658 );
5659
5660 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5661 assert_eq!(
5662 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5663 "*1\r\n:100\r\n"
5664 );
5665 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5666 assert_eq!(
5667 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5668 "*1\r\n:100\r\n",
5669 "and a plain read really does leave it alone"
5670 );
5671 assert_eq!(
5672 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5673 "*1\r\n$1\r\n1\r\n"
5674 );
5675 assert_eq!(
5676 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5677 "*1\r\n:-1\r\n"
5678 );
5679
5680 assert_eq!(
5681 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5682 "*1\r\n$1\r\n1\r\n",
5683 "the value goes out before the deadline that has already gone is applied"
5684 );
5685 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5686 assert_eq!(
5687 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5688 "*1\r\n$-1\r\n"
5689 );
5690 }
5691
5692 #[test]
5693 fn hsetex_writes_all_of_it_or_none_of_it() {
5694 let mut f = Fixture::new();
5695 assert_eq!(
5696 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5697 ":1\r\n"
5698 );
5699 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5700 assert_eq!(
5701 f.run(&[
5702 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5703 ]),
5704 ":0\r\n",
5705 "FNX wants every field named to be missing"
5706 );
5707 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5708 assert_eq!(
5709 f.run(&[b"HEXISTS", b"h", b"new"]),
5710 ":0\r\n",
5711 "and none of the list was written"
5712 );
5713 assert_eq!(
5714 f.run(&[
5715 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5716 ]),
5717 ":0\r\n",
5718 "and FXX wants every one of them to be there"
5719 );
5720 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5721 assert_eq!(
5722 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5723 ":1\r\n"
5724 );
5725 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5726
5727 assert_eq!(
5728 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5729 ":0\r\n"
5730 );
5731 assert_eq!(
5732 f.run(&[b"EXISTS", b"gone"]),
5733 ":0\r\n",
5734 "a key with no fields cannot meet FXX and is not created trying"
5735 );
5736 }
5737
5738 #[test]
5739 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5740 let mut f = Fixture::new();
5741 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5742 assert_eq!(
5743 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5744 "*1\r\n:100\r\n"
5745 );
5746
5747 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5748 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5749 assert_eq!(
5750 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5751 "*1\r\n:100\r\n",
5752 "KEEPTTL put back what the write cleared"
5753 );
5754
5755 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5756 assert_eq!(
5757 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5758 "*1\r\n:-1\r\n",
5759 "and without it a write clears the deadline the way HSET does"
5760 );
5761
5762 assert_eq!(
5765 f.run(&[
5766 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5767 ]),
5768 ":1\r\n"
5769 );
5770 assert_eq!(
5771 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5772 "*1\r\n:100\r\n"
5773 );
5774
5775 assert_eq!(
5776 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5777 ":1\r\n",
5778 "written, and not the separate code the HEXPIRE family has for this"
5779 );
5780 assert_eq!(
5781 f.run(&[b"EXISTS", b"h"]),
5782 ":0\r\n",
5783 "and storing it and then removing it emptied the hash"
5784 );
5785 }
5786
5787 #[test]
5788 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5789 let mut f = Fixture::new();
5790 f.run(&[b"HSET", b"h", b"a", b"1"]);
5791 for (bad, want) in [
5792 (
5794 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5795 "-ERR Number of fields must be a positive integer",
5796 ),
5797 (
5798 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5799 "-ERR The `numfields` parameter must match the number of arguments",
5800 ),
5801 (
5802 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5803 "-ERR Mandatory argument FIELDS is missing or not at the right position",
5804 ),
5805 (
5807 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5808 "-ERR invalid number of fields",
5809 ),
5810 (
5811 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5812 "-ERR wrong number of arguments",
5813 ),
5814 (
5815 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5816 "-ERR unknown argument: FIELD",
5817 ),
5818 (
5819 &[
5820 b"HGETEX".as_slice(),
5821 b"h",
5822 b"KEEPTTL",
5823 b"FIELDS",
5824 b"1",
5825 b"a",
5826 ][..],
5827 "-ERR unknown argument: KEEPTTL",
5828 ),
5829 (
5830 &[
5831 b"HGETEX".as_slice(),
5832 b"h",
5833 b"EX",
5834 b"100",
5835 b"PERSIST",
5836 b"FIELDS",
5837 b"1",
5838 b"a",
5839 ][..],
5840 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
5841 ),
5842 (
5843 &[
5844 b"HSETEX".as_slice(),
5845 b"h",
5846 b"EX",
5847 b"1",
5848 b"KEEPTTL",
5849 b"FIELDS",
5850 b"1",
5851 b"a",
5852 b"1",
5853 ][..],
5854 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
5855 ),
5856 (
5857 &[
5858 b"HSETEX".as_slice(),
5859 b"h",
5860 b"FNX",
5861 b"FXX",
5862 b"FIELDS",
5863 b"1",
5864 b"a",
5865 b"1",
5866 ][..],
5867 "-ERR Only one of FXX or FNX arguments can be specified",
5868 ),
5869 (
5870 &[
5871 b"HSETEX".as_slice(),
5872 b"h",
5873 b"FIELDS",
5874 b"2",
5875 b"a",
5876 b"1",
5877 b"b",
5878 ][..],
5879 "-ERR wrong number of arguments",
5880 ),
5881 (
5882 &[
5883 b"HGETEX".as_slice(),
5884 b"h",
5885 b"EX",
5886 b"-1",
5887 b"FIELDS",
5888 b"1",
5889 b"a",
5890 ][..],
5891 "-ERR invalid expire time, must be >= 0",
5892 ),
5893 (
5894 &[
5895 b"HGETEX".as_slice(),
5896 b"h",
5897 b"PXAT",
5898 b"99999999999999",
5899 b"FIELDS",
5900 b"1",
5901 b"a",
5902 ][..],
5903 "-ERR invalid expire time in 'hgetex' command",
5904 ),
5905 (
5906 &[
5907 b"HSETEX".as_slice(),
5908 b"h",
5909 b"EX",
5910 b"abc",
5911 b"FIELDS",
5912 b"1",
5913 b"a",
5914 b"1",
5915 ][..],
5916 "-ERR value is not an integer or out of range",
5917 ),
5918 ] {
5919 let reply = f.run(bad);
5920 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5921 assert!(!reply.contains('*'), "an array header went out in front");
5922 }
5923 assert_eq!(
5924 f.run(&[b"HGET", b"h", b"a"]),
5925 "$1\r\n1\r\n",
5926 "and not one of them wrote anything"
5927 );
5928 assert_eq!(
5929 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5930 "*1\r\n:-1\r\n"
5931 );
5932 }
5933
5934 #[test]
5935 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
5936 let mut f = Fixture::new();
5937 f.run(&[b"SET", b"str", b"v"]);
5938 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5939 for cmd in [
5940 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5941 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5942 &[
5943 b"HGETEX".as_slice(),
5944 b"str",
5945 b"EX",
5946 b"100",
5947 b"FIELDS",
5948 b"1",
5949 b"f",
5950 ][..],
5951 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
5952 ] {
5953 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5954 }
5955 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5956 }
5957
5958 #[test]
5966 fn himport_writes_declared_values_into_sorted_fields() {
5967 let mut f = Fixture::new();
5968 assert_eq!(
5969 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
5970 "+OK\r\n"
5971 );
5972 assert_eq!(
5973 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
5974 "+OK\r\n"
5975 );
5976 assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
5977 assert_eq!(
5978 f.run(&[b"HGETALL", b"k"]),
5979 bulks(&["a", "3", "b", "1", "aa", "2"])
5980 );
5981 }
5982
5983 #[test]
5986 fn himport_set_replaces_the_whole_key() {
5987 let mut f = Fixture::new();
5988 f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
5989 f.run(&[b"EXPIRE", b"k", b"100"]);
5990 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5991 assert_eq!(
5992 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5993 "+OK\r\n"
5994 );
5995 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5996 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
5997 }
5998
5999 #[test]
6002 fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
6003 let mut f = Fixture::new();
6004 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
6005 f.run(&[b"SELECT", b"1"]);
6006 assert_eq!(
6007 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6008 "+OK\r\n"
6009 );
6010 f.run(&[b"SELECT", b"0"]);
6011 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
6012 assert_eq!(
6013 f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
6014 "-ERR no such fieldset\r\n"
6015 );
6016 }
6017
6018 #[test]
6025 fn himport_complains_in_the_order_a_real_server_does() {
6026 let mut f = Fixture::new();
6027 f.run(&[b"SET", b"str", b"v"]);
6028 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6029 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6030 assert_eq!(
6031 f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
6032 wrong,
6033 "the type beats a missing fieldset"
6034 );
6035 assert_eq!(
6036 f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
6037 wrong,
6038 "and it beats a value count that does not fit"
6039 );
6040 assert_eq!(
6041 f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
6042 "-ERR no such fieldset\r\n"
6043 );
6044 for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
6046 let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
6047 line.extend_from_slice(values);
6048 assert_eq!(
6049 f.run(&line),
6050 "-ERR value count does not match fieldset field count\r\n",
6051 "{} values into two fields",
6052 values.len()
6053 );
6054 }
6055 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6056 }
6057
6058 #[test]
6060 fn himport_checks_each_subcommand_count_under_its_own_name() {
6061 let mut f = Fixture::new();
6062 assert_eq!(
6063 f.run(&[b"HIMPORT"]),
6064 "-ERR wrong number of arguments for 'himport' command\r\n"
6065 );
6066 for (rest, name) in [
6067 (&["PREPARE"][..], "prepare"),
6068 (&["PREPARE", "fs"][..], "prepare"),
6069 (&["SET"][..], "set"),
6070 (&["SET", "k"][..], "set"),
6071 (&["SET", "k", "fs"][..], "set"),
6072 (&["DISCARD"][..], "discard"),
6073 (&["DISCARD", "a", "b"][..], "discard"),
6074 (&["DISCARDALL", "x"][..], "discardall"),
6075 ] {
6076 let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
6077 line.extend(rest.iter().map(|a| a.as_bytes()));
6078 assert_eq!(
6079 f.run(&line),
6080 format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
6081 "HIMPORT {}",
6082 rest.join(" ")
6083 );
6084 }
6085 assert_eq!(
6086 f.run(&[b"HIMPORT", b"NOPE", b"x"]),
6087 "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
6088 );
6089 }
6090
6091 #[test]
6094 fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
6095 let mut f = Fixture::new();
6096 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6097 assert_eq!(
6098 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
6099 "-ERR duplicate field name in fieldset\r\n"
6100 );
6101 assert_eq!(
6102 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6103 "+OK\r\n"
6104 );
6105 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6106 }
6107
6108 #[test]
6111 fn himport_prepare_replaces_and_the_discards_count() {
6112 let mut f = Fixture::new();
6113 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6114 f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
6115 assert_eq!(
6116 f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6117 "+OK\r\n"
6118 );
6119 assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
6120
6121 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
6122 assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
6123 f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
6124 f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
6125 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
6126 assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
6127 }
6128
6129 fn int(reply: &str) -> i64 {
6135 let body = reply
6136 .strip_prefix(':')
6137 .and_then(|s| s.strip_suffix("\r\n"))
6138 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
6139 body.parse().expect("an integer")
6140 }
6141
6142 fn int_reply(reply: &str) -> i64 {
6143 let body = reply
6144 .strip_prefix("*1\r\n:")
6145 .and_then(|s| s.strip_suffix("\r\n"))
6146 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
6147 body.parse().expect("an integer")
6148 }
6149
6150 fn scan_reply(reply: &str) -> (String, Vec<String>) {
6152 let mut lines = reply.split("\r\n");
6153 assert_eq!(lines.next(), Some("*2"), "got {reply}");
6154 lines.next().expect("the cursor header");
6155 let cursor = lines.next().expect("a cursor").to_owned();
6156 let header = lines.next().expect("an item count");
6157 let n: usize = header[1..].parse().expect("a count");
6158 let mut items = Vec::with_capacity(n);
6159 for _ in 0..n {
6160 lines.next().expect("an item header");
6161 items.push(lines.next().expect("an item").to_owned());
6162 }
6163 (cursor, items)
6164 }
6165
6166 fn sorted(reply: &str) -> Vec<String> {
6169 let mut lines = reply.split("\r\n");
6170 let header = lines.next().expect("a header");
6171 assert!(
6172 header.starts_with('*') || header.starts_with('~'),
6173 "got {reply}"
6174 );
6175 let n: usize = header[1..].parse().expect("a member count");
6176 let mut got = Vec::with_capacity(n);
6177 for _ in 0..n {
6178 lines.next().expect("a member header");
6179 got.push(lines.next().expect("a member").to_owned());
6180 }
6181 got.sort();
6182 got
6183 }
6184
6185 #[test]
6186 fn the_algebra_answers_what_the_sets_share_and_do_not() {
6187 let mut f = Fixture::new();
6188 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6189 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6190 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
6191
6192 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
6193 assert_eq!(
6194 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
6195 ["1", "2", "3", "4", "5"]
6196 );
6197 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
6198 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
6199
6200 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
6203 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
6204 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
6205 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
6206 }
6207
6208 #[test]
6209 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
6210 let mut f = Fixture::new();
6211 f.run(&[b"SADD", b"a", b"x"]);
6212 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
6213 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
6214 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
6215
6216 f.run(&[b"HELLO", b"3"]);
6217 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
6218 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
6219 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
6220 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
6221 }
6222
6223 #[test]
6224 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
6225 let mut f = Fixture::new();
6226 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6227 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6228
6229 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
6230 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
6231 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
6232 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
6233 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
6234 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
6235
6236 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
6239 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6240 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
6241 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
6242
6243 f.run(&[b"SET", b"str", b"v"]);
6246 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
6247 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
6248 }
6249
6250 #[test]
6251 fn sintercard_counts_without_building_and_stops_at_a_limit() {
6252 let mut f = Fixture::new();
6253 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6254 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
6255
6256 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
6257 assert_eq!(
6258 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6259 ":2\r\n"
6260 );
6261 assert_eq!(
6262 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6263 ":3\r\n",
6264 "a limit of zero is no limit"
6265 );
6266 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
6267 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
6268
6269 assert_eq!(
6271 f.run(&[b"SINTERCARD", b"0", b"a"]),
6272 "-ERR numkeys should be greater than 0\r\n"
6273 );
6274 assert_eq!(
6275 f.run(&[b"SINTERCARD", b"abc", b"a"]),
6276 "-ERR numkeys should be greater than 0\r\n"
6277 );
6278 assert_eq!(
6279 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
6280 "-ERR Number of keys can't be greater than number of args\r\n"
6281 );
6282 assert_eq!(
6283 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
6284 "-ERR LIMIT can't be negative\r\n"
6285 );
6286 assert_eq!(
6287 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
6288 "-ERR syntax error\r\n"
6289 );
6290 f.run(&[b"SADD", b"LIMIT", b"2"]);
6292 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
6293 }
6294
6295 #[test]
6298 fn sunioncard_and_sdiffcard_count_without_building() {
6299 let mut f = Fixture::new();
6300 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6301 f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
6302
6303 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
6304 assert_eq!(
6305 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6306 ":2\r\n"
6307 );
6308 assert_eq!(
6309 f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6310 ":6\r\n",
6311 "a limit of zero is no limit"
6312 );
6313 assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6314 assert_eq!(
6315 f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6316 ":4\r\n",
6317 "a missing key adds nothing to a union"
6318 );
6319
6320 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6321 assert_eq!(
6322 f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6323 ":1\r\n"
6324 );
6325 assert_eq!(
6326 f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6327 ":2\r\n",
6328 "a difference is not symmetric"
6329 );
6330 assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6331 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6332 assert_eq!(
6333 f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6334 ":0\r\n",
6335 "nothing taken away from nothing"
6336 );
6337
6338 for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6341 assert_eq!(
6342 f.run(&[name, b"0", b"a"]),
6343 "-ERR numkeys should be greater than 0\r\n"
6344 );
6345 assert_eq!(
6346 f.run(&[name, b"abc", b"a"]),
6347 "-ERR numkeys should be greater than 0\r\n"
6348 );
6349 assert_eq!(
6350 f.run(&[name, b"-1", b"a"]),
6351 "-ERR numkeys should be greater than 0\r\n"
6352 );
6353 assert_eq!(
6354 f.run(&[name, b"3", b"a", b"b"]),
6355 "-ERR Number of keys can't be greater than number of args\r\n"
6356 );
6357 assert_eq!(
6358 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6359 "-ERR LIMIT can't be negative\r\n"
6360 );
6361 assert_eq!(
6362 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6363 "-ERR LIMIT can't be negative\r\n",
6364 "a LIMIT that is not a number gets the negative message too"
6365 );
6366 assert_eq!(
6367 f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6368 "-ERR syntax error\r\n"
6369 );
6370 assert_eq!(
6371 f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6372 "-ERR syntax error\r\n"
6373 );
6374 assert_eq!(
6375 f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6376 "-ERR syntax error\r\n"
6377 );
6378 }
6379
6380 f.run(&[b"SADD", b"LIMIT", b"2"]);
6382 assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6383 assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6384 }
6385
6386 #[test]
6387 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6388 let mut f = Fixture::new();
6389 f.run(&[b"SADD", b"a", b"1"]);
6390 f.run(&[b"SADD", b"d", b"old"]);
6391 f.run(&[b"SET", b"str", b"v"]);
6392
6393 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6394 for bad in [
6395 &[b"SINTER".as_slice(), b"a", b"str"][..],
6396 &[b"SUNION".as_slice(), b"str"][..],
6397 &[b"SDIFF".as_slice(), b"a", b"str"][..],
6398 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6399 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6400 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6401 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6402 ] {
6403 let reply = f.run(bad);
6404 assert_eq!(reply, wrong, "for {:?}", bad[0]);
6405 }
6406 assert_eq!(
6407 f.run(&[b"SMEMBERS", b"d"]),
6408 "*1\r\n$3\r\nold\r\n",
6409 "and the destination was left alone every time"
6410 );
6411 }
6412
6413 #[test]
6416 fn churning_sets_does_not_grow_the_server() {
6417 let mut f = Fixture::new();
6418 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6419 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6420 .chain(std::iter::once(&b"s"[..]))
6421 .chain(members.iter().map(Vec::as_slice))
6422 .collect();
6423
6424 f.run(&args);
6425 f.run(&[b"DEL", b"s"]);
6426 f.server.compact_step();
6427 let after_first = f.server.memory_bytes();
6428
6429 for _ in 0..200 {
6430 f.run(&args);
6431 f.run(&[b"DEL", b"s"]);
6432 f.server.compact_step();
6433 }
6434 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6435 assert!(
6436 f.server.memory_bytes() <= after_first * 2,
6437 "held {} after two hundred passes against {after_first} after one",
6438 f.server.memory_bytes()
6439 );
6440 }
6441
6442 #[test]
6450 fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6451 let mut f = Fixture::new();
6452 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6453 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6454 assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6455 assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6456 assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6457 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6458
6459 assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6461 assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6462 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6463
6464 f.run(&[b"SET", b"num", b"12345"]);
6465 assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6466 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6467 assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6468 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6469 assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6470 }
6471
6472 #[test]
6477 fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6478 let mut f = Fixture::new();
6479 f.run(&[b"SET", b"mykey", b"foobar"]);
6480 assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6481 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6482 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6483 assert_eq!(
6484 f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6485 ":6\r\n"
6486 );
6487 assert_eq!(
6488 f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6489 ":25\r\n"
6490 );
6491 assert_eq!(
6492 f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6493 ":17\r\n"
6494 );
6495 assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6496
6497 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6500
6501 assert_eq!(
6503 f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6504 "-ERR syntax error\r\n"
6505 );
6506 assert_eq!(
6507 f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6508 "-ERR syntax error\r\n"
6509 );
6510 }
6511
6512 #[test]
6518 fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6519 let mut f = Fixture::new();
6520 f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6521 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6522 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6523 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6524 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6525 assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6526
6527 f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6528 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6529 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6530 assert_eq!(
6531 f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6532 ":8\r\n"
6533 );
6534
6535 assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6538 assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6539 }
6540
6541 #[test]
6543 fn the_eight_combinations_write_what_a_real_server_writes() {
6544 let mut f = Fixture::new();
6545 f.run(&[b"SET", b"a", b"abc"]);
6546 f.run(&[b"SET", b"b", b"abd"]);
6547 let cases: &[(&[u8], &str)] = &[
6548 (b"AND", "ab`"),
6549 (b"OR", "abg"),
6550 (b"XOR", "\u{0}\u{0}\u{7}"),
6551 (b"DIFF", "\u{0}\u{0}\u{3}"),
6552 (b"DIFF1", "\u{0}\u{0}\u{4}"),
6553 (b"ANDOR", "ab`"),
6554 (b"ONE", "\u{0}\u{0}\u{7}"),
6555 ];
6556 for (op, want) in cases {
6557 assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6558 assert_eq!(
6559 f.run(&[b"GET", b"d"]),
6560 format!("$3\r\n{want}\r\n"),
6561 "{op:?}"
6562 );
6563 }
6564 assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6566 assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6567
6568 assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6571 assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6572
6573 f.run(&[b"SET", b"dest", b"x"]);
6576 assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6577 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6578 }
6579
6580 #[test]
6582 fn bitop_names_the_operation_in_its_own_complaints() {
6583 let mut f = Fixture::new();
6584 f.run(&[b"SET", b"a", b"abc"]);
6585 assert_eq!(
6586 f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6587 "-ERR syntax error\r\n"
6588 );
6589 assert_eq!(
6590 f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6591 "-ERR BITOP NOT must be called with a single source key.\r\n"
6592 );
6593 for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6594 assert_eq!(
6595 f.run(&[b"BITOP", op, b"d", b"a"]),
6596 format!(
6597 "-ERR BITOP {} must be called with at least two source keys.\r\n",
6598 String::from_utf8_lossy(op)
6599 )
6600 );
6601 }
6602 f.run(&[b"LPUSH", b"l", b"x"]);
6603 assert_eq!(
6604 f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6605 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6606 );
6607 }
6608
6609 #[test]
6611 fn bitfield_reads_and_writes_packed_fields() {
6612 let mut f = Fixture::new();
6613 assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6614 assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6615
6616 assert_eq!(
6617 f.run(&[
6618 b"BITFIELD",
6619 b"bf",
6620 b"INCRBY",
6621 b"u2",
6622 b"100",
6623 b"1",
6624 b"GET",
6625 b"u4",
6626 b"0"
6627 ]),
6628 "*2\r\n:1\r\n:0\r\n"
6629 );
6630 assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6633
6634 assert_eq!(
6636 f.run(&[
6637 b"BITFIELD",
6638 b"bf",
6639 b"SET",
6640 b"u8",
6641 b"#0",
6642 b"255",
6643 b"GET",
6644 b"u8",
6645 b"#0"
6646 ]),
6647 "*2\r\n:0\r\n:255\r\n"
6648 );
6649
6650 assert_eq!(
6651 f.run(&[
6652 b"BITFIELD",
6653 b"bf",
6654 b"OVERFLOW",
6655 b"SAT",
6656 b"INCRBY",
6657 b"i8",
6658 b"0",
6659 b"120",
6660 b"INCRBY",
6661 b"i8",
6662 b"0",
6663 b"120"
6664 ]),
6665 "*2\r\n:119\r\n:127\r\n"
6666 );
6667 assert_eq!(
6668 f.run(&[
6669 b"BITFIELD",
6670 b"bf2",
6671 b"OVERFLOW",
6672 b"FAIL",
6673 b"INCRBY",
6674 b"u2",
6675 b"0",
6676 b"5"
6677 ]),
6678 "*1\r\n$-1\r\n"
6679 );
6680 assert_eq!(
6681 f.run(&[
6682 b"BITFIELD",
6683 b"bf3",
6684 b"OVERFLOW",
6685 b"WRAP",
6686 b"INCRBY",
6687 b"u2",
6688 b"0",
6689 b"5"
6690 ]),
6691 "*1\r\n:1\r\n"
6692 );
6693 assert_eq!(
6694 f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6695 "*1\r\n:4611686018427387904\r\n"
6696 );
6697 }
6698
6699 #[test]
6705 fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6706 let mut f = Fixture::new();
6707 let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6708 assert_eq!(
6709 f.run(&[
6710 b"BITFIELD",
6711 b"bad",
6712 b"SET",
6713 b"u8",
6714 b"0",
6715 b"1",
6716 b"GET",
6717 b"u99",
6718 b"0"
6719 ]),
6720 bad_type
6721 );
6722 assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6723 assert_eq!(
6724 f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6725 bad_type
6726 );
6727 assert_eq!(
6728 f.run(&[b"BITFIELD", b"bad", b"GET"]),
6729 "-ERR syntax error\r\n"
6730 );
6731 assert_eq!(
6732 f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6733 "-ERR syntax error\r\n"
6734 );
6735 assert_eq!(
6736 f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6737 "-ERR syntax error\r\n"
6738 );
6739 assert_eq!(
6740 f.run(&[
6741 b"BITFIELD",
6742 b"bad",
6743 b"OVERFLOW",
6744 b"NOPE",
6745 b"GET",
6746 b"u8",
6747 b"0"
6748 ]),
6749 "-ERR Invalid OVERFLOW type specified\r\n"
6750 );
6751 assert_eq!(
6752 f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6753 "-ERR value is not an integer or out of range\r\n"
6754 );
6755 for at in [&b"#-1"[..], b"abc"] {
6756 assert_eq!(
6757 f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6758 "-ERR bit offset is not an integer or out of range\r\n"
6759 );
6760 }
6761 }
6762
6763 #[test]
6765 fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6766 let mut f = Fixture::new();
6767 f.run(&[b"SET", b"n", b"123"]);
6768 assert_eq!(
6769 f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6770 "*1\r\n:49\r\n"
6771 );
6772 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6774
6775 assert_eq!(
6777 f.run(&[
6778 b"BITFIELD_RO",
6779 b"n",
6780 b"OVERFLOW",
6781 b"SAT",
6782 b"GET",
6783 b"u8",
6784 b"0"
6785 ]),
6786 "*1\r\n:49\r\n"
6787 );
6788 for sub in [&b"SET"[..], b"INCRBY"] {
6789 assert_eq!(
6790 f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6791 "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6792 );
6793 }
6794
6795 assert_eq!(
6796 f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6797 "*1\r\n:0\r\n"
6798 );
6799 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6800 }
6801
6802 #[test]
6804 fn an_offset_off_the_end_of_the_world_is_refused() {
6805 let mut f = Fixture::new();
6806 let bad = "-ERR bit offset is not an integer or out of range\r\n";
6807 for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6808 assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6809 assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6810 }
6811 for arg in [&b"2"[..], b"-1"] {
6812 assert_eq!(
6813 f.run(&[b"BITPOS", b"k", arg]),
6814 "-ERR The bit argument must be 1 or 0.\r\n"
6815 );
6816 }
6817 assert_eq!(
6818 f.run(&[b"BITPOS", b"k", b"abc"]),
6819 "-ERR value is not an integer or out of range\r\n"
6820 );
6821 assert_eq!(
6822 f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
6823 "-ERR value is not an integer or out of range\r\n"
6824 );
6825 let bad_bit = "-ERR bit is not an integer or out of range\r\n";
6826 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
6827 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
6828 }
6829
6830 #[test]
6832 fn every_bitmap_command_says_wrongtype() {
6833 let mut f = Fixture::new();
6834 f.run(&[b"LPUSH", b"l", b"x"]);
6835 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6836 let cases: &[&[&[u8]]] = &[
6837 &[b"SETBIT", b"l", b"0", b"1"],
6838 &[b"GETBIT", b"l", b"0"],
6839 &[b"BITCOUNT", b"l"],
6840 &[b"BITPOS", b"l", b"1"],
6841 &[b"BITOP", b"AND", b"d", b"l"],
6842 &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
6843 &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
6844 ];
6845 for case in cases {
6846 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
6847 }
6848 }
6849
6850 #[test]
6853 fn a_sketch_is_added_to_and_counted() {
6854 let mut f = Fixture::new();
6855 assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
6857 assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
6858 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
6859 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
6860 assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
6863 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
6864
6865 assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
6866 assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
6867 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6868 }
6869
6870 #[test]
6871 fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
6872 let mut f = Fixture::new();
6873 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6874 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";
6876 let mut reply = b"$27\r\n".to_vec();
6877 reply.extend_from_slice(want);
6878 reply.extend_from_slice(b"\r\n");
6879 assert_eq!(f.raw(&[b"GET", b"h"]), reply);
6880 }
6881
6882 #[test]
6883 fn counting_several_keys_counts_their_union() {
6884 let mut f = Fixture::new();
6885 f.run(&[b"PFADD", b"a", b"x", b"y"]);
6886 f.run(&[b"PFADD", b"b", b"y", b"z"]);
6887 assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
6888 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
6889 assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
6892 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
6893 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6894 }
6895
6896 #[test]
6897 fn a_merge_keeps_what_the_destination_had() {
6898 let mut f = Fixture::new();
6899 f.run(&[b"PFADD", b"a", b"x", b"y"]);
6900 f.run(&[b"PFADD", b"b", b"z"]);
6901 assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
6902 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
6903 f.run(&[b"PFADD", b"c", b"w"]);
6905 assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
6906 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
6907 assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
6910 assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
6911 }
6912
6913 #[test]
6914 fn the_debug_forms_answer_four_different_shapes() {
6915 let mut f = Fixture::new();
6916 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6917 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
6918 assert_eq!(
6919 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6920 "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
6921 );
6922 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
6923 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
6924 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
6925 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
6926 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6927 assert_eq!(
6929 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6930 "-ERR HLL encoding is not sparse\r\n"
6931 );
6932
6933 let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
6935 assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
6936 assert_eq!(reply.matches(":0\r\n").count(), 16381);
6937 assert_eq!(reply.matches(":1\r\n").count(), 2);
6938 assert_eq!(reply.matches(":2\r\n").count(), 1);
6939
6940 assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
6941 }
6942
6943 #[test]
6944 fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
6945 let mut f = Fixture::new();
6946 f.run(&[b"SET", b"plain", b"not a sketch"]);
6947 let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
6948 assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
6949 assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
6950 assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
6951 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
6952
6953 f.run(&[b"RPUSH", b"l", b"x"]);
6956 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6957 assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
6958 assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
6959 assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
6960 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6961 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
6962 }
6963
6964 #[test]
6965 fn pfdebug_has_its_own_complaints() {
6966 let mut f = Fixture::new();
6967 f.run(&[b"PFADD", b"h", b"a"]);
6968 assert_eq!(
6971 f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
6972 "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
6973 );
6974 let gone = "-ERR The specified key does not exist\r\n";
6976 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
6977 assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
6978 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
6979 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
6980 assert_eq!(
6981 f.run(&[b"PFDEBUG"]),
6982 "-ERR wrong number of arguments for 'pfdebug' command\r\n"
6983 );
6984 assert_eq!(
6985 f.run(&[b"PFSELFTEST", b"x"]),
6986 "-ERR wrong number of arguments for 'pfselftest' command\r\n"
6987 );
6988 }
6989
6990 #[test]
6991 fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
6992 let mut f = Fixture::new();
6993 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6994 let reply = f.raw(&[b"GET", b"h"]);
6997 let short = reply[5..reply.len() - 3].to_vec();
6998 f.run(&[b"SET", b"h", &short]);
6999 assert_eq!(
7000 f.run(&[b"PFCOUNT", b"h"]),
7001 "-INVALIDOBJ Corrupted HLL object detected\r\n"
7002 );
7003 }
7004
7005 #[test]
7006 fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
7007 let mut f = Fixture::new();
7008 f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
7011 for i in 0..10_000u32 {
7012 let ele = format!("e{i}");
7013 f.run(&[b"PFADD", b"big", ele.as_bytes()]);
7014 }
7015 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
7016 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
7017
7018 for key in [&b"small"[..], b"big"] {
7019 let mut copy = key.to_vec();
7020 copy.push(b'2');
7021 let bytes = payload(&f.raw(&[b"DUMP", key]));
7022 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
7023 assert_eq!(f.raw(&[b"GET", ©]), f.raw(&[b"GET", key]));
7026 assert_eq!(
7027 f.run(&[b"PFDEBUG", b"ENCODING", ©]),
7028 f.run(&[b"PFDEBUG", b"ENCODING", key])
7029 );
7030 assert_eq!(f.run(&[b"PFCOUNT", ©]), f.run(&[b"PFCOUNT", key]));
7031 }
7032 assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
7033 assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
7034 }
7035
7036 fn bulk(s: &str) -> String {
7040 format!("${}\r\n{s}\r\n", s.len())
7041 }
7042
7043 fn bulks(parts: &[&str]) -> String {
7046 let mut s = format!("*{}\r\n", parts.len());
7047 for p in parts {
7048 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
7049 }
7050 s
7051 }
7052
7053 #[test]
7054 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
7055 let mut f = Fixture::new();
7056 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
7060 assert_eq!(
7061 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7062 bulks(&["c", "b", "a"])
7063 );
7064 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
7065 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
7066 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
7067 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
7068 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
7069 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
7070 }
7071
7072 #[test]
7073 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
7074 let mut f = Fixture::new();
7075 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
7076 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
7077 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7078 f.run(&[b"RPUSH", b"k", b"a"]);
7079 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
7080 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
7081 assert_eq!(
7082 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7083 bulks(&["z", "a", "y"])
7084 );
7085 }
7086
7087 #[test]
7090 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
7091 let mut f = Fixture::new();
7092 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
7093 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
7094 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
7095 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
7096 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7097 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
7100 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
7101 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
7103 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7104 }
7105
7106 #[test]
7107 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
7108 let mut f = Fixture::new();
7109 f.run(&[b"RPUSH", b"k", b"a"]);
7110 let range = "-ERR value is out of range, must be positive\r\n";
7111 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
7112 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
7113 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
7114 assert_eq!(
7117 f.run(&[b"LPOP", b"k", b"1", b"2"]),
7118 "-ERR wrong number of arguments for 'lpop' command\r\n"
7119 );
7120 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7121 }
7122
7123 #[test]
7124 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
7125 let mut f = Fixture::new();
7126 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7127 assert_eq!(
7128 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7129 bulks(&["a", "b", "c"])
7130 );
7131 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
7132 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
7133 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
7134 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
7135 assert_eq!(
7136 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
7137 bulks(&["a", "b", "c"])
7138 );
7139 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
7142 assert_eq!(
7143 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
7144 "-ERR value is not an integer or out of range\r\n"
7145 );
7146 }
7147
7148 #[test]
7149 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
7150 let mut f = Fixture::new();
7151 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7152 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
7153 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
7154 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
7155 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
7156 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
7157 assert_eq!(
7158 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7159 bulks(&["a", "b", "z"])
7160 );
7161 assert_eq!(
7164 f.run(&[b"LSET", b"k", b"99", b"z"]),
7165 "-ERR index out of range\r\n"
7166 );
7167 assert_eq!(
7168 f.run(&[b"LSET", b"nope", b"0", b"z"]),
7169 "-ERR no such key\r\n"
7170 );
7171 }
7172
7173 #[test]
7174 fn linsert_says_three_things_with_one_signed_number() {
7175 let mut f = Fixture::new();
7176 assert_eq!(
7179 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
7180 ":0\r\n"
7181 );
7182 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7183 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
7184 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
7185 assert_eq!(
7186 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7187 bulks(&["X", "a", "b", "Y"])
7188 );
7189 assert_eq!(
7190 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
7191 ":-1\r\n"
7192 );
7193 assert_eq!(
7194 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
7195 "-ERR syntax error\r\n"
7196 );
7197 }
7198
7199 #[test]
7200 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
7201 let mut f = Fixture::new();
7202 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
7203 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
7204 assert_eq!(
7205 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7206 bulks(&["b", "c", "a"])
7207 );
7208 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
7209 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7210 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
7211 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
7212 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7213 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
7214 }
7215
7216 #[test]
7217 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
7218 let mut f = Fixture::new();
7219 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
7220 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
7221 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7222 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
7225 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7226 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
7227 }
7228
7229 #[test]
7230 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
7231 let mut f = Fixture::new();
7232 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
7233 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
7234 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
7235 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
7236 assert_eq!(
7237 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
7238 "*2\r\n:0\r\n:3\r\n"
7239 );
7240 assert_eq!(
7241 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
7242 "*3\r\n:6\r\n:3\r\n:0\r\n"
7243 );
7244 assert_eq!(
7247 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
7248 "*1\r\n:0\r\n"
7249 );
7250 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
7253 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
7254 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
7255 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
7256 }
7257
7258 #[test]
7259 fn lpos_words_its_three_mistakes_the_way_redis_does() {
7260 let mut f = Fixture::new();
7261 f.run(&[b"RPUSH", b"p", b"a"]);
7262 assert_eq!(
7265 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
7266 "-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"
7267 );
7268 assert_eq!(
7269 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
7270 "-ERR COUNT can't be negative\r\n"
7271 );
7272 assert_eq!(
7273 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
7274 "-ERR MAXLEN can't be negative\r\n"
7275 );
7276 assert_eq!(
7277 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
7278 "-ERR syntax error\r\n"
7279 );
7280 assert_eq!(
7281 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
7282 "-ERR syntax error\r\n"
7283 );
7284 }
7285
7286 #[test]
7287 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
7288 let mut f = Fixture::new();
7289 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7290 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
7291 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7292 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
7293 assert_eq!(
7294 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
7295 "$1\r\na\r\n"
7296 );
7297 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
7298 f.run(&[b"DEL", b"r"]);
7301 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
7302 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
7303 assert_eq!(
7304 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
7305 bulks(&["3", "1", "2"])
7306 );
7307 assert_eq!(
7308 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7309 "$-1\r\n"
7310 );
7311 assert_eq!(
7312 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7313 "-ERR syntax error\r\n"
7314 );
7315 }
7316
7317 #[test]
7318 fn a_move_checks_the_destination_before_it_takes_anything() {
7319 let mut f = Fixture::new();
7320 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7321 f.run(&[b"SET", b"str", b"v"]);
7322 assert_eq!(
7323 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7324 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7325 );
7326 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7328 }
7329
7330 #[test]
7331 fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7332 for (from, to, order, want) in [
7336 ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7337 ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7338 ("LEFT", "LEFT", "OBO", ["b", "a"]),
7339 ("LEFT", "LEFT", "BULK", ["a", "b"]),
7340 ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7341 ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7342 ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7343 ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7344 ] {
7345 let mut f = Fixture::new();
7346 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7347 let how = format!("{from} {to} {order}");
7348 let reply = f.run(&[
7349 b"LMOVEM",
7350 b"s",
7351 b"d",
7352 from.as_bytes(),
7353 to.as_bytes(),
7354 b"COUNT",
7355 b"2",
7356 order.as_bytes(),
7357 ]);
7358 assert_eq!(reply, bulks(&want), "the reply for {how}");
7359 assert_eq!(
7360 f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7361 bulks(&want),
7362 "the destination for {how}"
7363 );
7364 }
7365 }
7366
7367 #[test]
7368 fn a_block_move_of_one_needs_no_count_at_all() {
7369 let mut f = Fixture::new();
7370 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7371 assert_eq!(
7372 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7373 bulks(&["a"])
7374 );
7375 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7376 assert_eq!(
7379 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7380 "-ERR syntax error\r\n"
7381 );
7382 assert_eq!(
7383 f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7384 "-ERR syntax error\r\n"
7385 );
7386 }
7387
7388 #[test]
7389 fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7390 let mut f = Fixture::new();
7391 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7392 assert_eq!(
7396 f.run(&[
7397 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7398 ]),
7399 "*-1\r\n"
7400 );
7401 assert_eq!(
7402 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7403 bulks(&["a", "b", "c"])
7404 );
7405 assert_eq!(
7407 f.run(&[
7408 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7409 ]),
7410 bulks(&["a", "b", "c"])
7411 );
7412 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7413 assert_eq!(
7414 f.run(&[
7415 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7416 ]),
7417 "*-1\r\n"
7418 );
7419 }
7420
7421 #[test]
7422 fn a_block_move_onto_itself_rotates_by_the_count() {
7423 let mut f = Fixture::new();
7424 f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7425 assert_eq!(
7426 f.run(&[
7427 b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7428 ]),
7429 bulks(&["a", "b"])
7430 );
7431 assert_eq!(
7432 f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7433 bulks(&["c", "a", "b"])
7434 );
7435 }
7436
7437 #[test]
7438 fn a_block_move_reads_the_count_before_the_ordering_word() {
7439 let mut f = Fixture::new();
7440 f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7441 f.run(&[b"SET", b"str", b"v"]);
7442 let count = "-ERR count should be greater than 0\r\n";
7443 assert_eq!(
7444 f.run(&[
7445 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7446 ]),
7447 count
7448 );
7449 assert_eq!(
7450 f.run(&[
7451 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7452 ]),
7453 count
7454 );
7455 assert_eq!(
7456 f.run(&[
7457 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7458 ]),
7459 "-ERR syntax error\r\n"
7460 );
7461 assert_eq!(
7462 f.run(&[
7463 b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7464 ]),
7465 "-ERR syntax error\r\n"
7466 );
7467 assert_eq!(
7470 f.run(&[
7471 b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7472 ]),
7473 count
7474 );
7475 assert_eq!(
7476 f.run(&[
7477 b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7478 ]),
7479 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7480 );
7481 assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7482 }
7483
7484 #[test]
7485 fn lmpop_answers_from_the_first_key_that_has_anything() {
7486 let mut f = Fixture::new();
7487 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7488 assert_eq!(
7491 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7492 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7493 );
7494 assert_eq!(
7495 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7496 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7497 );
7498 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7499 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7502 }
7503
7504 #[test]
7505 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7506 let mut f = Fixture::new();
7507 f.run(&[b"RPUSH", b"k", b"a"]);
7508 assert_eq!(
7509 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7510 "-ERR numkeys should be greater than 0\r\n"
7511 );
7512 assert_eq!(
7513 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7514 "-ERR numkeys should be greater than 0\r\n"
7515 );
7516 assert_eq!(
7517 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7518 "-ERR count should be greater than 0\r\n"
7519 );
7520 assert_eq!(
7523 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7524 "-ERR syntax error\r\n"
7525 );
7526 assert_eq!(
7527 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7528 "-ERR syntax error\r\n"
7529 );
7530 assert_eq!(
7531 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7532 "-ERR syntax error\r\n"
7533 );
7534 assert_eq!(
7535 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7536 "-ERR syntax error\r\n"
7537 );
7538 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7539 }
7540
7541 #[test]
7542 fn every_list_command_says_wrongtype_and_writes_nothing() {
7543 let mut f = Fixture::new();
7544 f.run(&[b"SET", b"str", b"v"]);
7545 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7546 for cmd in [
7547 &[b"LPUSH".as_slice(), b"str", b"a"][..],
7548 &[b"RPUSH", b"str", b"a"],
7549 &[b"LPUSHX", b"str", b"a"],
7550 &[b"RPUSHX", b"str", b"a"],
7551 &[b"LPOP", b"str"],
7552 &[b"LPOP", b"str", b"2"],
7553 &[b"RPOP", b"str"],
7554 &[b"LLEN", b"str"],
7555 &[b"LRANGE", b"str", b"0", b"-1"],
7556 &[b"LINDEX", b"str", b"0"],
7557 &[b"LSET", b"str", b"0", b"a"],
7558 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7559 &[b"LREM", b"str", b"0", b"a"],
7560 &[b"LTRIM", b"str", b"0", b"-1"],
7561 &[b"LPOS", b"str", b"a"],
7562 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7563 &[b"RPOPLPUSH", b"str", b"d"],
7564 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7565 &[b"LMPOP", b"1", b"str", b"LEFT"],
7566 ] {
7567 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7568 }
7569 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7570 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7571 }
7572
7573 #[test]
7577 fn a_timeout_has_three_ways_of_being_wrong() {
7578 let mut f = Fixture::new();
7579 let not_float = "-ERR timeout is not a float or out of range\r\n";
7580 let range = "-ERR timeout is out of range\r\n";
7581 for (bad, want) in [
7582 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7583 (&[b"BLPOP", b"k", b"nan"], not_float),
7584 (&[b"BLPOP", b"k", b""], not_float),
7585 (&[b"BLPOP", b"k", b" 1"], not_float),
7588 (&[b"BLPOP", b"k", b"1 "], not_float),
7589 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7590 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7591 (&[b"BLPOP", b"k", b"1e400"], range),
7594 (&[b"BLPOP", b"k", b"inf"], range),
7595 (&[b"BLPOP", b"k", b"9999999999999999"], range),
7596 (&[b"BRPOP", b"k", b"abc"], not_float),
7597 (
7598 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7599 not_float,
7600 ),
7601 (
7602 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7603 "-ERR timeout is negative\r\n",
7604 ),
7605 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7606 ] {
7607 assert_eq!(f.run(bad), want, "for {bad:?}");
7608 }
7609 }
7610
7611 #[test]
7614 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7615 let mut f = Fixture::new();
7616 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7617 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7618 assert_eq!(flow, Flow::Block, "for {timeout:?}");
7619 assert!(out.is_empty(), "for {timeout:?}");
7620 }
7621 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7625 assert_eq!(flow, Flow::Block);
7626 assert!(out.is_empty());
7627 }
7628
7629 #[test]
7630 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7631 let mut f = Fixture::new();
7632 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7633
7634 assert_eq!(
7637 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7638 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7639 );
7640 assert_eq!(
7641 f.run(&[b"BRPOP", b"L", b"0"]),
7642 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7643 );
7644 assert_eq!(
7645 f.run(&[
7646 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7647 ]),
7648 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7649 );
7650 assert_eq!(
7651 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7652 "$1\r\nd\r\n"
7653 );
7654 assert_eq!(
7655 f.run(&[b"EXISTS", b"L"]),
7656 ":0\r\n",
7657 "and the key went with it"
7658 );
7659 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7660 f.run(&[b"RPUSH", b"D", b"x"]);
7663 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7664 assert_eq!(
7665 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7666 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7667 );
7668 }
7669
7670 #[test]
7671 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7672 let mut f = Fixture::new();
7673 f.run(&[b"RPUSH", b"k", b"a"]);
7674 for (bad, want) in [
7675 (
7676 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7677 "-ERR numkeys should be greater than 0\r\n",
7678 ),
7679 (
7680 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7681 "-ERR numkeys should be greater than 0\r\n",
7682 ),
7683 (
7686 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7687 "-ERR syntax error\r\n",
7688 ),
7689 (
7690 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7691 "-ERR syntax error\r\n",
7692 ),
7693 (
7694 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7695 "-ERR syntax error\r\n",
7696 ),
7697 (
7698 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7699 "-ERR syntax error\r\n",
7700 ),
7701 (
7704 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7705 "-ERR count should be greater than 0\r\n",
7706 ),
7707 (
7708 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7709 "-ERR count should be greater than 0\r\n",
7710 ),
7711 ] {
7712 assert_eq!(f.run(bad), want, "for {bad:?}");
7713 }
7714 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7715 }
7716
7717 #[test]
7718 fn a_blocking_move_reads_its_directions_before_its_timeout() {
7719 let mut f = Fixture::new();
7720 assert_eq!(
7723 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7724 "-ERR syntax error\r\n"
7725 );
7726 assert_eq!(
7727 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7728 "-ERR syntax error\r\n"
7729 );
7730 }
7731
7732 #[test]
7736 fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7737 let mut f = Fixture::new();
7738 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7739 assert_eq!(
7740 f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7741 (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7742 );
7743 assert_eq!(
7744 f.run(&[
7745 b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7746 ]),
7747 bulks(&["e", "d"])
7748 );
7749 assert_eq!(
7750 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7751 bulks(&["a", "e", "d"])
7752 );
7753 assert_eq!(
7755 f.run(&[
7756 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7757 ]),
7758 bulks(&["b", "c"])
7759 );
7760 assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7761 }
7762
7763 #[test]
7767 fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7768 let mut f = Fixture::new();
7769 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7770 assert_eq!(
7772 f.flow(&[
7773 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7774 ]),
7775 (Flow::Continue, bulks(&["a", "b"]))
7776 );
7777
7778 f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7779 assert_eq!(
7782 f.flow(&[
7783 b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7784 ])
7785 .0,
7786 Flow::Block
7787 );
7788 assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7789 }
7790
7791 #[test]
7792 fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7793 let mut f = Fixture::new();
7794 let syntax = "-ERR syntax error\r\n";
7795 assert_eq!(
7797 f.run(&[
7798 b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7799 ]),
7800 syntax
7801 );
7802 assert_eq!(
7804 f.run(&[
7805 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7806 ]),
7807 "-ERR timeout is not a float or out of range\r\n"
7808 );
7809 assert_eq!(
7810 f.run(&[
7811 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7812 ]),
7813 "-ERR timeout is negative\r\n"
7814 );
7815 assert_eq!(
7817 f.run(&[
7818 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
7819 ]),
7820 "-ERR count should be greater than 0\r\n"
7821 );
7822 assert_eq!(
7823 f.run(&[
7824 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
7825 ]),
7826 syntax
7827 );
7828 assert_eq!(
7831 f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
7832 syntax
7833 );
7834 assert_eq!(
7835 f.run(&[
7836 b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
7837 ]),
7838 syntax
7839 );
7840 }
7841
7842 #[test]
7845 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
7846 let mut f = Fixture::new();
7847 f.run(&[b"SET", b"S", b"v"]);
7848 f.run(&[b"RPUSH", b"D", b"x"]);
7849 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7850
7851 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
7852 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
7855 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
7856 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
7857 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
7858 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
7861 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
7862 assert_eq!(
7863 f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
7864 wrong
7865 );
7866 assert_eq!(
7867 f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
7868 wrong
7869 );
7870
7871 assert_eq!(
7875 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7876 .0,
7877 Flow::Block
7878 );
7879 assert_eq!(
7883 f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7884 .0,
7885 Flow::Block
7886 );
7887 f.run(&[b"RPUSH", b"E", b"1", b"2"]);
7888 assert_eq!(
7889 f.flow(&[
7890 b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
7891 ])
7892 .0,
7893 Flow::Block
7894 );
7895 }
7896
7897 #[test]
7901 fn churning_lists_does_not_grow_the_server() {
7902 let mut f = Fixture::new();
7903 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
7904 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
7905 .into_iter()
7906 .chain(vals.iter().map(Vec::as_slice))
7907 .collect();
7908
7909 f.run(&args);
7910 f.run(&[b"DEL", b"k"]);
7911 f.server.compact_step();
7912 let after_first = f.server.memory_bytes();
7913
7914 for _ in 0..200 {
7915 f.run(&args);
7916 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
7917 f.server.compact_step();
7918 }
7919 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
7920 assert!(
7921 f.server.memory_bytes() <= after_first * 2,
7922 "held {} after two hundred passes against {after_first} after one",
7923 f.server.memory_bytes()
7924 );
7925 }
7926
7927 #[test]
7930 fn a_sorted_set_takes_scores_and_gives_them_back() {
7931 let mut f = Fixture::new();
7932 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
7933 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
7934 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
7935 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
7936 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
7937 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
7938 assert_eq!(
7939 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
7940 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
7941 );
7942 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
7943 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
7944 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
7946 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7947 }
7948
7949 #[test]
7950 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
7951 let mut f = Fixture::new();
7952 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
7953 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
7954 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
7955 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
7956
7957 f.out = Out::new(Proto::Resp3);
7958 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
7959 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
7960 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
7961 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
7962 }
7963
7964 #[test]
7965 fn the_zadd_options_gate_what_gets_written() {
7966 let mut f = Fixture::new();
7967 f.run(&[b"ZADD", b"z", b"5", b"a"]);
7968 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
7970 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
7971 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
7972 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
7973 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
7975 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
7976 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
7977 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
7979 assert_eq!(
7980 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
7981 ":2\r\n"
7982 );
7983 }
7984
7985 #[test]
7986 fn zadd_incr_answers_a_score_or_nothing_at_all() {
7987 let mut f = Fixture::new();
7988 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
7989 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
7990 assert_eq!(
7993 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
7994 "$-1\r\n"
7995 );
7996 assert_eq!(
7997 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
7998 "$-1\r\n"
7999 );
8000 assert_eq!(
8001 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
8002 "$-1\r\n"
8003 );
8004 assert_eq!(
8005 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
8006 "$1\r\n8\r\n"
8007 );
8008 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
8009 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
8010 }
8011
8012 #[test]
8013 fn the_two_infinities_will_not_be_added_together() {
8014 let mut f = Fixture::new();
8015 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
8016 let nan = "-ERR resulting score is not a number (NaN)\r\n";
8017 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
8018 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
8019 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
8020 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
8022 }
8023
8024 #[test]
8025 fn zadd_says_its_mistakes_the_way_redis_says_them() {
8026 let mut f = Fixture::new();
8027 assert_eq!(
8030 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
8031 "-ERR syntax error\r\n"
8032 );
8033 assert_eq!(
8034 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
8035 "-ERR XX and NX options at the same time are not compatible\r\n"
8036 );
8037 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
8038 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
8039 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
8040 assert_eq!(
8041 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
8042 "-ERR INCR option supports a single increment-element pair\r\n"
8043 );
8044 assert_eq!(
8046 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
8047 "-ERR syntax error\r\n"
8048 );
8049 assert_eq!(
8051 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
8052 "-ERR value is not a valid float\r\n"
8053 );
8054 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8055 }
8056
8057 #[test]
8058 fn a_rank_says_where_a_member_sits_from_either_end() {
8059 let mut f = Fixture::new();
8060 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8061 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
8062 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
8063 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
8064 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
8065 assert_eq!(
8067 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
8068 "*2\r\n:1\r\n$1\r\n2\r\n"
8069 );
8070 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
8071 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
8072 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
8073 assert_eq!(
8076 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
8077 "-ERR syntax error\r\n"
8078 );
8079 assert_eq!(
8080 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
8081 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
8082 );
8083 }
8084
8085 #[test]
8086 fn the_two_counts_read_their_two_kinds_of_bound() {
8087 let mut f = Fixture::new();
8088 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8089 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
8090 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
8091 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
8092 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
8093 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
8094 assert_eq!(
8095 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
8096 "-ERR min or max is not a float\r\n"
8097 );
8098
8099 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
8100 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
8101 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
8102 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
8103 assert_eq!(
8106 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
8107 "-ERR min or max not valid string range item\r\n"
8108 );
8109 }
8110
8111 #[test]
8117 fn one_range_command_selects_by_rank_or_score_or_name() {
8118 let mut f = Fixture::new();
8119 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8120 assert_eq!(
8121 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8122 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8123 );
8124 assert_eq!(
8125 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
8126 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8127 );
8128 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
8129 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
8130 assert_eq!(
8133 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
8134 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8135 );
8136 assert_eq!(
8137 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
8138 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8139 );
8140 assert_eq!(
8143 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
8144 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8145 );
8146 assert_eq!(
8147 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
8148 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8149 );
8150 assert_eq!(
8151 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
8152 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8153 );
8154 }
8155
8156 #[test]
8159 fn the_older_range_spellings_name_their_high_end_first() {
8160 let mut f = Fixture::new();
8161 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8162 assert_eq!(
8163 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
8164 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8165 );
8166 assert_eq!(
8167 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
8168 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8169 );
8170 assert_eq!(
8171 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
8172 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8173 );
8174 assert_eq!(
8175 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
8176 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8177 );
8178 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
8182 assert_eq!(
8183 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
8184 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
8185 );
8186 assert_eq!(
8187 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
8188 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
8189 );
8190 for cmd in [
8193 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
8194 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
8195 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
8196 ] {
8197 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
8198 }
8199 }
8200
8201 #[test]
8204 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
8205 let mut f = Fixture::new();
8206 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8207 assert_eq!(
8208 f.run(&[
8209 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
8210 ]),
8211 "*1\r\n$1\r\nb\r\n"
8212 );
8213 assert_eq!(
8215 f.run(&[
8216 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
8217 ]),
8218 "*0\r\n"
8219 );
8220 assert_eq!(
8221 f.run(&[
8222 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
8223 ]),
8224 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8225 );
8226 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";
8228 assert_eq!(
8229 f.run(&[
8230 b"ZRANGEBYSCORE",
8231 b"z",
8232 b"1",
8233 b"3",
8234 b"WITHSCORES",
8235 b"LIMIT",
8236 b"0",
8237 b"2"
8238 ]),
8239 both
8240 );
8241 assert_eq!(
8242 f.run(&[
8243 b"ZRANGEBYSCORE",
8244 b"z",
8245 b"1",
8246 b"3",
8247 b"LIMIT",
8248 b"0",
8249 b"2",
8250 b"WITHSCORES"
8251 ]),
8252 both
8253 );
8254 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
8257 assert_eq!(
8258 f.run(&[
8259 b"ZREVRANGE",
8260 b"z",
8261 b"0",
8262 b"-1",
8263 b"WITHSCORES",
8264 b"LIMIT",
8265 b"0",
8266 b"1"
8267 ]),
8268 needs_by
8269 );
8270 assert_eq!(
8271 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
8272 needs_by
8273 );
8274 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
8275 assert_eq!(
8276 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
8277 not_bylex
8278 );
8279 assert_eq!(
8280 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
8281 not_bylex
8282 );
8283 for cmd in [
8286 &[
8287 b"ZRANGE".as_slice(),
8288 b"z",
8289 b"0",
8290 b"-1",
8291 b"BYSCORE",
8292 b"BYLEX",
8293 ][..],
8294 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
8295 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
8296 ] {
8297 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8298 }
8299 assert_eq!(
8300 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
8301 "-ERR min or max is not a float\r\n"
8302 );
8303 assert_eq!(
8304 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
8305 "-ERR min or max not valid string range item\r\n"
8306 );
8307 assert_eq!(
8308 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8309 "-ERR value is not an integer or out of range\r\n"
8310 );
8311 }
8312
8313 #[test]
8316 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8317 let mut f = Fixture::new();
8318 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8319 assert_eq!(
8320 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8321 "*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"
8322 );
8323 f.out = Out::new(Proto::Resp3);
8324 assert_eq!(
8325 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8326 "*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"
8327 );
8328 assert_eq!(
8329 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8330 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8331 );
8332 }
8333
8334 #[test]
8336 fn a_range_store_writes_the_window_into_another_key() {
8337 let mut f = Fixture::new();
8338 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8339 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8340 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8343 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8344 assert_eq!(
8345 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8346 ":2\r\n"
8347 );
8348 assert_eq!(
8349 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8350 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8351 );
8352 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8355 assert_eq!(
8356 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8357 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8358 );
8359 assert_eq!(
8362 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8363 "-ERR syntax error\r\n"
8364 );
8365 }
8366
8367 #[test]
8370 fn the_three_removals_share_their_window_with_the_reads() {
8371 let mut f = Fixture::new();
8372 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8373 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8374 assert_eq!(
8375 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8376 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8377 );
8378 assert_eq!(
8379 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8380 ":1\r\n"
8381 );
8382 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8383 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8385 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8386 assert_eq!(
8387 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8388 ":0\r\n"
8389 );
8390 assert_eq!(
8391 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8392 "-ERR value is not an integer or out of range\r\n"
8393 );
8394 }
8395
8396 #[test]
8398 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8399 let mut f = Fixture::new();
8400 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8401 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8402 assert_eq!(
8403 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8404 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8405 );
8406 assert_eq!(
8409 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8410 "*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"
8411 );
8412 assert_eq!(
8413 f.run(&[
8414 b"ZUNION",
8415 b"2",
8416 b"z",
8417 b"y",
8418 b"WEIGHTS",
8419 b"2",
8420 b"3",
8421 b"WITHSCORES"
8422 ]),
8423 "*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"
8424 );
8425 assert_eq!(
8426 f.run(&[
8427 b"ZUNION",
8428 b"2",
8429 b"z",
8430 b"y",
8431 b"AGGREGATE",
8432 b"MIN",
8433 b"WITHSCORES"
8434 ]),
8435 "*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"
8436 );
8437 assert_eq!(
8438 f.run(&[
8439 b"ZUNION",
8440 b"2",
8441 b"z",
8442 b"y",
8443 b"AGGREGATE",
8444 b"MAX",
8445 b"WITHSCORES"
8446 ]),
8447 "*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"
8448 );
8449 assert_eq!(
8450 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8451 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8452 );
8453 assert_eq!(
8454 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8455 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8456 );
8457 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8458 f.run(&[b"SADD", b"p", b"a", b"d"]);
8461 assert_eq!(
8462 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8463 "*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"
8464 );
8465 for cmd in [
8468 &[
8469 b"ZDIFF".as_slice(),
8470 b"2",
8471 b"z",
8472 b"y",
8473 b"WEIGHTS",
8474 b"1",
8475 b"1",
8476 ][..],
8477 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8478 ] {
8479 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8480 }
8481 }
8482
8483 #[test]
8485 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8486 let mut f = Fixture::new();
8487 f.run(&[b"ZADD", b"z", b"1", b"a"]);
8488 f.run(&[b"ZADD", b"y", b"2", b"b"]);
8489 assert_eq!(
8491 f.run(&[b"ZUNION", b"0", b"z"]),
8492 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8493 );
8494 assert_eq!(
8495 f.run(&[b"ZUNION", b"-1", b"z"]),
8496 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8497 );
8498 assert_eq!(
8499 f.run(&[b"ZINTERCARD", b"0", b"z"]),
8500 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8501 );
8502 assert_eq!(
8505 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8506 "-ERR syntax error\r\n"
8507 );
8508 assert_eq!(
8509 f.run(&[b"ZUNION", b"x", b"z"]),
8510 "-ERR value is not an integer or out of range\r\n"
8511 );
8512 assert_eq!(
8515 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8516 "-ERR syntax error\r\n"
8517 );
8518 assert_eq!(
8519 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8520 "-ERR weight value is not a float\r\n"
8521 );
8522 assert_eq!(
8523 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8524 "-ERR syntax error\r\n"
8525 );
8526 }
8527
8528 #[test]
8530 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8531 let mut f = Fixture::new();
8532 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8533 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8534 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8535 assert_eq!(
8536 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8537 "*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"
8538 );
8539 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8540 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8541 assert_eq!(
8544 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8545 ":0\r\n"
8546 );
8547 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8548 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8550 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8551 for cmd in [
8552 &[
8553 b"ZUNIONSTORE".as_slice(),
8554 b"d",
8555 b"2",
8556 b"z",
8557 b"y",
8558 b"WITHSCORES",
8559 ][..],
8560 &[
8561 b"ZDIFFSTORE",
8562 b"d",
8563 b"2",
8564 b"z",
8565 b"y",
8566 b"WEIGHTS",
8567 b"1",
8568 b"1",
8569 ],
8570 ] {
8571 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8572 }
8573 }
8574
8575 #[test]
8577 fn intercard_counts_and_stops_at_its_limit() {
8578 let mut f = Fixture::new();
8579 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8580 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8581 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8582 assert_eq!(
8584 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8585 ":2\r\n"
8586 );
8587 assert_eq!(
8588 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8589 ":1\r\n"
8590 );
8591 let bad = "-ERR LIMIT can't be negative\r\n";
8594 assert_eq!(
8595 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8596 bad
8597 );
8598 assert_eq!(
8599 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8600 bad
8601 );
8602 for cmd in [
8603 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8604 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8605 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8606 ] {
8607 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8608 }
8609 }
8610
8611 #[test]
8613 fn a_draw_answers_one_member_or_an_array_of_them() {
8614 let mut f = Fixture::new();
8615 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8616 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8619 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8620 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8621 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8622 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8625 assert!(all.starts_with("*3\r\n"), "{all}");
8626 for m in ["a", "b", "c"] {
8627 assert!(all.contains(m), "{all}");
8628 }
8629 assert!(
8632 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8633 "five draws with replacement"
8634 );
8635 assert!(
8636 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8637 .starts_with("*4\r\n"),
8638 "two pairs, flat on RESP2"
8639 );
8640 f.out = Out::new(Proto::Resp3);
8641 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8642 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8643 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8644 f.out = Out::new(Proto::Resp2);
8645 assert_eq!(
8646 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8647 "-ERR syntax error\r\n"
8648 );
8649 assert_eq!(
8650 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8651 "-ERR value is not an integer or out of range\r\n"
8652 );
8653 }
8654
8655 #[test]
8657 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8658 let mut f = Fixture::new();
8659 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8660 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";
8661 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8662 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8663 assert_eq!(
8664 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8665 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8666 );
8667 assert_eq!(
8668 f.run(&[b"ZSCAN", b"nokey", b"0"]),
8669 "*2\r\n$1\r\n0\r\n*0\r\n"
8670 );
8671 f.out = Out::new(Proto::Resp3);
8674 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8675 f.out = Out::new(Proto::Resp2);
8676 assert_eq!(
8677 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8678 "-ERR NOVALUES option can only be used in HSCAN\r\n"
8679 );
8680 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8681 assert_eq!(
8682 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8683 "-ERR syntax error\r\n"
8684 );
8685 }
8686
8687 #[test]
8689 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8690 let mut f = Fixture::new();
8691 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8692 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8694 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8695 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8696 assert_eq!(
8698 f.run(&[b"ZPOPMIN", b"z", b"2"]),
8699 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8700 );
8701 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8704 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8705 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8706 assert_eq!(
8708 f.run(&[b"ZPOPMIN", b"z", b"9"]),
8709 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8710 );
8711 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8712
8713 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8714 f.out = Out::new(Proto::Resp3);
8715 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8716 assert_eq!(
8717 f.run(&[b"ZPOPMIN", b"z", b"1"]),
8718 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8719 );
8720 f.out = Out::new(Proto::Resp2);
8721 let bad = "-ERR value is out of range, must be positive\r\n";
8724 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8725 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8726 assert_eq!(
8727 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8728 "-ERR syntax error\r\n"
8729 );
8730 }
8731
8732 #[test]
8734 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8735 let mut f = Fixture::new();
8736 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8737 assert_eq!(
8738 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8739 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8740 );
8741 assert_eq!(
8744 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8745 "*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"
8746 );
8747 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8749 f.out = Out::new(Proto::Resp3);
8750 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8751 f.out = Out::new(Proto::Resp2);
8752 let numkeys = "-ERR numkeys should be greater than 0\r\n";
8753 for bad in [
8754 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8755 &[b"ZMPOP", b"-1", b"z", b"MIN"],
8756 &[b"ZMPOP", b"x", b"z", b"MIN"],
8757 ] {
8758 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8759 }
8760 let count = "-ERR count should be greater than 0\r\n";
8761 for bad in [
8762 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8763 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8764 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8765 ] {
8766 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8767 }
8768 let syntax = "-ERR syntax error\r\n";
8769 for bad in [
8770 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8773 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8774 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8775 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8776 ] {
8777 assert_eq!(f.run(bad), syntax, "{bad:?}");
8778 }
8779 }
8780
8781 #[test]
8784 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8785 let mut f = Fixture::new();
8786 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8787 assert_eq!(
8788 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8789 (
8790 Flow::Continue,
8791 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8792 )
8793 );
8794 assert_eq!(
8795 f.run(&[b"BZPOPMAX", b"z", b"0"]),
8796 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8797 );
8798 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8799 assert_eq!(
8800 f.run(&[
8801 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8802 ]),
8803 "*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"
8804 );
8805 f.out = Out::new(Proto::Resp3);
8806 assert_eq!(
8807 f.run(&[b"BZPOPMIN", b"z", b"0"]),
8808 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8809 );
8810 f.out = Out::new(Proto::Resp2);
8811 assert_eq!(
8813 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8814 (Flow::Block, String::new())
8815 );
8816 assert_eq!(
8817 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
8818 (Flow::Block, String::new())
8819 );
8820 assert_eq!(
8823 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
8824 "-ERR timeout is not a float or out of range\r\n"
8825 );
8826 assert_eq!(
8827 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
8828 "-ERR numkeys should be greater than 0\r\n"
8829 );
8830 assert_eq!(
8831 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
8832 "-ERR timeout is negative\r\n"
8833 );
8834 }
8835
8836 #[test]
8840 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
8841 let mut f = Fixture::new();
8842 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
8843 assert_eq!(f.server.waiters().len(), 1);
8844 f.run(&[b"SET", b"z", b"v"]);
8847 let mut out = Out::new(Proto::Resp2);
8848 assert!(!f.server.serve_waiter(0, 0, &mut out));
8849 assert!(out.as_slice().is_empty());
8850 f.run(&[b"DEL", b"z"]);
8851 f.run(&[b"ZADD", b"z", b"5", b"m"]);
8852 assert!(f.server.serve_waiter(0, 0, &mut out));
8853 assert_eq!(
8854 core::str::from_utf8(out.as_slice()).expect("ascii"),
8855 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
8856 );
8857 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8860 }
8861
8862 #[test]
8863 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
8864 let mut f = Fixture::new();
8865 f.run(&[b"SET", b"s", b"v"]);
8866 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8867 for cmd in [
8868 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
8869 &[b"ZINCRBY", b"s", b"1", b"a"],
8870 &[b"ZCARD", b"s"],
8871 &[b"ZSCORE", b"s", b"a"],
8872 &[b"ZMSCORE", b"s", b"a"],
8873 &[b"ZREM", b"s", b"a"],
8874 &[b"ZRANK", b"s", b"a"],
8875 &[b"ZREVRANK", b"s", b"a"],
8876 &[b"ZCOUNT", b"s", b"1", b"2"],
8877 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
8878 &[b"ZRANGE", b"s", b"0", b"-1"],
8879 &[b"ZREVRANGE", b"s", b"0", b"-1"],
8880 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
8881 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
8882 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
8883 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
8884 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
8885 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
8886 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
8887 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
8888 &[b"ZUNION", b"1", b"s"],
8889 &[b"ZINTER", b"1", b"s"],
8890 &[b"ZDIFF", b"1", b"s"],
8891 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
8892 &[b"ZINTERSTORE", b"d", b"1", b"s"],
8893 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
8894 &[b"ZINTERCARD", b"1", b"s"],
8895 &[b"ZRANDMEMBER", b"s"],
8896 &[b"ZSCAN", b"s", b"0"],
8897 &[b"ZPOPMIN", b"s"],
8898 &[b"ZPOPMAX", b"s", b"2"],
8899 &[b"ZMPOP", b"1", b"s", b"MIN"],
8900 &[b"BZPOPMIN", b"s", b"0"],
8901 &[b"BZPOPMAX", b"s", b"0"],
8902 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
8903 ] {
8904 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
8905 }
8906 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
8907 }
8908
8909 #[test]
8913 fn churning_sorted_sets_does_not_grow_the_server() {
8914 let mut f = Fixture::new();
8915 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
8916 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
8917 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
8918 for i in 0..200 {
8919 args.push(&scores[i]);
8920 args.push(&members[i]);
8921 }
8922
8923 f.run(&args);
8924 f.run(&[b"DEL", b"z"]);
8925 f.server.compact_step();
8926 let after_first = f.server.memory_bytes();
8927
8928 for _ in 0..200 {
8929 f.run(&args);
8930 f.run(&[b"DEL", b"z"]);
8931 f.server.compact_step();
8932 }
8933 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8934 assert!(
8935 f.server.memory_bytes() <= after_first * 2,
8936 "held {} after two hundred passes against {after_first} after one",
8937 f.server.memory_bytes()
8938 );
8939 }
8940
8941 fn sicily(f: &mut Fixture) {
8949 f.run(&[
8950 b"GEOADD",
8951 b"Sicily",
8952 b"13.361389",
8953 b"38.115556",
8954 b"Palermo",
8955 b"15.087269",
8956 b"37.502669",
8957 b"Catania",
8958 ]);
8959 f.run(&[
8960 b"GEOADD",
8961 b"Sicily",
8962 b"13.583333",
8963 b"37.316667",
8964 b"Agrigento",
8965 ]);
8966 }
8967
8968 #[test]
8969 fn places_go_in_as_scores_and_come_back_as_positions() {
8970 let mut f = Fixture::new();
8971 assert_eq!(
8972 f.run(&[
8973 b"GEOADD",
8974 b"Sicily",
8975 b"13.361389",
8976 b"38.115556",
8977 b"Palermo",
8978 b"15.087269",
8979 b"37.502669",
8980 b"Catania"
8981 ]),
8982 ":2\r\n"
8983 );
8984 assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
8988 assert_eq!(
8989 f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
8990 "$16\r\n3479099956230698\r\n"
8991 );
8992 assert_eq!(
8993 f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
8994 "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
8995 );
8996 assert_eq!(
8997 f.run(&[
8998 b"GEOHASH",
8999 b"Sicily",
9000 b"Palermo",
9001 b"Catania",
9002 b"NonExisting"
9003 ]),
9004 "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
9005 );
9006 assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
9010 assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
9011 }
9012
9013 #[test]
9014 fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
9015 let mut f = Fixture::new();
9016 sicily(&mut f);
9017 assert_eq!(
9018 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
9019 "$11\r\n166274.1516\r\n"
9020 );
9021 assert_eq!(
9022 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
9023 "$8\r\n166.2742\r\n"
9024 );
9025 assert_eq!(
9026 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
9027 "$8\r\n103.3182\r\n"
9028 );
9029 assert_eq!(
9033 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
9034 "$-1\r\n"
9035 );
9036 assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
9037 assert_eq!(
9038 f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
9039 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
9040 );
9041 assert_eq!(
9042 f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
9043 "-ERR syntax error\r\n"
9044 );
9045 }
9046
9047 #[test]
9048 fn a_search_finds_what_is_inside_it_nearest_first() {
9049 let mut f = Fixture::new();
9050 sicily(&mut f);
9051 let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
9052 assert_eq!(
9053 f.run(&[
9054 b"GEOSEARCH",
9055 b"Sicily",
9056 b"FROMLONLAT",
9057 b"15",
9058 b"37",
9059 b"BYRADIUS",
9060 b"200",
9061 b"km",
9062 b"ASC"
9063 ]),
9064 all
9065 );
9066 assert_eq!(
9069 f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
9070 all
9071 );
9072 assert_eq!(
9073 f.run(&[
9074 b"GEORADIUS_RO",
9075 b"Sicily",
9076 b"15",
9077 b"37",
9078 b"200",
9079 b"km",
9080 b"ASC"
9081 ]),
9082 all
9083 );
9084 assert_eq!(
9087 f.run(&[
9088 b"GEORADIUS",
9089 b"Sicily",
9090 b"15",
9091 b"37",
9092 b"200",
9093 b"km",
9094 b"DESC",
9095 b"COUNT",
9096 b"1"
9097 ]),
9098 "*1\r\n$7\r\nPalermo\r\n"
9099 );
9100 assert_eq!(
9101 f.run(&[
9102 b"GEORADIUS",
9103 b"Sicily",
9104 b"15",
9105 b"37",
9106 b"200",
9107 b"km",
9108 b"COUNT",
9109 b"1"
9110 ]),
9111 "*1\r\n$7\r\nCatania\r\n"
9112 );
9113 let empty = "*0\r\n";
9116 assert_eq!(
9117 f.run(&[
9118 b"GEOSEARCH",
9119 b"Sicily",
9120 b"FROMLONLAT",
9121 b"15",
9122 b"37",
9123 b"BYRADIUS",
9124 b"1",
9125 b"km"
9126 ]),
9127 empty
9128 );
9129 assert_eq!(
9130 f.run(&[
9131 b"GEOSEARCH",
9132 b"nokey",
9133 b"FROMLONLAT",
9134 b"15",
9135 b"37",
9136 b"BYRADIUS",
9137 b"1",
9138 b"km"
9139 ]),
9140 empty
9141 );
9142 assert_eq!(
9143 f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
9144 empty
9145 );
9146 }
9147
9148 #[test]
9149 fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
9150 let mut f = Fixture::new();
9151 sicily(&mut f);
9152 assert_eq!(
9153 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
9154 "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9155 );
9156 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";
9159 assert_eq!(
9160 f.run(&[
9161 b"GEORADIUSBYMEMBER_RO",
9162 b"Sicily",
9163 b"Agrigento",
9164 b"100",
9165 b"km",
9166 b"WITHDIST"
9167 ]),
9168 with_dist
9169 );
9170 assert_eq!(
9171 f.run(&[
9172 b"GEOSEARCH",
9173 b"Sicily",
9174 b"FROMMEMBER",
9175 b"Agrigento",
9176 b"BYRADIUS",
9177 b"100",
9178 b"km",
9179 b"ASC",
9180 b"WITHDIST"
9181 ]),
9182 with_dist
9183 );
9184 assert_eq!(
9185 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
9186 "-ERR could not decode requested zset member\r\n"
9187 );
9188 }
9189
9190 #[test]
9191 fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
9192 let mut f = Fixture::new();
9193 sicily(&mut f);
9194 assert_eq!(
9198 f.run(&[
9199 b"GEOSEARCH",
9200 b"Sicily",
9201 b"FROMLONLAT",
9202 b"15",
9203 b"37",
9204 b"BYBOX",
9205 b"400",
9206 b"400",
9207 b"km",
9208 b"ASC",
9209 b"WITHCOORD",
9210 b"WITHDIST",
9211 b"WITHHASH"
9212 ]),
9213 "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
9214 $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
9215 *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
9216 $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
9217 *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
9218 $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
9219 );
9220 }
9221
9222 #[test]
9223 fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
9224 let mut f = Fixture::new();
9225 sicily(&mut f);
9226 let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
9227 $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
9228 $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
9229 assert_eq!(
9230 f.run(&[
9231 b"GEOSEARCHSTORE",
9232 b"dst",
9233 b"Sicily",
9234 b"FROMLONLAT",
9235 b"15",
9236 b"37",
9237 b"BYRADIUS",
9238 b"200",
9239 b"km",
9240 b"ASC"
9241 ]),
9242 ":3\r\n"
9243 );
9244 assert_eq!(
9245 f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
9246 hashes
9247 );
9248 assert_eq!(
9251 f.run(&[
9252 b"GEORADIUS",
9253 b"Sicily",
9254 b"15",
9255 b"37",
9256 b"200",
9257 b"km",
9258 b"STORE",
9259 b"dst3"
9260 ]),
9261 ":3\r\n"
9262 );
9263 assert_eq!(
9264 f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
9265 hashes
9266 );
9267 assert_eq!(
9276 f.run(&[
9277 b"GEOSEARCHSTORE",
9278 b"dst2",
9279 b"Sicily",
9280 b"FROMLONLAT",
9281 b"15",
9282 b"37",
9283 b"BYRADIUS",
9284 b"200",
9285 b"km",
9286 b"ASC",
9287 b"STOREDIST"
9288 ]),
9289 ":3\r\n"
9290 );
9291 for (member, want) in [
9292 ("Catania", 56.441_257_870_158_19),
9293 ("Agrigento", 130.423_487_067_147_14),
9294 ("Palermo", 190.442_429_847_757_92),
9295 ] {
9296 let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
9297 let got: f64 = reply
9298 .trim_start_matches(|c: char| c != '\n')
9299 .trim()
9300 .parse()
9301 .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
9302 assert!(
9303 (got - want).abs() < 1e-9,
9304 "{member} scored {got} not {want}"
9305 );
9306 }
9307 assert_eq!(
9310 f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9311 "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9312 );
9313 assert_eq!(
9317 f.run(&[
9318 b"GEOSEARCHSTORE",
9319 b"dst",
9320 b"nokey",
9321 b"FROMLONLAT",
9322 b"15",
9323 b"37",
9324 b"BYRADIUS",
9325 b"200",
9326 b"km"
9327 ]),
9328 ":0\r\n"
9329 );
9330 assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9331 }
9332
9333 #[test]
9334 fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9335 let mut f = Fixture::new();
9336 sicily(&mut f);
9337 assert_eq!(
9340 f.run(&[
9341 b"GEOADD",
9342 b"Sicily",
9343 b"XX",
9344 b"CH",
9345 b"13.361389",
9346 b"38.115556",
9347 b"Palermo"
9348 ]),
9349 ":0\r\n"
9350 );
9351 assert_eq!(
9352 f.run(&[
9353 b"GEOADD",
9354 b"Sicily",
9355 b"NX",
9356 b"13.361389",
9357 b"38.9",
9358 b"Palermo"
9359 ]),
9360 ":0\r\n"
9361 );
9362 assert_eq!(
9363 f.run(&[
9364 b"GEOADD",
9365 b"Sicily",
9366 b"CH",
9367 b"13.361389",
9368 b"38.9",
9369 b"Palermo"
9370 ]),
9371 ":1\r\n"
9372 );
9373 assert_eq!(
9376 f.run(&[
9377 b"GEOADD",
9378 b"new",
9379 b"13.361389",
9380 b"38.115556",
9381 b"here",
9382 b"181",
9383 b"38",
9384 b"there"
9385 ]),
9386 "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9387 );
9388 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9389 assert_eq!(
9390 f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9391 "-ERR value is not a valid float\r\n"
9392 );
9393 assert_eq!(
9396 f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9397 "-ERR syntax error\r\n"
9398 );
9399 assert_eq!(
9400 f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9401 "-ERR syntax error\r\n"
9402 );
9403 assert_eq!(
9404 f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9405 "-ERR syntax error\r\n"
9406 );
9407 assert_eq!(
9408 f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9409 "-ERR wrong number of arguments for 'geoadd' command\r\n"
9410 );
9411 }
9412
9413 #[test]
9416 fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9417 let mut f = Fixture::new();
9418 sicily(&mut f);
9419 let cases: &[(&[&[u8]], &str)] = &[
9420 (
9421 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9422 "-ERR need numeric radius\r\n",
9423 ),
9424 (
9425 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9426 "-ERR radius cannot be negative\r\n",
9427 ),
9428 (
9429 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9430 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9431 ),
9432 (
9433 &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9434 "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9435 ),
9436 (
9437 &[
9438 b"GEOSEARCH",
9439 b"Sicily",
9440 b"FROMLONLAT",
9441 b"15",
9442 b"37",
9443 b"BYBOX",
9444 b"x",
9445 b"1",
9446 b"km",
9447 ],
9448 "-ERR need numeric width\r\n",
9449 ),
9450 (
9451 &[
9452 b"GEOSEARCH",
9453 b"Sicily",
9454 b"FROMLONLAT",
9455 b"15",
9456 b"37",
9457 b"BYBOX",
9458 b"1",
9459 b"y",
9460 b"km",
9461 ],
9462 "-ERR need numeric height\r\n",
9463 ),
9464 (
9465 &[
9466 b"GEOSEARCH",
9467 b"Sicily",
9468 b"FROMLONLAT",
9469 b"15",
9470 b"37",
9471 b"BYBOX",
9472 b"-1",
9473 b"1",
9474 b"km",
9475 ],
9476 "-ERR height or width cannot be negative\r\n",
9477 ),
9478 (
9479 &[
9480 b"GEOSEARCH",
9481 b"Sicily",
9482 b"FROMLONLAT",
9483 b"15",
9484 b"37",
9485 b"BYRADIUS",
9486 b"1",
9487 b"km",
9488 b"ANY",
9489 ],
9490 "-ERR the ANY argument requires COUNT argument\r\n",
9491 ),
9492 (
9493 &[
9494 b"GEOSEARCH",
9495 b"Sicily",
9496 b"FROMLONLAT",
9497 b"15",
9498 b"37",
9499 b"BYRADIUS",
9500 b"1",
9501 b"km",
9502 b"COUNT",
9503 b"0",
9504 ],
9505 "-ERR COUNT must be > 0\r\n",
9506 ),
9507 (
9508 &[
9509 b"GEOSEARCH",
9510 b"Sicily",
9511 b"BYRADIUS",
9512 b"1",
9513 b"km",
9514 b"BYBOX",
9515 b"1",
9516 b"1",
9517 b"km",
9518 ],
9519 "-ERR syntax error\r\n",
9520 ),
9521 (
9522 &[
9523 b"GEOSEARCH",
9524 b"Sicily",
9525 b"FROMMEMBER",
9526 b"Palermo",
9527 b"FROMLONLAT",
9528 b"1",
9529 b"2",
9530 b"BYRADIUS",
9531 b"1",
9532 b"km",
9533 ],
9534 "-ERR syntax error\r\n",
9535 ),
9536 (
9539 &[
9540 b"geosearch",
9541 b"Sicily",
9542 b"BYRADIUS",
9543 b"1",
9544 b"km",
9545 b"ASC",
9546 b"WITHDIST",
9547 ],
9548 "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9549 ),
9550 (
9551 &[
9552 b"GEOSEARCH",
9553 b"Sicily",
9554 b"FROMLONLAT",
9555 b"15",
9556 b"37",
9557 b"ASC",
9558 b"WITHDIST",
9559 ],
9560 "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9561 ),
9562 (
9565 &[
9566 b"GEOSEARCHSTORE",
9567 b"d",
9568 b"Sicily",
9569 b"FROMLONLAT",
9570 b"15",
9571 b"37",
9572 b"BYRADIUS",
9573 b"1",
9574 b"km",
9575 b"WITHCOORD",
9576 ],
9577 "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9578 ),
9579 (
9580 &[
9581 b"GEORADIUS",
9582 b"Sicily",
9583 b"15",
9584 b"37",
9585 b"1",
9586 b"km",
9587 b"WITHDIST",
9588 b"STORE",
9589 b"d",
9590 ],
9591 "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9592 ),
9593 (
9596 &[
9597 b"GEORADIUS_RO",
9598 b"Sicily",
9599 b"15",
9600 b"37",
9601 b"1",
9602 b"km",
9603 b"STORE",
9604 b"d",
9605 ],
9606 "-ERR syntax error\r\n",
9607 ),
9608 (
9609 &[
9610 b"GEOSEARCH",
9611 b"Sicily",
9612 b"FROMLONLAT",
9613 b"15",
9614 b"37",
9615 b"BYRADIUS",
9616 b"1",
9617 b"km",
9618 b"STOREDIST",
9619 ],
9620 "-ERR syntax error\r\n",
9621 ),
9622 ];
9623 for (parts, want) in cases {
9624 assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9625 }
9626 }
9627
9628 #[test]
9631 fn every_geo_command_says_wrongtype() {
9632 let mut f = Fixture::new();
9633 f.run(&[b"SET", b"s", b"v"]);
9634 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9635 let cases: &[&[&[u8]]] = &[
9636 &[b"GEOADD", b"s", b"13", b"38", b"m"],
9637 &[b"GEOPOS", b"s", b"m"],
9638 &[b"GEOHASH", b"s", b"m"],
9639 &[b"GEODIST", b"s", b"a", b"b"],
9640 &[
9641 b"GEOSEARCH",
9642 b"s",
9643 b"FROMLONLAT",
9644 b"15",
9645 b"37",
9646 b"BYRADIUS",
9647 b"1",
9648 b"km",
9649 ],
9650 &[
9651 b"GEOSEARCHSTORE",
9652 b"d",
9653 b"s",
9654 b"FROMLONLAT",
9655 b"15",
9656 b"37",
9657 b"BYRADIUS",
9658 b"1",
9659 b"km",
9660 ],
9661 &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9662 &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9663 &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9664 &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9665 ];
9666 for case in cases {
9667 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9668 }
9669 assert_eq!(
9672 f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9673 wrong
9674 );
9675 }
9676
9677 #[test]
9680 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9681 let mut f = Fixture::new();
9682 assert_eq!(
9685 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9686 ":3\r\n"
9687 );
9688 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9689 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9690 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9691 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9693 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9694 assert_eq!(
9695 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9696 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9697 );
9698 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9700 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9701 }
9702
9703 #[test]
9706 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9707 let mut f = Fixture::new();
9708 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9709 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9710 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9711 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9712 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9713 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9715 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9716 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9717
9718 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9722 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9723 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9724 assert_eq!(
9727 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9728 "-ERR array index overflow\r\n"
9729 );
9730 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9731 }
9732
9733 #[test]
9736 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9737 let mut f = Fixture::new();
9738 f.run(&[b"ARSET", b"a", b"1", b"x"]);
9739 assert_eq!(
9740 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9741 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9742 );
9743 assert_eq!(
9746 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9747 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9748 );
9749 assert_eq!(
9751 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9752 "*2\r\n$-1\r\n$-1\r\n"
9753 );
9754 assert_eq!(
9758 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9759 "-ERR range exceeds maximum of 1000000 items\r\n"
9760 );
9761 }
9762
9763 #[test]
9766 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9767 let mut f = Fixture::new();
9768 assert_eq!(
9769 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9770 "-ERR invalid array index\r\n"
9771 );
9772 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9773 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9774 assert_eq!(
9775 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9776 "-ERR invalid array index\r\n"
9777 );
9778 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9779 assert_eq!(
9782 f.run(&[b"ARGET", b"a", b"-1"]),
9783 "-ERR invalid array index\r\n"
9784 );
9785 assert_eq!(
9788 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9789 "-ERR wrong number of arguments for 'armset' command\r\n"
9790 );
9791 assert_eq!(
9792 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9793 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9794 );
9795 }
9796
9797 #[test]
9798 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9799 let mut f = Fixture::new();
9800 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9801 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9802 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9803 assert_eq!(
9806 f.run(&[
9807 b"ARDELRANGE",
9808 b"a",
9809 b"100",
9810 b"200",
9811 b"0",
9812 b"18446744073709551614"
9813 ]),
9814 ":2\r\n"
9815 );
9816 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9817 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
9818 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
9819 }
9820
9821 #[test]
9824 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
9825 let mut f = Fixture::new();
9826 let long = vec![b'v'; 200];
9827 f.run(&[
9828 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
9829 b"short", b"5", &long, b"6", b"-0",
9830 ]);
9831 assert_eq!(
9835 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
9836 format!(
9837 "*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",
9838 String::from_utf8_lossy(&long)
9839 )
9840 );
9841 }
9842
9843 #[test]
9844 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
9845 let mut f = Fixture::new();
9846 f.run(&[b"ARSET", b"a", b"0", b"x"]);
9847 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
9848 assert_eq!(
9849 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
9850 "$12\r\nsliced-array\r\n"
9851 );
9852 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
9854 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
9855 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
9856 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
9857 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
9858 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
9859 }
9860
9861 #[test]
9862 fn every_array_command_refuses_a_key_holding_something_else() {
9863 let mut f = Fixture::new();
9864 f.run(&[b"SET", b"s", b"v"]);
9865 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9866 for cmd in [
9867 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
9868 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
9869 &[b"ARGET".as_ref(), b"s", b"0"][..],
9870 &[b"ARMGET".as_ref(), b"s", b"0"][..],
9871 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
9872 &[b"ARLEN".as_ref(), b"s"][..],
9873 &[b"ARCOUNT".as_ref(), b"s"][..],
9874 &[b"ARDEL".as_ref(), b"s", b"0"][..],
9875 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
9876 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
9877 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
9878 &[b"ARNEXT".as_ref(), b"s"][..],
9879 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
9880 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
9881 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
9882 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
9883 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
9884 &[b"ARINFO".as_ref(), b"s"][..],
9885 ] {
9886 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
9887 }
9888 }
9889
9890 #[test]
9894 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
9895 let mut f = Fixture::new();
9896 f.run(&[b"SET", b"s", b"v"]);
9897 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9898 let bad = "-ERR invalid array index\r\n";
9899 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
9900 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
9901 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
9902 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
9903 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
9904 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
9905 f.run(&[b"ARSET", b"a", b"0", b"x"]);
9907 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
9908 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
9909 }
9910
9911 #[test]
9912 fn an_append_follows_a_cursor_the_client_can_move() {
9913 let mut f = Fixture::new();
9914 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
9915 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
9916 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
9917 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
9918 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
9919
9920 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
9923 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
9924 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
9925 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
9926 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
9927 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
9928 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
9929
9930 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
9933 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
9934 assert_eq!(
9935 f.run(&[b"ARINSERT", b"a", b"x"]),
9936 "-ERR insert index overflow\r\n"
9937 );
9938 assert_eq!(
9939 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
9940 "-ERR invalid array index\r\n"
9941 );
9942 }
9943
9944 #[test]
9945 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
9946 let mut f = Fixture::new();
9947 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
9948 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
9949 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
9950 assert_eq!(
9951 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
9952 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
9953 );
9954 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
9957 assert_eq!(
9958 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
9959 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
9960 );
9961 assert_eq!(
9964 f.run(&[b"ARRING", b"r", b"0", b"x"]),
9965 "-ERR size must be positive\r\n"
9966 );
9967 assert_eq!(
9968 f.run(&[b"ARRING", b"r", b"big", b"x"]),
9969 "-ERR invalid size\r\n"
9970 );
9971 }
9972
9973 #[test]
9974 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
9975 let mut f = Fixture::new();
9976 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
9977 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
9978 assert_eq!(
9979 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
9980 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
9981 );
9982 assert_eq!(
9983 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
9984 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
9985 );
9986 assert_eq!(
9987 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
9988 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
9989 "more than there is gets what there is"
9990 );
9991 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
9994 assert_eq!(
9995 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
9996 "-ERR syntax error\r\n"
9997 );
9998 assert_eq!(
9999 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
10000 "-ERR invalid COUNT\r\n"
10001 );
10002
10003 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
10006 assert_eq!(
10007 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
10008 "*2\r\n$-1\r\n$1\r\nz\r\n"
10009 );
10010 }
10011
10012 #[test]
10013 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
10014 let mut f = Fixture::new();
10015 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
10016 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
10017 assert_eq!(
10020 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
10021 "*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"
10022 );
10023 assert_eq!(
10024 f.run(&[
10025 b"ARSCAN",
10026 b"a",
10027 b"18446744073709551614",
10028 b"0",
10029 b"LIMIT",
10030 b"1"
10031 ]),
10032 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
10033 );
10034 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
10035 assert_eq!(
10036 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
10037 "-ERR LIMIT must be positive\r\n"
10038 );
10039 assert_eq!(
10040 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
10041 "-ERR syntax error\r\n"
10042 );
10043 assert_eq!(
10044 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
10045 "-ERR wrong number of arguments for 'arscan' command\r\n"
10046 );
10047 }
10048
10049 #[test]
10050 fn a_grep_answers_the_indexes_whose_elements_match() {
10051 let mut f = Fixture::new();
10052 assert_eq!(
10053 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
10054 "*0\r\n"
10055 );
10056 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
10057
10058 assert_eq!(
10061 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
10062 "*3\r\n:0\r\n:1\r\n:2\r\n"
10063 );
10064 assert_eq!(
10065 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
10066 "*3\r\n:2\r\n:1\r\n:0\r\n"
10067 );
10068 assert_eq!(
10069 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
10070 "*2\r\n:1\r\n:2\r\n"
10071 );
10072
10073 assert_eq!(
10076 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
10077 "*1\r\n:0\r\n"
10078 );
10079 assert_eq!(
10080 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
10081 "*2\r\n:0\r\n:3\r\n"
10082 );
10083 assert_eq!(
10084 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
10085 "*1\r\n:2\r\n"
10086 );
10087 assert_eq!(
10088 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
10089 "*2\r\n:1\r\n:2\r\n"
10090 );
10091
10092 let both: &[&[u8]] = &[
10095 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
10096 ];
10097 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
10098 assert_eq!(
10099 f.run(&[
10100 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
10101 ]),
10102 "*0\r\n"
10103 );
10104 assert_eq!(
10105 f.run(&[
10106 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
10107 ]),
10108 "*2\r\n:0\r\n:1\r\n"
10109 );
10110
10111 assert_eq!(
10114 f.run(&[
10115 b"ARGREP",
10116 b"a",
10117 b"-",
10118 b"+",
10119 b"MATCH",
10120 b"a",
10121 b"WITHVALUES",
10122 b"LIMIT",
10123 b"2"
10124 ]),
10125 "*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"
10126 );
10127 assert_eq!(
10128 f.run(&[
10129 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
10130 ]),
10131 "*1\r\n:3\r\n"
10132 );
10133 }
10134
10135 #[test]
10137 fn a_grep_reports_a_broken_command_the_way_redis_does() {
10138 let mut f = Fixture::new();
10139 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
10140 let syntax = "-ERR syntax error\r\n";
10141
10142 assert_eq!(
10145 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
10146 "-ERR invalid array index\r\n"
10147 );
10148 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
10149 assert_eq!(
10151 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
10152 syntax
10153 );
10154 assert_eq!(
10155 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
10156 syntax
10157 );
10158 assert_eq!(
10159 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
10160 syntax,
10161 "a command with no predicate in it at all"
10162 );
10163 assert_eq!(
10164 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
10165 "-ERR LIMIT must be positive\r\n"
10166 );
10167 assert_eq!(
10168 f.run(&[
10169 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
10170 ]),
10171 "-ERR value is not an integer or out of range\r\n"
10172 );
10173 assert_eq!(
10174 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
10175 "-ERR regular expression is empty\r\n"
10176 );
10177 assert_eq!(
10178 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
10179 "-ERR invalid regular expression: Missing ')'\r\n"
10180 );
10181 assert_eq!(
10182 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
10183 "-ERR regular expression backreferences are not supported\r\n"
10184 );
10185 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
10188 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
10189 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
10190 }
10191
10192 #[test]
10193 fn an_op_reduces_a_range_to_one_number() {
10194 let mut f = Fixture::new();
10195 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
10196 assert_eq!(
10197 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
10198 "$4\r\n-0.5\r\n"
10199 );
10200 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
10201 assert_eq!(
10202 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
10203 "$3\r\n2.5\r\n"
10204 );
10205 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
10206 assert_eq!(
10207 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
10208 ":1\r\n"
10209 );
10210 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
10213 assert_eq!(
10214 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
10215 "$19\r\n0.30000000000000004\r\n"
10216 );
10217 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
10218 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
10219
10220 f.run(&[b"ARSET", b"w", b"0", b"word"]);
10223 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
10224 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
10225 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
10226
10227 assert_eq!(
10228 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
10229 "-ERR unknown operation\r\n"
10230 );
10231 assert_eq!(
10232 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
10233 "-ERR MATCH requires a value argument\r\n"
10234 );
10235 assert_eq!(
10236 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
10237 "-ERR wrong number of arguments for 'arop' command\r\n"
10238 );
10239 }
10240
10241 #[test]
10242 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
10243 let mut f = Fixture::new();
10244 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
10245 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
10246 let short = f.run(&[b"ARINFO", b"a"]);
10247 assert!(
10248 short.starts_with("*14\r\n"),
10249 "seven pairs on RESP2: {short}"
10250 );
10251 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
10252 assert!(
10253 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
10254 "{short}"
10255 );
10256 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
10257 let full = f.run(&[b"ARINFO", b"a", b"full"]);
10258 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
10259 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
10262 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
10263 assert!(
10264 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
10265 "{full}"
10266 );
10267 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
10268
10269 let mut g = Fixture::new();
10271 g.run(&[b"HELLO", b"3"]);
10272 g.run(&[b"ARINSERT", b"a", b"x"]);
10273 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
10274 assert!(map.starts_with("%12\r\n"), "{map}");
10275 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
10276 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
10277 }
10278
10279 #[test]
10280 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
10281 let mut f = Fixture::new();
10282 for (score, want) in [
10285 ("3", "3"),
10286 ("3.5", "3.5"),
10287 ("0.3", "0.3"),
10288 ("1e30", "1e+30"),
10289 ("1e19", "1e+19"),
10290 ("1e-7", "1e-7"),
10291 ("0.000001", "0.000001"),
10292 ("4611686018427387904", "4611686018427387904"),
10293 ("-0", "-0"),
10294 ] {
10295 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
10296 assert_eq!(
10297 f.run(&[b"ZSCORE", b"z", b"m"]),
10298 format!("${}\r\n{want}\r\n", want.len()),
10299 "score {score}"
10300 );
10301 }
10302
10303 let mut g = Fixture::new();
10306 g.run(&[b"HELLO", b"3"]);
10307 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10308 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10309 assert_eq!(
10314 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10315 "$31\r\n1000000000000000000000000000000\r\n"
10316 );
10317 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10318 assert_eq!(
10319 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10320 "$20\r\n10000000000000000000\r\n"
10321 );
10322 }
10323
10324 #[test]
10327 fn a_node_comes_back_with_the_fields_it_went_in_with() {
10328 let mut f = Fixture::new();
10329 assert_eq!(
10330 f.run(&[
10331 b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10332 ]),
10333 ":1\r\n"
10334 );
10335 assert_eq!(
10341 f.run(&[b"G.NGET", b"social", b"ada"]),
10342 "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10343 );
10344 assert_eq!(
10347 f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10348 ":0\r\n"
10349 );
10350 assert_eq!(
10351 f.run(&[b"G.NGET", b"social", b"ada"]),
10352 "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10353 );
10354 assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10357 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10358 assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10359 assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10360
10361 assert_eq!(
10364 f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10365 "-ERR syntax error\r\n"
10366 );
10367 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10368
10369 let mut g = Fixture::new();
10371 g.run(&[b"HELLO", b"3"]);
10372 g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10373 assert_eq!(
10374 g.run(&[b"G.NGET", b"social", b"ada"]),
10375 "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10376 );
10377 }
10378
10379 #[test]
10380 fn an_edge_creates_the_ends_it_needs() {
10381 let mut f = Fixture::new();
10382 assert_eq!(
10383 f.run(&[
10384 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10385 ]),
10386 ":1\r\n"
10387 );
10388 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10390 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10391 assert_eq!(
10392 f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10393 "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10394 );
10395 assert_eq!(
10396 f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10397 "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10398 );
10399 assert_eq!(
10402 f.run(&[
10403 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10404 ]),
10405 ":0\r\n"
10406 );
10407 assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10408 assert_eq!(
10410 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10411 ":1\r\n"
10412 );
10413 assert_eq!(
10414 f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10415 ":1\r\n"
10416 );
10417
10418 assert_eq!(
10419 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10420 ":1\r\n"
10421 );
10422 assert_eq!(
10423 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10424 ":0\r\n"
10425 );
10426 assert_eq!(
10429 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10430 ":0\r\n"
10431 );
10432 assert_eq!(
10433 f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10434 ":0\r\n"
10435 );
10436 assert_eq!(
10437 f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10438 ":0\r\n"
10439 );
10440 }
10441
10442 #[test]
10445 fn a_hop_answers_a_cursor_and_a_page() {
10446 let mut f = Fixture::new();
10447 for i in 0..25u32 {
10448 let dst = format!("n{i}");
10449 f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10450 }
10451 let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10453 assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10454
10455 let mut seen = 0;
10456 let mut cursor = String::from("0");
10457 loop {
10458 let page = f.run(&[
10459 b"G.OUT",
10460 b"social",
10461 b"hub",
10462 b"FOLLOWS",
10463 b"COUNT",
10464 b"7",
10465 b"CURSOR",
10466 cursor.as_bytes(),
10467 ]);
10468 let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10469 cursor = head
10470 .rsplit("\r\n")
10471 .next()
10472 .expect("the cursor line")
10473 .to_string();
10474 seen += rest
10475 .split_once("\r\n")
10476 .expect("the page length")
10477 .0
10478 .parse::<usize>()
10479 .expect("a length");
10480 if cursor == "0" {
10481 break;
10482 }
10483 }
10484 assert_eq!(seen, 25, "every neighbour once across the pages");
10485
10486 assert_eq!(
10489 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10490 "*2\r\n$1\r\n0\r\n*0\r\n"
10491 );
10492 assert_eq!(
10493 f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10494 "*2\r\n$1\r\n0\r\n*0\r\n"
10495 );
10496 assert_eq!(
10497 f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10498 "*2\r\n$1\r\n0\r\n*0\r\n"
10499 );
10500 assert_eq!(
10501 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10502 "-ERR COUNT must be a positive integer\r\n"
10503 );
10504 assert_eq!(
10505 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10506 "-ERR syntax error\r\n"
10507 );
10508 }
10509
10510 #[test]
10511 fn a_degree_counts_one_way_or_both() {
10512 let mut f = Fixture::new();
10513 f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10514 f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10515 f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10516 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10517 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10518 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10519 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10520 assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10521 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10522 assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10523 assert_eq!(
10524 f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10525 "-ERR syntax error\r\n"
10526 );
10527 }
10528
10529 #[test]
10532 fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10533 let mut f = Fixture::new();
10534 for (src, dst) in [
10535 ("ada", "grace"),
10536 ("ada", "alan"),
10537 ("grace", "edsger"),
10538 ("alan", "edsger"),
10539 ("edsger", "barbara"),
10540 ] {
10541 f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10542 }
10543 assert_eq!(
10546 f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10547 "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10548 );
10549 assert_eq!(
10550 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10551 "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10552 );
10553 let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10554 assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10555 assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10556 assert_eq!(
10558 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10559 "*1\r\n$5\r\ngrace\r\n"
10560 );
10561 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10563 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10564 assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10565 assert_eq!(
10566 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10567 "-ERR DEPTH must be a positive integer\r\n"
10568 );
10569 assert_eq!(
10570 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10571 "-ERR syntax error\r\n"
10572 );
10573 }
10574
10575 #[test]
10578 fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10579 let mut f = Fixture::new();
10580 for i in 0..6u32 {
10583 let src = format!("n{i}");
10584 let dst = format!("n{}", i + 1);
10585 f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10586 }
10587 assert_eq!(
10588 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10589 "*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"
10590 );
10591 f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10592 assert_eq!(
10593 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10594 "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10595 );
10596 assert_eq!(
10599 f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10600 "*1\r\n$2\r\nn2\r\n"
10601 );
10602 assert_eq!(
10603 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10604 "*0\r\n"
10605 );
10606 assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10608 f.run(&[b"G.NADD", b"road", b"island"]);
10611 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10612 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10613 assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10614 assert_eq!(
10615 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10616 "-ERR syntax error\r\n"
10617 );
10618 }
10619
10620 #[test]
10624 fn the_keyspace_sees_a_graph_key_like_any_other() {
10625 let mut f = Fixture::new();
10626 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10627 assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10628 assert_eq!(
10629 f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10630 "$9\r\nadjacency\r\n"
10631 );
10632 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10633 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10634 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10635 let held = f.server.memory_bytes();
10639 for i in 0..200u32 {
10640 let dst = format!("n{i}");
10641 f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10642 }
10643 assert!(
10644 f.server.memory_bytes() > held,
10645 "two hundred edges cost something: {held} then {}",
10646 f.server.memory_bytes()
10647 );
10648 f.run(&[b"DEL", b"big"]);
10649
10650 assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10653 assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10654 assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10655 assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10656 assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10657 f.run(&[b"SELECT", b"1"]);
10658 assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10659
10660 assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10661 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10662 f.run(&[b"G.NADD", b"g", b"n"]);
10663 assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10664 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10665 }
10666
10667 #[test]
10670 fn a_graph_cannot_be_copied_or_dumped() {
10671 let mut f = Fixture::new();
10672 f.run(&[b"G.NADD", b"social", b"ada"]);
10673 assert_eq!(
10674 f.run(&[b"COPY", b"social", b"other"]),
10675 "-ERR COPY is not supported for a graph\r\n"
10676 );
10677 assert_eq!(
10678 f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10679 "-ERR COPY is not supported for a graph\r\n"
10680 );
10681 assert_eq!(
10682 f.run(&[b"DUMP", b"social"]),
10683 "-ERR DUMP is not supported for a graph\r\n"
10684 );
10685 assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10687 }
10688
10689 #[test]
10692 fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10693 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10694 let mut f = Fixture::new();
10695 f.run(&[b"G.NADD", b"social", b"ada"]);
10696 assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10697 assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10698 assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10699
10700 f.run(&[b"SET", b"str", b"v"]);
10701 for cmd in [
10702 vec![b"G.NADD".as_ref(), b"str", b"n"],
10703 vec![b"G.NGET".as_ref(), b"str", b"n"],
10704 vec![b"G.NDEL".as_ref(), b"str", b"n"],
10705 vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10706 vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10707 vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10708 vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10709 vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10710 vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10711 vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10712 ] {
10713 assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10714 }
10715 }
10716
10717 #[test]
10720 fn a_graph_goes_when_its_last_node_does() {
10721 let mut f = Fixture::new();
10722 f.run(&[
10723 b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10724 ]);
10725 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10726 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10728 assert_eq!(
10729 f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10730 ":0\r\n"
10731 );
10732 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10733 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10734
10735 assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10736 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10737 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10738 assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10739
10740 f.run(&[b"G.NADD", b"social", b"first"]);
10743 f.run(&[b"G.NADD", b"social", b"second"]);
10744 f.run(&[b"G.NDEL", b"social", b"first"]);
10745 f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10746 assert_eq!(
10747 f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10748 "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10749 );
10750 }
10751
10752 #[test]
10757 fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10758 let mut f = Fixture::new();
10759 let doc = br#"{"a":1,"b":{"c":true}}"#;
10760 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10761 assert_eq!(
10764 f.run(&[b"JSON.GET", b"doc"]),
10765 bulk(r#"{"a":1,"b":{"c":true}}"#)
10766 );
10767 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10768 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10769 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10770 assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10773 assert_eq!(
10774 f.run(&[b"JSON.GET", b"doc", b".nope"]),
10775 "-ERR Path does not exist\r\n"
10776 );
10777 assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10778 assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10781 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10782 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10783 assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10784 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10785 }
10786
10787 #[test]
10793 fn the_two_json_errors_that_carry_no_prefix() {
10794 let mut f = Fixture::new();
10795 f.run(&[b"SET", b"plain", b"x"]);
10796 let wrong = "-Existing key has wrong Redis type\r\n";
10797 assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10798 assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10799 assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10800 assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10801 assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10802
10803 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10804 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10808 assert_eq!(
10809 f.run(&[b"JSON.GET", b"doc"]),
10810 bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10811 );
10812 assert_eq!(
10813 f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10814 "-Err wrong static path\r\n"
10815 );
10816 }
10817
10818 #[test]
10820 fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10821 let mut f = Fixture::new();
10822 assert_eq!(
10824 f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
10825 "-ERR new objects must be created at the root\r\n"
10826 );
10827 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
10828 assert_eq!(
10831 f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
10832 "-ERR new objects must be created at the root\r\n"
10833 );
10834 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
10835 assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
10836
10837 f.run(&[
10838 b"JSON.SET",
10839 b"doc",
10840 b"$",
10841 br#"{"o":{},"arr":[1,2],"s":"x"}"#,
10842 ]);
10843 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10845 assert_eq!(
10848 f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
10849 "$-1\r\n"
10850 );
10851 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
10852 assert_eq!(
10854 f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
10855 "-ERR array index out of range\r\n"
10856 );
10857 assert_eq!(
10858 f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
10859 "-ERR array index out of range\r\n"
10860 );
10861 assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
10862 assert_eq!(
10865 f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
10866 "$-1\r\n"
10867 );
10868 assert_eq!(
10869 f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
10870 "$-1\r\n"
10871 );
10872 assert_eq!(
10873 f.run(&[b"JSON.GET", b"doc"]),
10874 bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
10875 );
10876 assert!(
10880 f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
10881 .starts_with("-this is not the start of a value")
10882 );
10883 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
10884 }
10885
10886 #[test]
10889 fn the_json_commands_that_do_not_answer_text() {
10890 let mut f = Fixture::new();
10891 let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
10892 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10893
10894 assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
10895 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
10896 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
10897 assert_eq!(
10898 f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
10899 format!("*1\r\n{}", bulk("integer"))
10900 );
10901 assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
10904 assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
10905
10906 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
10909 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
10910 assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
10913 assert_eq!(
10914 f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
10915 "-ERR Path does not exist or not a bool\r\n"
10916 );
10917 assert_eq!(
10918 f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
10919 "-ERR Path does not exist or not a bool\r\n"
10920 );
10921 assert_eq!(
10922 f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
10923 "-ERR could not perform this operation on a key that doesn't exist\r\n"
10924 );
10925
10926 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
10929 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
10930 assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
10931 assert_eq!(
10932 f.run(&[b"JSON.GET", b"doc"]),
10933 bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
10934 );
10935
10936 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
10939 assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
10940 assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
10945 assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
10946 assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10947 assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
10948 assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
10949 assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
10950 assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
10951 assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
10952 }
10953
10954 #[test]
10961 fn json_get_lays_out_the_wrapper_it_builds() {
10962 let mut f = Fixture::new();
10963 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
10964
10965 assert_eq!(
10966 f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
10967 bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
10968 );
10969 assert_eq!(
10971 f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
10972 bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
10973 );
10974 let fmt: &[&[u8]] = &[b"INDENT", b" ", b"NEWLINE", b"\n", b"SPACE", b" "];
10975 let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
10976 one.extend_from_slice(fmt);
10977 one.push(b"$.b");
10978 assert_eq!(
10979 f.run(&one),
10980 bulk("[\n [\n 1,\n {\n \"c\": 2\n }\n ]\n]")
10981 );
10982 let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
10983 two.extend_from_slice(fmt);
10984 two.push(b"$.a");
10985 two.push(b"$.nope");
10986 assert_eq!(
10987 f.run(&two),
10988 bulk("{\n \"$.a\": [\n 1\n ],\n \"$.nope\": []\n}")
10989 );
10990 let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
10993 root.push(b".a");
10994 assert_eq!(f.run(&root), bulk("1"));
10995 }
10996
10997 #[test]
11000 fn json_mget_answers_once_per_key_whatever_is_under_them() {
11001 let mut f = Fixture::new();
11002 f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
11003 f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
11004 f.run(&[b"SET", b"plain", b"x"]);
11005 assert_eq!(
11006 f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
11007 format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
11008 );
11009 assert_eq!(
11012 f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
11013 format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
11014 );
11015 assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
11018 }
11019
11020 #[test]
11028 fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
11029 let mut f = Fixture::new();
11030 let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
11031 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11032
11033 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
11034 assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
11035 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
11036 assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
11037 assert_eq!(
11038 f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
11039 format!("*2\r\n{}{}", bulk("x"), bulk("y"))
11040 );
11041 assert_eq!(
11044 f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
11045 "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
11046 );
11047
11048 assert_eq!(
11051 f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
11052 "-ERR Path does not exist\r\n"
11053 );
11054 assert_eq!(
11055 f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
11056 "-ERR Path does not exist\r\n"
11057 );
11058 assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
11059 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
11062 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
11065
11066 assert_eq!(
11070 f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
11071 "-ERR Path does not exist or not an array\r\n"
11072 );
11073 assert_eq!(
11074 f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
11075 "-ERR Path does not exist or not an object\r\n"
11076 );
11077 assert_eq!(
11078 f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
11079 "-WRONGTYPE wrong type of path value - expected object\r\n"
11080 );
11081 assert_eq!(
11082 f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
11083 "-WRONGTYPE wrong type of path value - expected string\r\n"
11084 );
11085
11086 assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
11089 assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
11090 assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
11091 assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
11092 assert_eq!(
11093 f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
11094 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11095 );
11096 assert_eq!(
11098 f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
11099 "-ERR Path does not exist or not an object\r\n"
11100 );
11101 }
11102
11103 #[test]
11109 fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
11110 let mut f = Fixture::new();
11111 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
11112
11113 assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
11114 assert_eq!(
11115 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
11116 "*1\r\n:6\r\n"
11117 );
11118 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
11119
11120 assert_eq!(
11123 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
11124 ":7\r\n"
11125 );
11126 assert_eq!(
11127 f.run(&[b"JSON.GET", b"doc", b".a"]),
11128 bulk("[1,2,3,4,5,0,6]")
11129 );
11130 assert_eq!(
11131 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
11132 ":8\r\n"
11133 );
11134 assert_eq!(
11136 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
11137 "-ERR index out of bounds\r\n"
11138 );
11139 assert_eq!(
11140 f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
11141 "-ERR index out of bounds\r\n"
11142 );
11143
11144 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
11147 assert_eq!(
11148 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
11149 ":3\r\n"
11150 );
11151 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
11152 assert_eq!(
11153 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
11154 ":2\r\n"
11155 );
11156 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
11157 assert_eq!(
11158 f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
11159 ":0\r\n"
11160 );
11161 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11162
11163 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
11166 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
11167 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
11168 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
11169 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
11170
11171 for call in [
11174 &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
11175 &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
11176 &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
11177 &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
11178 ] {
11179 for path in [&b".n"[..], &b".nope"[..]] {
11180 let args: Vec<&[u8]> = call
11181 .iter()
11182 .map(|a| if *a == b"PATH" { path } else { *a })
11183 .collect();
11184 assert_eq!(
11185 f.run(&args),
11186 "-ERR Path does not exist or not an array\r\n",
11187 "{} {}",
11188 String::from_utf8_lossy(call[0]),
11189 String::from_utf8_lossy(path)
11190 );
11191 }
11192 }
11193
11194 assert_eq!(
11197 f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
11198 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11199 );
11200 assert_eq!(
11201 f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
11202 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11203 );
11204
11205 assert!(
11210 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
11211 .starts_with("-this is not the start of a value")
11212 );
11213 assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11214 }
11215
11216 #[test]
11224 fn json_arrinsert_is_all_or_nothing_across_the_matches() {
11225 let mut f = Fixture::new();
11226 let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
11227 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11228 assert_eq!(
11229 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
11230 "-ERR index out of bounds\r\n"
11231 );
11232 assert_eq!(
11233 f.run(&[b"JSON.GET", b"doc"]),
11234 bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
11235 );
11236 assert_eq!(
11238 f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
11239 "*3\r\n:4\r\n:3\r\n:2\r\n"
11240 );
11241 assert_eq!(
11242 f.run(&[b"JSON.GET", b"doc"]),
11243 bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
11244 );
11245 }
11246
11247 #[test]
11255 fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
11256 let mut f = Fixture::new();
11257 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
11258
11259 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
11260 assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
11261 assert_eq!(
11262 f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
11263 "*1\r\n:1\r\n"
11264 );
11265
11266 assert_eq!(
11269 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
11270 ":3\r\n"
11271 );
11272 assert_eq!(
11275 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
11276 ":-1\r\n"
11277 );
11278
11279 for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
11282 assert_eq!(
11283 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
11284 ":3\r\n",
11285 "{}",
11286 String::from_utf8_lossy(start)
11287 );
11288 }
11289 assert_eq!(
11290 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
11291 ":0\r\n"
11292 );
11293 f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
11296 assert_eq!(
11297 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
11298 ":-1\r\n"
11299 );
11300
11301 f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
11305 assert_eq!(
11306 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11307 ":0\r\n"
11308 );
11309 assert_eq!(
11310 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11311 ":1\r\n"
11312 );
11313 assert_eq!(
11314 f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11315 ":-1\r\n"
11316 );
11317
11318 assert_eq!(
11322 f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11323 "-ERR Path does not exist\r\n"
11324 );
11325 assert_eq!(
11326 f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11327 "-WRONGTYPE wrong type of path value - expected array\r\n"
11328 );
11329 assert_eq!(
11330 f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11331 "-ERR Path does not exist\r\n"
11332 );
11333 assert_eq!(
11334 f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11335 "-ERR Path does not exist\r\n"
11336 );
11337 }
11338
11339 #[test]
11342 fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11343 let mut f = Fixture::new();
11344 let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11345 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11346
11347 assert_eq!(
11350 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11351 bulk("9").as_str()
11352 );
11353 assert_eq!(
11355 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11356 bulk("[11]").as_str()
11357 );
11358 assert_eq!(
11361 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11362 bulk("13.0").as_str()
11363 );
11364 assert_eq!(
11365 f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11366 bulk("number").as_str()
11367 );
11368 assert_eq!(
11369 f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11370 bulk("3.0").as_str()
11371 );
11372 assert_eq!(
11373 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11374 bulk("-8").as_str()
11375 );
11376 f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11379 assert_eq!(
11380 f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11381 bulk("1.224744871391589").as_str()
11382 );
11383 assert_eq!(
11384 f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11385 "-ERR result is not a number\r\n"
11386 );
11387 f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11391 assert_eq!(
11392 f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11393 "-ERR numeric overflow\r\n"
11394 );
11395 f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11396 assert_eq!(
11397 f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11398 "-ERR numeric overflow\r\n"
11399 );
11400 f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11402 assert_eq!(
11403 f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11404 "-ERR result is not a number\r\n"
11405 );
11406
11407 assert_eq!(
11411 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11412 bulk("[null]").as_str()
11413 );
11414 assert_eq!(
11415 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11416 bulk("[]").as_str()
11417 );
11418 assert_eq!(
11419 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11420 "-ERR Path does not exist or does not contains a number\r\n"
11421 );
11422 assert_eq!(
11423 f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11424 "-ERR Path does not exist or does not contains a number\r\n"
11425 );
11426 assert_eq!(
11429 f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11430 "-bad input number\r\n"
11431 );
11432 assert_eq!(
11433 f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11434 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11435 );
11436 assert_eq!(
11437 f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11438 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11439 );
11440 }
11441
11442 #[test]
11445 fn json_strappend_reads_its_shape_off_the_argument_count() {
11446 let mut f = Fixture::new();
11447 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11448
11449 assert_eq!(
11450 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11451 ":3\r\n"
11452 );
11453 assert_eq!(
11454 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11455 "*1\r\n:4\r\n"
11456 );
11457 assert_eq!(
11460 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11461 ":6\r\n"
11462 );
11463 f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11466 assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11467 assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11468
11469 assert_eq!(
11473 f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11474 "-WRONGTYPE wrong type of path value - expected string\r\n"
11475 );
11476 assert_eq!(
11477 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11478 "*1\r\n$-1\r\n"
11479 );
11480 assert_eq!(
11481 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11482 "-ERR Path does not exist or not a string\r\n"
11483 );
11484 assert_eq!(
11485 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11486 "*0\r\n"
11487 );
11488 assert_eq!(
11489 f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11490 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11491 );
11492 }
11493
11494 #[test]
11497 fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11498 let mut f = Fixture::new();
11499 let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11502
11503 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11504 assert_eq!(
11505 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11506 ":4\r\n"
11507 );
11508 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11509 assert_eq!(
11510 f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11511 ":2\r\n"
11512 );
11513 f.run(&[b"JSON.SET", b"doc", b"$", three]);
11514 assert_eq!(
11515 f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11516 ":1\r\n"
11517 );
11518 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11519 assert_eq!(
11520 f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11521 bulk("1").as_str()
11522 );
11523 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11524 assert_eq!(
11525 f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11526 bulk("13").as_str()
11527 );
11528 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11529 assert_eq!(
11530 f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11531 ":4\r\n"
11532 );
11533 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11534 assert_eq!(
11535 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11536 bulk("false").as_str()
11537 );
11538 assert_eq!(
11541 f.run(&[b"JSON.GET", b"doc", b".a"]),
11542 bulk("[false,true,false]").as_str()
11543 );
11544
11545 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11548 assert_eq!(
11549 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11550 ":3\r\n"
11551 );
11552 assert_eq!(
11553 f.run(&[b"JSON.GET", b"doc", b".a"]),
11554 bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11555 );
11556 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11559 assert_eq!(
11560 f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11561 "-ERR Path does not exist or not an array\r\n"
11562 );
11563 assert_eq!(
11564 f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11565 "-ERR Path does not exist or not a bool\r\n"
11566 );
11567 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11571 assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11572 assert_eq!(
11573 f.run(&[b"JSON.GET", b"doc", b".a"]),
11574 bulk("[[],[2]]").as_str()
11575 );
11576 }
11577
11578 #[test]
11581 fn a_write_reaches_a_match_that_sits_inside_another_match() {
11582 let mut f = Fixture::new();
11583 let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11584
11585 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11586 assert_eq!(
11587 f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11588 "*3\r\n:3\r\n:2\r\n:3\r\n"
11589 );
11590 assert_eq!(
11591 f.run(&[b"JSON.GET", b"doc", b"$"]),
11592 bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11593 );
11594
11595 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11598 assert_eq!(
11599 f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11600 "*3\r\n:1\r\n:1\r\n:1\r\n"
11601 );
11602 assert_eq!(
11603 f.run(&[b"JSON.GET", b"doc", b"$"]),
11604 bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11605 );
11606
11607 f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11610 assert_eq!(
11611 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11612 bulk("[null,8,8]").as_str()
11613 );
11614 }
11615
11616 #[test]
11619 fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11620 let mut f = Fixture::new();
11621 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11622
11623 assert_eq!(
11626 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11627 bulk("[null]").as_str()
11628 );
11629 assert_eq!(
11630 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11631 bulk("[null]").as_str()
11632 );
11633 assert_eq!(
11634 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11635 bulk("[]").as_str()
11636 );
11637 assert_eq!(
11638 f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11639 "-ERR Path does not exist or does not contains a number\r\n"
11640 );
11641 assert_eq!(
11643 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11644 "-bad input number\r\n"
11645 );
11646
11647 assert_eq!(
11649 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11650 "*1\r\n$-1\r\n"
11651 );
11652 assert_eq!(
11653 f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11654 "-ERR Path does not exist or not a string\r\n"
11655 );
11656 assert_eq!(
11657 f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11658 "-WRONGTYPE wrong type of path value - expected string\r\n"
11659 );
11660
11661 assert_eq!(
11663 f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11664 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11665 );
11666 assert_eq!(
11667 f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11668 "-ERR could not perform this operation on a key that doesn't exist\r\n"
11669 );
11670 }
11671
11672 #[test]
11675 fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11676 let mut f = Fixture::new();
11677
11678 assert_eq!(
11682 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11683 "+OK\r\n"
11684 );
11685 assert_eq!(
11686 f.run(&[b"JSON.GET", b"doc", b"$"]),
11687 bulk(r#"[{"x":null,"y":1}]"#).as_str()
11688 );
11689
11690 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11693 assert_eq!(
11694 f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11695 "+OK\r\n"
11696 );
11697 assert_eq!(
11698 f.run(&[b"JSON.GET", b"doc", b"$"]),
11699 bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11700 );
11701
11702 assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11704 assert_eq!(
11705 f.run(&[b"JSON.GET", b"doc", b"$"]),
11706 bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11707 );
11708
11709 assert_eq!(
11713 f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11714 "+OK\r\n"
11715 );
11716 assert_eq!(
11717 f.run(&[b"JSON.GET", b"doc", b"$"]),
11718 bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11719 );
11720
11721 assert_eq!(
11725 f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11726 "+OK\r\n"
11727 );
11728 assert_eq!(
11729 f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11730 bulk(r#"[{"z":null}]"#).as_str()
11731 );
11732 assert_eq!(
11733 f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11734 "$-1\r\n"
11735 );
11736 assert_eq!(
11737 f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11738 "-Err wrong static path\r\n"
11739 );
11740
11741 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11743 assert_eq!(
11744 f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11745 "+OK\r\n"
11746 );
11747 assert_eq!(
11748 f.run(&[b"JSON.GET", b"doc", b"$"]),
11749 bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11750 );
11751
11752 assert_eq!(
11754 f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11755 "-ERR syntax error\r\n"
11756 );
11757 assert_eq!(
11758 f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11759 "-ERR new objects must be created at the root\r\n"
11760 );
11761 f.run(&[b"SET", b"str", b"x"]);
11762 assert_eq!(
11763 f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11764 "-Existing key has wrong Redis type\r\n"
11765 );
11766 }
11767
11768 #[test]
11771 fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11772 let mut f = Fixture::new();
11773 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11774 assert_eq!(
11775 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11776 "+OK\r\n"
11777 );
11778 assert_eq!(
11782 f.run(&[b"JSON.GET", b"doc", b"$"]),
11783 bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11784 );
11785
11786 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11789 assert_eq!(
11790 f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11791 "+OK\r\n"
11792 );
11793 assert_eq!(
11794 f.run(&[b"JSON.GET", b"doc", b"$"]),
11795 bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11796 );
11797 }
11798
11799 #[test]
11802 fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11803 let mut f = Fixture::new();
11804 let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11805 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11806
11807 assert_eq!(
11808 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11809 bulk(r#"["a","c"]"#).as_str()
11810 );
11811 assert_eq!(
11814 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11815 bulk(r#"["a","c"]"#).as_str()
11816 );
11817 assert_eq!(
11819 f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
11820 bulk(r#""a""#).as_str()
11821 );
11822 assert_eq!(
11823 f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
11824 "*1\r\n$6\r\nobject\r\n"
11825 );
11826
11827 assert_eq!(
11831 f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
11832 bulk("[9,10]").as_str()
11833 );
11834 assert_eq!(
11835 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
11836 "+OK\r\n"
11837 );
11838 assert_eq!(
11839 f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
11840 "-Err wrong static path\r\n"
11841 );
11842 assert_eq!(
11843 f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
11844 ":2\r\n"
11845 );
11846 assert_eq!(
11847 f.run(&[b"JSON.GET", b"doc", b"$"]),
11848 bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
11849 );
11850
11851 assert!(
11854 f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
11855 .starts_with("-ERR")
11856 );
11857 assert!(
11858 f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
11859 .starts_with("-ERR")
11860 );
11861 }
11862
11863 #[test]
11866 fn a_filter_takes_the_membership_operators_and_the_methods_too() {
11867 let mut f = Fixture::new();
11868 let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
11869 f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11870
11871 for (path, want) in [
11872 (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
11873 (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
11874 (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
11875 (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
11876 (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
11877 (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
11878 (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
11879 (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
11880 (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
11881 (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
11882 (b"$.box[?(@.n~)].t", "[]"),
11883 (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
11884 (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
11885 (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
11886 (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
11887 ] {
11888 assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
11889 }
11890
11891 assert_eq!(
11894 f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
11895 "+OK\r\n"
11896 );
11897 assert_eq!(
11898 f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
11899 bulk(r#"["b"]"#).as_str()
11900 );
11901 }
11902
11903 #[test]
11906 fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
11907 let mut f = Fixture::new();
11908 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
11909 assert_eq!(
11910 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11911 "+OK\r\n"
11912 );
11913 assert_eq!(
11914 f.run(&[b"JSON.GET", b"doc", b"$"]),
11915 bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
11916 );
11917 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
11920 assert_eq!(
11921 f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11922 "+OK\r\n"
11923 );
11924 assert_eq!(
11925 f.run(&[b"JSON.GET", b"doc", b"$"]),
11926 bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
11927 );
11928 }
11929
11930 #[test]
11933 fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
11934 let mut f = Fixture::new();
11935 assert_eq!(
11936 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
11937 "+OK\r\n"
11938 );
11939 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
11940 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
11941
11942 assert_eq!(
11944 f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
11945 "+OK\r\n"
11946 );
11947 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
11948
11949 f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
11954 assert_eq!(
11955 f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
11956 "$-1\r\n"
11957 );
11958 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
11959 assert_eq!(
11960 f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
11961 "$-1\r\n"
11962 );
11963 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11964
11965 f.run(&[b"SET", b"str", b"x"]);
11970 assert_eq!(
11971 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
11972 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
11973 );
11974 assert_eq!(
11975 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
11976 "-Existing key has wrong Redis type\r\n"
11977 );
11978 assert_eq!(
11979 f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
11980 "-ERR new objects must be created at the root\r\n"
11981 );
11982 assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
11983
11984 assert_eq!(
11989 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
11990 "-Err wrong static path\r\n"
11991 );
11992 assert_eq!(
11993 f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
11994 "-ERR array index out of range\r\n"
11995 );
11996 assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11997
11998 f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
12002 assert_eq!(
12003 f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
12004 "+OK\r\n"
12005 );
12006 assert_eq!(
12007 f.run(&[b"JSON.GET", b"c", b"$"]),
12008 bulk(r#"[{"n":3}]"#).as_str()
12009 );
12010
12011 assert_eq!(
12014 f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
12015 "-ERR wrong number of arguments for 'json.mset' command\r\n"
12016 );
12017 }
12018
12019 #[test]
12022 fn json_resp_answers_the_document_as_resp_types() {
12023 let mut f = Fixture::new();
12024 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
12025 assert_eq!(
12026 f.run(&[b"JSON.RESP", b"doc"]),
12027 "*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"
12028 );
12029 assert_eq!(
12031 f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
12032 "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
12033 );
12034
12035 f.run(&[
12036 b"JSON.SET",
12037 b"doc",
12038 b"$",
12039 br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
12040 ]);
12041 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
12042 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
12043 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
12046 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
12047 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
12048
12049 assert_eq!(
12052 f.run(&[b"JSON.RESP", b"doc", b".nope"]),
12053 "-ERR Path does not exist\r\n"
12054 );
12055 assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
12056 assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
12057 assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
12058 }
12059
12060 #[test]
12064 fn json_debug_answers_a_byte_count_and_its_own_help() {
12065 let mut f = Fixture::new();
12066 f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
12067 let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
12068 assert!(one.starts_with(':'), "{one}");
12069 assert_eq!(
12070 f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
12071 format!("*1\r\n{one}")
12072 );
12073 let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
12074 assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
12075
12076 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
12080 assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
12081 assert_eq!(
12082 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
12083 "-ERR Path does not exist\r\n"
12084 );
12085 assert_eq!(
12086 f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
12087 "*0\r\n"
12088 );
12089
12090 assert_eq!(
12091 f.run(&[b"JSON.DEBUG", b"HELP"]),
12092 "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
12093 $34\r\nHELP - this message\r\n"
12094 );
12095 assert_eq!(
12096 f.run(&[b"JSON.DEBUG", b"NOPE"]),
12097 "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
12098 );
12099 assert_eq!(
12100 f.run(&[b"JSON.DEBUG", b"MEMORY"]),
12101 "-ERR wrong number of arguments for 'json.debug' command\r\n"
12102 );
12103 }
12104
12105 #[test]
12110 fn the_first_vadd_decides_how_wide_the_set_is() {
12111 let mut f = Fixture::new();
12112 assert_eq!(
12113 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
12114 ":1\r\n"
12115 );
12116 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12117 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12118 assert_eq!(
12121 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
12122 ":0\r\n"
12123 );
12124 assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12125 assert_eq!(
12128 f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
12129 "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
12130 );
12131 assert_eq!(
12134 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
12135 ":1\r\n"
12136 );
12137 assert_eq!(
12138 f.run(&[b"VEMB", b"v", b"nowhere"]),
12139 "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
12140 );
12141 assert_eq!(
12145 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
12146 "-ERR asked quantization mismatch with existing vector set\r\n"
12147 );
12148 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
12151 assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
12152 assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
12153 }
12154
12155 #[test]
12158 fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
12159 let mut f = Fixture::new();
12160 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
12161 assert_eq!(
12167 f.run(&[b"VEMB", b"v", b"a"]),
12168 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12169 );
12170 f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
12172 assert_eq!(
12173 f.run(&[b"VEMB", b"n", b"a"]),
12174 "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
12175 );
12176 f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
12179 assert_eq!(
12180 f.run(&[b"VEMB", b"b", b"a"]),
12181 "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
12182 );
12183 assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
12184 assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
12185
12186 let mut f = Fixture::new();
12190 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
12191 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
12192 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
12193 assert_eq!(
12194 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
12195 "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
12196 $8\r\nopposite\r\n$1\r\n0\r\n"
12197 );
12198 assert_eq!(
12201 f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
12202 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12203 );
12204 assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
12207 assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
12208 assert_eq!(
12211 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
12212 "*1\r\n$6\r\nacross\r\n"
12213 );
12214 assert_eq!(
12215 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
12216 "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12217 );
12218 assert_eq!(
12222 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
12223 "*1\r\n$6\r\nacross\r\n"
12224 );
12225
12226 let mut g = Fixture::new();
12229 g.run(&[b"HELLO", b"3"]);
12230 g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12231 assert_eq!(
12232 g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
12233 "%1\r\n$4\r\neast\r\n,1\r\n"
12234 );
12235 }
12236
12237 #[test]
12239 fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
12240 let mut f = Fixture::new();
12241 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12242 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12243 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
12244 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
12245 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
12248 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
12249 assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
12251 assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12252 assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
12255 assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
12256 assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
12257 assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
12260 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
12261 assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
12262 assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
12263
12264 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12266 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12267 assert_eq!(
12268 f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
12269 "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
12270 );
12271 }
12272
12273 #[test]
12276 fn vrem_takes_the_attribute_with_it() {
12277 let mut f = Fixture::new();
12278 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12279 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12280 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
12281 assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
12282 assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
12283 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12286
12287 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12290 f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12291 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12292 f.run(&[b"VREM", b"v", b"east"]);
12293 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
12294 assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
12295 }
12296
12297 #[test]
12300 fn vinfo_says_partition_first() {
12301 let mut f = Fixture::new();
12302 f.run(&[
12303 b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
12304 ]);
12305 f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
12306 let info = f.run(&[b"VINFO", b"v"]);
12307 assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12308 assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12311 assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12312 assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12313 assert!(
12316 info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12317 "{info}"
12318 );
12319 let mut f = Fixture::new();
12320 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12321 assert!(
12322 f.run(&[b"VINFO", b"v"])
12323 .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12324 );
12325 assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12326 }
12327
12328 fn named() -> Fixture {
12330 let mut f = Fixture::new();
12331 for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12332 .iter()
12333 .enumerate()
12334 {
12335 let x = (i + 1).to_string();
12336 f.run(&[
12337 b"VADD",
12338 b"r",
12339 b"VALUES",
12340 b"2",
12341 x.as_bytes(),
12342 b"1",
12343 name.as_bytes(),
12344 ]);
12345 }
12346 f
12347 }
12348
12349 #[test]
12352 fn vrange_walks_the_names_and_not_the_vectors() {
12353 let mut f = named();
12354 assert_eq!(
12355 f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12356 "*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"
12357 );
12358 assert_eq!(
12359 f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12360 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12361 "the high end is a name and not a prefix, so delta is past it"
12362 );
12363 assert_eq!(
12364 f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12365 "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12366 );
12367 assert_eq!(
12368 f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12369 "*1\r\n$4\r\nbeta\r\n"
12370 );
12371 assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12372 f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12375 assert_eq!(
12376 f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12377 "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12378 );
12379 assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12380 }
12381
12382 #[test]
12385 fn a_vrange_count_of_zero_asks_for_nothing() {
12386 let mut f = named();
12387 assert_eq!(
12388 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12389 "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12390 );
12391 assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12392 assert!(
12393 f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12394 .starts_with("*5\r\n"),
12395 "a negative count is no limit at all"
12396 );
12397 }
12398
12399 #[test]
12402 fn vrange_says_which_end_it_could_not_read() {
12403 let mut f = named();
12404 assert_eq!(
12405 f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12406 "-ERR invalid start range format\r\n"
12407 );
12408 assert_eq!(
12409 f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12410 "-ERR invalid end range format\r\n",
12411 "the high end is spelled wrong, which is worth saying before the \
12412 low end being on the wrong side"
12413 );
12414 assert_eq!(
12415 f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12416 "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12417 );
12418 assert_eq!(
12421 f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12422 "-ERR invalid start range format\r\n"
12423 );
12424 assert_eq!(
12425 f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12426 "-ERR invalid COUNT value\r\n"
12427 );
12428 assert_eq!(
12429 f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12430 "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12431 );
12432 f.run(&[b"SET", b"s", b"x"]);
12433 assert!(
12434 f.run(&[b"VRANGE", b"s", b"-", b"+"])
12435 .starts_with("-WRONGTYPE")
12436 );
12437 }
12438
12439 #[test]
12442 fn reduce_is_refused_and_not_ignored() {
12443 let mut f = Fixture::new();
12444 let reduce = f.run(&[
12445 b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12446 ]);
12447 assert!(
12448 reduce.starts_with("-ERR REDUCE is not supported."),
12449 "{reduce}"
12450 );
12451 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12452 }
12453
12454 #[test]
12457 fn vsim_filter_reads_the_attributes() {
12458 let mut f = Fixture::new();
12459 for (name, x, y, attr) in [
12460 ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12461 ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12462 ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12463 ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12464 ] {
12465 f.run(&[
12466 b"VADD",
12467 b"v",
12468 b"VALUES",
12469 b"2",
12470 x.as_bytes(),
12471 y.as_bytes(),
12472 name.as_bytes(),
12473 b"SETATTR",
12474 attr.as_bytes(),
12475 ]);
12476 }
12477 assert_eq!(
12480 f.run(&[
12481 b"VSIM",
12482 b"v",
12483 b"VALUES",
12484 b"2",
12485 b"9",
12486 b"1",
12487 b"COUNT",
12488 b"2",
12489 b"FILTER",
12490 b".lang == \"en\"",
12491 ]),
12492 "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12493 );
12494 assert_eq!(
12497 f.run(&[
12498 b"VSIM",
12499 b"v",
12500 b"VALUES",
12501 b"2",
12502 b"9",
12503 b"1",
12504 b"FILTER",
12505 b".lang == 'en' and .year > 1980",
12506 ]),
12507 "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12508 );
12509 assert_eq!(
12511 f.run(&[
12512 b"VSIM",
12513 b"v",
12514 b"VALUES",
12515 b"2",
12516 b"9",
12517 b"1",
12518 b"FILTER",
12519 b".lang in ['fr', 'de']",
12520 ]),
12521 "*1\r\n$1\r\nb\r\n"
12522 );
12523 assert_eq!(
12524 f.run(&[
12525 b"VSIM",
12526 b"v",
12527 b"VALUES",
12528 b"2",
12529 b"9",
12530 b"1",
12531 b"FILTER",
12532 b".rating > 3"
12533 ]),
12534 "*0\r\n"
12535 );
12536 assert_eq!(
12539 f.run(&[
12540 b"VSIM",
12541 b"v",
12542 b"VALUES",
12543 b"2",
12544 b"9",
12545 b"1",
12546 b"TRUTH",
12547 b"FILTER",
12548 b".year < 1980",
12549 ]),
12550 "*1\r\n$1\r\nc\r\n"
12551 );
12552 f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12555 assert_eq!(
12556 f.run(&[
12557 b"VSIM",
12558 b"v",
12559 b"VALUES",
12560 b"2",
12561 b"9",
12562 b"1",
12563 b"COUNT",
12564 b"1",
12565 b"FILTER",
12566 b".lang == \"en\"",
12567 ]),
12568 "*1\r\n$1\r\nb\r\n"
12569 );
12570 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12573 assert_eq!(
12574 f.run(&[
12575 b"VSIM",
12576 b"v",
12577 b"VALUES",
12578 b"2",
12579 b"9",
12580 b"1",
12581 b"COUNT",
12582 b"1",
12583 b"FILTER",
12584 b".lang == \"en\"",
12585 ]),
12586 "*1\r\n$1\r\nb\r\n"
12587 );
12588
12589 let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12592 assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12593 assert_eq!(
12594 f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12595 "-ERR invalid FILTER expression\r\n"
12596 );
12597 assert_eq!(
12600 f.run(&[
12601 b"VSIM",
12602 b"v",
12603 b"VALUES",
12604 b"2",
12605 b"9",
12606 b"1",
12607 b"COUNT",
12608 b"1",
12609 b"FILTER-EF",
12610 b"500",
12611 b"FILTER",
12612 b".lang == 'en'",
12613 ]),
12614 "*1\r\n$1\r\nb\r\n"
12615 );
12616 assert_eq!(
12617 f.run(&[
12618 b"VSIM",
12619 b"v",
12620 b"VALUES",
12621 b"2",
12622 b"9",
12623 b"1",
12624 b"COUNT",
12625 b"1",
12626 b"FILTER-EF",
12627 b"0"
12628 ]),
12629 "*1\r\n$1\r\nb\r\n"
12630 );
12631 assert_eq!(
12632 f.run(&[
12633 b"VSIM",
12634 b"v",
12635 b"VALUES",
12636 b"2",
12637 b"9",
12638 b"1",
12639 b"FILTER-EF",
12640 b"lots"
12641 ]),
12642 "-ERR EF must be a positive integer\r\n"
12643 );
12644 }
12645
12646 #[test]
12649 fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12650 let mut f = Fixture::new();
12651 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12652 assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12653 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12654 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12655 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12656 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12657 assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12658 assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12659 assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12660 assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12661 assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12662
12663 f.run(&[b"SET", b"s", b"1"]);
12665 assert_eq!(
12666 f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12667 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12668 );
12669 assert_eq!(
12670 f.run(&[b"VCARD", b"s"]),
12671 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12672 );
12673 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12674 assert_eq!(
12675 f.run(&[b"GET", b"v"]),
12676 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12677 );
12678 f.run(&[b"G.NADD", b"social", b"ada"]);
12682 assert_eq!(
12683 f.run(&[b"VCARD", b"social"]),
12684 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12685 );
12686 assert_eq!(
12687 f.run(&[b"G.NGET", b"v", b"ada"]),
12688 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12689 );
12690 }
12691
12692 #[test]
12695 fn vrandmember_has_the_two_shapes_srandmember_has() {
12696 let mut f = Fixture::new();
12697 for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12698 let x = (i + 1).to_string();
12699 f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12700 }
12701 let one = f.run(&[b"VRANDMEMBER", b"v"]);
12703 assert!(one.starts_with("$1\r\n"), "{one}");
12704 let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12706 assert!(all.starts_with("*3\r\n"), "{all}");
12707 for name in ["a", "b", "c"] {
12708 assert!(all.contains(name), "{all} is missing {name}");
12709 }
12710 all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12711 assert!(all.starts_with("*2\r\n"), "{all}");
12712 let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12714 assert!(many.starts_with("*5\r\n"), "{many}");
12715 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12717 assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12718 }
12719
12720 #[test]
12723 fn vlinks_reports_one_layer_of_partition_neighbours() {
12724 let mut f = Fixture::new();
12725 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12726 f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12727 assert_eq!(
12730 f.run(&[b"VLINKS", b"v", b"east"]),
12731 "*1\r\n*1\r\n$5\r\nnorth\r\n"
12732 );
12733 assert_eq!(
12734 f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12735 "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12736 );
12737 assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12738 assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12739 }
12740
12741 #[test]
12744 fn fp32_and_values_are_the_same_vector() {
12745 let mut f = Fixture::new();
12746 let mut blob = Vec::new();
12747 for x in [3.0f32, 4.0] {
12748 blob.extend_from_slice(&x.to_le_bytes());
12749 }
12750 assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12751 assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12752 assert_eq!(
12753 f.run(&[b"VEMB", b"v", b"a"]),
12754 "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12755 );
12756 assert_eq!(
12762 f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
12763 "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
12764 );
12765 assert_eq!(
12767 f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12768 "-ERR invalid vector specification\r\n"
12769 );
12770 assert_eq!(
12772 f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12773 "-ERR syntax error\r\n"
12774 );
12775 assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12776 }
12777
12778 #[test]
12783 fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12784 let mut f = Fixture::new();
12785 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12786 assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12787 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12788 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12789 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12790 assert_eq!(
12793 f.run(&[b"BF.INFO", b"b"]),
12794 "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12795 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12796 +Expansion rate\r\n:2\r\n"
12797 );
12798 assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12799 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12800 assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12803 assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12804 }
12805
12806 #[test]
12812 fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12813 let mut f = Fixture::new();
12814 f.run(&[b"SET", b"s", b"text"]);
12815 assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12816 assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12817 for cmd in [
12818 vec![&b"BF.ADD"[..], b"s", b"x"],
12819 vec![&b"BF.MADD"[..], b"s", b"x"],
12820 vec![&b"BF.CARD"[..], b"s"],
12821 vec![&b"BF.INFO"[..], b"s"],
12822 vec![&b"BF.DEBUG"[..], b"s"],
12823 vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
12824 ] {
12825 let name = String::from_utf8_lossy(cmd[0]).into_owned();
12826 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12827 }
12828 assert_eq!(
12831 f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
12832 "-ERR bad error rate\r\n"
12833 );
12834 assert!(
12835 f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
12836 .starts_with("-WRONGTYPE")
12837 );
12838 }
12839
12840 #[test]
12843 fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
12844 let mut f = Fixture::new();
12845 assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
12846 for i in 0..10u32 {
12847 assert_eq!(
12848 f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
12849 ":1\r\n"
12850 );
12851 }
12852 assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
12853 assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
12854 assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
12855 assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
12858 assert_eq!(
12859 f.run(&[b"BF.DEBUG", b"g"]),
12860 "*3\r\n$7\r\nsize:11\r\n\
12861 $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
12862 $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
12863 );
12864
12865 assert_eq!(
12867 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
12868 "+OK\r\n"
12869 );
12870 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
12871 assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
12872 assert_eq!(
12873 f.run(&[b"BF.ADD", b"n", b"c"]),
12874 "-ERR non scaling filter is full\r\n"
12875 );
12876 assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12879 assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
12882 f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
12883 assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
12884 assert_eq!(
12887 f.run(&[
12888 b"BF.RESERVE",
12889 b"q",
12890 b"0.01",
12891 b"2",
12892 b"NONSCALING",
12893 b"EXPANSION",
12894 b"2"
12895 ]),
12896 "-Nonscaling filters cannot expand\r\n"
12897 );
12898 }
12899
12900 #[test]
12903 fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
12904 let mut f = Fixture::new();
12905 f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
12906 assert_eq!(
12907 f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
12908 "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
12909 );
12910 assert_eq!(
12911 f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
12912 "*2\r\n:1\r\n:0\r\n"
12913 );
12914 }
12915
12916 #[test]
12919 fn insert_is_a_reserve_and_a_madd_with_different_errors() {
12920 let mut f = Fixture::new();
12921 assert_eq!(
12922 f.run(&[
12923 b"BF.INSERT",
12924 b"i",
12925 b"CAPACITY",
12926 b"50",
12927 b"ERROR",
12928 b"0.001",
12929 b"ITEMS",
12930 b"a",
12931 b"b"
12932 ]),
12933 "*2\r\n:1\r\n:1\r\n"
12934 );
12935 assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
12936 assert_eq!(
12938 f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
12939 "-ERR not found\r\n"
12940 );
12941 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
12942 assert_eq!(
12945 f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
12946 "-Bad capacity\r\n"
12947 );
12948 assert_eq!(
12949 f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
12950 "-Bad error rate\r\n"
12951 );
12952 assert_eq!(
12953 f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
12954 "-Bad expansion\r\n"
12955 );
12956 assert_eq!(
12961 f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
12962 "*1\r\n:1\r\n"
12963 );
12964 assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
12965 assert_eq!(
12969 f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
12970 "*1\r\n:1\r\n"
12971 );
12972 assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
12973 assert_eq!(
12974 f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
12975 "*1\r\n:1\r\n"
12976 );
12977 assert_eq!(
12978 f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
12979 "-ERR not found\r\n"
12980 );
12981 assert_eq!(
12983 f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12984 "-Unknown argument received\r\n"
12985 );
12986 assert_eq!(
12988 f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12989 "*1\r\n:1\r\n"
12990 );
12991 assert!(
12993 f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
12994 .contains("wrong number of arguments")
12995 );
12996 }
12997
12998 #[test]
13001 fn a_dump_replays_into_a_filter_that_answers_the_same() {
13002 let mut f = Fixture::new();
13003 f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
13004 for i in 0..25u32 {
13005 f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
13006 }
13007 assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
13008
13009 let mut iter = b"0".to_vec();
13012 let mut chunks = 0;
13013 loop {
13014 let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
13015 let text = String::from_utf8_lossy(&raw).into_owned();
13016 let next = text
13017 .split("\r\n")
13018 .nth(1)
13019 .and_then(|n| n.strip_prefix(':'))
13020 .expect("a two element reply of an iterator and a chunk")
13021 .to_owned();
13022 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13023 let data = &body[body
13024 .windows(2)
13025 .position(|w| w == b"\r\n")
13026 .expect("a length line")
13027 + 2..body.len() - 2];
13028 if next == "0" {
13029 assert!(data.is_empty(), "the last chunk is empty");
13030 break;
13031 }
13032 let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
13033 assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
13034 iter = next.into_bytes();
13035 chunks += 1;
13036 }
13037 assert_eq!(chunks, 3, "a header and one chunk per link");
13038
13039 assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
13040 assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
13041 for i in 0..25u32 {
13042 assert_eq!(
13043 f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
13044 ":1\r\n"
13045 );
13046 }
13047
13048 assert_eq!(
13051 f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
13052 "-ERR received bad data\r\n"
13053 );
13054 assert_eq!(
13055 f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
13056 "-ERR received bad data\r\n"
13057 );
13058 assert_eq!(
13060 f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
13061 "-ERR invalid offset - no link found\r\n"
13062 );
13063 assert_eq!(
13064 f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
13065 "-ERR Second argument must be numeric\r\n"
13066 );
13067 assert_eq!(
13070 f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
13071 "-Second argument must be numeric\r\n"
13072 );
13073 }
13074
13075 #[test]
13078 fn reserve_reads_its_numbers_the_way_string2ll_does() {
13079 let mut f = Fixture::new();
13080 for (args, want) in [
13081 (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
13082 (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
13083 (
13084 vec![&b"0"[..], b"10"],
13085 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13086 ),
13087 (
13088 vec![&b"1"[..], b"10"],
13089 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13090 ),
13091 (
13092 vec![&b"inf"[..], b"10"],
13093 "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13094 ),
13095 (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
13096 (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
13097 (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
13098 (
13099 vec![&b"0.01"[..], b"0"],
13100 "-ERR capacity must be in the range [1, 1073741824]\r\n",
13101 ),
13102 (
13103 vec![&b"0.01"[..], b"1073741825"],
13104 "-ERR capacity must be in the range [1, 1073741824]\r\n",
13105 ),
13106 ] {
13107 let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
13108 cmd.extend(args.iter().copied());
13109 assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
13110 }
13111 assert_eq!(
13112 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
13113 "-ERR no expansion\r\n"
13114 );
13115 assert_eq!(
13116 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
13117 "-ERR bad expansion\r\n"
13118 );
13119 assert_eq!(
13120 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
13121 "-ERR expansion must be in the range [0, 32768]\r\n"
13122 );
13123 assert_eq!(
13125 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
13126 "+OK\r\n"
13127 );
13128 assert_eq!(
13129 f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
13130 "-ERR item exists\r\n"
13131 );
13132 assert_eq!(
13133 f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
13134 "-Invalid information value\r\n"
13135 );
13136 assert!(
13137 f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
13138 .contains("wrong number of arguments")
13139 );
13140 }
13141
13142 #[test]
13144 fn the_bloom_family_answers_in_resp3_spelling_too() {
13145 let mut f = Fixture::new();
13146 f.out.set_proto(Proto::Resp3);
13147 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
13148 assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
13149 assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
13150 assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
13151 assert_eq!(
13152 f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
13153 "*2\r\n#t\r\n#f\r\n"
13154 );
13155 assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
13157 assert_eq!(
13158 f.run(&[b"BF.INFO", b"b"]),
13159 "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
13160 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
13161 +Expansion rate\r\n:2\r\n"
13162 );
13163 assert_eq!(
13166 f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
13167 "%1\r\n+Capacity\r\n:100\r\n"
13168 );
13169 }
13170
13171 fn cf_header(
13180 items: u64,
13181 buckets: u64,
13182 deletes: u64,
13183 filters: u64,
13184 geometry: [u16; 3],
13185 ) -> Vec<u8> {
13186 let mut out = Vec::with_capacity(38);
13187 for n in [items, buckets, deletes, filters] {
13188 out.extend_from_slice(&n.to_le_bytes());
13189 }
13190 for n in geometry {
13191 out.extend_from_slice(&n.to_le_bytes());
13192 }
13193 out
13194 }
13195
13196 #[test]
13200 fn cf_add_makes_the_filter_and_counts_the_copies() {
13201 let mut f = Fixture::new();
13202 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13203 assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13204 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
13205 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
13208 assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
13209 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
13210 assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
13211 assert_eq!(
13212 f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
13213 "*2\r\n:1\r\n:0\r\n"
13214 );
13215 assert_eq!(
13218 f.run(&[b"CF.INFO", b"d"]),
13219 "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13220 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
13221 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
13222 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13223 );
13224 assert_eq!(
13225 f.run(&[b"CF.DEBUG", b"d"]),
13226 "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
13227 max_iterations:20 expansion:1\r\n"
13228 );
13229 assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
13230 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13231
13232 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13234 assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
13235 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13236 assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
13237 assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
13238
13239 assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
13242 assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
13243 assert_eq!(
13244 f.run(&[b"CF.COMPACT", b"gone"]),
13245 "-Cuckoo filter was not found\r\n"
13246 );
13247 assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
13248 assert!(
13252 f.run(&[b"CF.COMPACT", b"a", b"b"])
13253 .contains("wrong number of arguments")
13254 );
13255 }
13256
13257 #[test]
13260 fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
13261 let mut f = Fixture::new();
13262 f.run(&[b"SET", b"s", b"text"]);
13263 assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
13264 assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
13265 assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
13266 assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
13270 assert_eq!(
13271 f.run(&[b"CF.COMPACT", b"s"]),
13272 "-Cuckoo filter was not found\r\n"
13273 );
13274 for cmd in [
13275 vec![&b"CF.ADD"[..], b"s", b"x"],
13276 vec![&b"CF.ADDNX"[..], b"s", b"x"],
13277 vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
13278 vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
13279 vec![&b"CF.INFO"[..], b"s"],
13280 vec![&b"CF.DEBUG"[..], b"s"],
13281 vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
13282 vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
13283 vec![&b"CF.RESERVE"[..], b"s", b"64"],
13284 ] {
13285 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13286 assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
13287 }
13288 }
13289
13290 #[test]
13293 fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
13294 let mut f = Fixture::new();
13295 assert_eq!(
13296 f.run(&[
13297 b"CF.RESERVE",
13298 b"r",
13299 b"64",
13300 b"BUCKETSIZE",
13301 b"1",
13302 b"MAXITERATIONS",
13303 b"7",
13304 b"EXPANSION",
13305 b"4"
13306 ]),
13307 "+OK\r\n"
13308 );
13309 assert_eq!(
13310 f.run(&[b"CF.DEBUG", b"r"]),
13311 "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13312 max_iterations:7 expansion:4\r\n"
13313 );
13314 assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13315
13316 assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13317 assert_eq!(
13318 f.run(&[b"CF.RESERVE", b"q", b"1"]),
13319 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13320 );
13321 assert_eq!(
13324 f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13325 "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13326 );
13327 assert_eq!(
13328 f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13329 "+OK\r\n"
13330 );
13331
13332 assert_eq!(
13337 f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13338 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13339 );
13340 assert_eq!(
13341 f.run(&[
13342 b"CF.RESERVE",
13343 b"q2",
13344 b"64",
13345 b"EXPANSION",
13346 b"xx",
13347 b"BUCKETSIZE",
13348 b"0"
13349 ]),
13350 "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13351 );
13352 assert_eq!(
13353 f.run(&[
13354 b"CF.RESERVE",
13355 b"q2",
13356 b"64",
13357 b"MAXITERATIONS",
13358 b"0",
13359 b"BUCKETSIZE",
13360 b"0"
13361 ]),
13362 "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13363 );
13364 assert_eq!(
13368 f.run(&[
13369 b"CF.RESERVE",
13370 b"q3",
13371 b"64",
13372 b"BUCKETSIZE",
13373 b"1",
13374 b"BUCKETSIZE",
13375 b"0"
13376 ]),
13377 "+OK\r\n"
13378 );
13379 assert_eq!(
13382 f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13383 "+OK\r\n"
13384 );
13385 assert_eq!(
13386 f.run(&[b"CF.DEBUG", b"q4"]),
13387 "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13388 max_iterations:20 expansion:1\r\n"
13389 );
13390 assert!(
13393 f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13394 .contains("wrong number of arguments")
13395 );
13396 }
13397
13398 #[test]
13401 fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13402 let mut f = Fixture::new();
13403 assert_eq!(
13404 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13405 "*2\r\n:1\r\n:1\r\n"
13406 );
13407 assert_eq!(
13408 f.run(&[b"CF.DEBUG", b"i"]),
13409 "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13410 max_iterations:20 expansion:1\r\n"
13411 );
13412 assert_eq!(
13415 f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13416 "*2\r\n:0\r\n:1\r\n"
13417 );
13418 assert_eq!(
13419 f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13420 "-ERR not found\r\n"
13421 );
13422 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13423
13424 assert_eq!(
13425 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13426 "-Bad capacity\r\n"
13427 );
13428 assert_eq!(
13431 f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13432 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13433 );
13434 assert_eq!(
13438 f.run(&[
13439 b"CF.INSERT",
13440 b"i",
13441 b"CAPACITY",
13442 b"8",
13443 b"CAPACITY",
13444 b"2",
13445 b"ITEMS",
13446 b"a"
13447 ]),
13448 "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13449 );
13450 assert_eq!(
13454 f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13455 "*1\r\n:1\r\n"
13456 );
13457 assert_eq!(
13458 f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13459 "*1\r\n:1\r\n"
13460 );
13461 assert_eq!(
13462 f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13463 "-Unknown argument received\r\n"
13464 );
13465 assert_eq!(
13467 f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13468 "*1\r\n:1\r\n"
13469 );
13470 assert!(
13472 f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13473 .contains("wrong number of arguments")
13474 );
13475 assert!(
13476 f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13477 .contains("wrong number of arguments")
13478 );
13479 }
13480
13481 #[test]
13484 fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13485 let mut f = Fixture::new();
13486 f.run(&[
13487 b"CF.RESERVE",
13488 b"s",
13489 b"4",
13490 b"BUCKETSIZE",
13491 b"1",
13492 b"EXPANSION",
13493 b"0",
13494 ]);
13495 for i in 0..4u32 {
13496 assert_eq!(
13497 f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13498 ":1\r\n"
13499 );
13500 }
13501 assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13502 assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13503 assert_eq!(
13506 f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13507 "*2\r\n:-1\r\n:-1\r\n"
13508 );
13509 assert_eq!(
13510 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13511 "*2\r\n:0\r\n:-1\r\n"
13512 );
13513
13514 let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13520 assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13521 assert_eq!(
13522 f.run(&[b"CF.ADD", b"g", b"q"]),
13523 "-Maximum expansions reached\r\n"
13524 );
13525 assert_eq!(
13526 f.run(&[b"CF.INFO", b"g"]),
13527 "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13528 +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13529 +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13530 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13531 );
13532 }
13533
13534 #[test]
13538 fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13539 let mut f = Fixture::new();
13540 f.run(&[
13541 b"CF.RESERVE",
13542 b"src",
13543 b"8",
13544 b"BUCKETSIZE",
13545 b"2",
13546 b"EXPANSION",
13547 b"2",
13548 ]);
13549 for i in 0..40u32 {
13550 f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13551 }
13552 let mut pos = b"0".to_vec();
13556 let mut chunks = 0;
13557 loop {
13558 let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13559 let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13560 let next = head
13561 .split("\r\n")
13562 .nth(1)
13563 .and_then(|n| n.strip_prefix(':'))
13564 .expect("a two element reply of a position and a chunk")
13565 .to_owned();
13566 if next == "0" {
13567 assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13568 break;
13569 }
13570 let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13571 let at = body
13572 .windows(2)
13573 .position(|w| w == b"\r\n")
13574 .expect("a length line")
13575 + 2;
13576 let data = &body[at..body.len() - 2];
13577 assert_eq!(
13578 f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13579 "+OK\r\n",
13580 "loading chunk {chunks}"
13581 );
13582 pos = next.into_bytes();
13583 chunks += 1;
13584 }
13585 assert!(chunks >= 2, "a header and at least one chunk");
13586
13587 assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13588 assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13589 for i in 0..40u32 {
13590 assert_eq!(
13591 f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13592 ":1\r\n"
13593 );
13594 }
13595
13596 f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13599 assert_eq!(
13600 f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13601 "*2\r\n:0\r\n$-1\r\n"
13602 );
13603
13604 assert_eq!(
13608 f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13609 "-Invalid position\r\n"
13610 );
13611 assert_eq!(
13612 f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13613 "-Invalid position\r\n"
13614 );
13615 assert_eq!(
13616 f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13617 "-Invalid position\r\n"
13618 );
13619 assert_eq!(
13620 f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13621 "-Couldn't load chunk!\r\n"
13622 );
13623 let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13625 assert_eq!(
13626 f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13627 "-ERR item exists\r\n"
13628 );
13629 assert_eq!(
13633 f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13634 "-Invalid header\r\n"
13635 );
13636 for (why, bad) in [
13637 ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13638 ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13639 (
13640 "a bucket count that is not a power of two",
13641 cf_header(0, 3, 0, 1, [2, 20, 1]),
13642 ),
13643 ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13644 ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13645 (
13646 "a growth nobody could reach",
13647 cf_header(0, 8, 0, 1, [2, 20, 32769]),
13648 ),
13649 (
13650 "a chain that cannot grow and did",
13651 cf_header(0, 8, 0, 2, [2, 20, 0]),
13652 ),
13653 (
13656 "a filter count that wraps",
13657 cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13658 ),
13659 ] {
13660 assert_eq!(
13661 f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13662 "-Couldn't create filter!\r\n",
13663 "{why}"
13664 );
13665 }
13666 }
13667
13668 #[test]
13671 fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13672 let mut f = Fixture::new();
13673 f.out.set_proto(Proto::Resp3);
13674 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13675 assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13676 assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13677 assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13678 assert_eq!(
13679 f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13680 "*2\r\n#t\r\n#f\r\n"
13681 );
13682 assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13683 assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13684 assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13686 assert_eq!(
13687 f.run(&[b"CF.INFO", b"c"]),
13688 "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13689 +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13690 +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13691 +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13692 );
13693
13694 f.run(&[
13699 b"CF.RESERVE",
13700 b"s",
13701 b"4",
13702 b"BUCKETSIZE",
13703 b"1",
13704 b"EXPANSION",
13705 b"0",
13706 ]);
13707 assert_eq!(
13708 f.run(&[
13709 b"CF.INSERT",
13710 b"s",
13711 b"ITEMS",
13712 b"a",
13713 b"b",
13714 b"c",
13715 b"d",
13716 b"e",
13717 b"f"
13718 ]),
13719 "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13720 );
13721 assert_eq!(
13722 f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13723 "*2\r\n:0\r\n:-1\r\n"
13724 );
13725 assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13726 assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13729 }
13730
13731 #[test]
13736 fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13737 let mut f = Fixture::new();
13738 assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13739 assert_eq!(
13740 f.run(&[b"CMS.INFO", b"d"]),
13741 "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13742 );
13743 assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13744 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13745 assert_eq!(
13748 f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13749 "+OK\r\n"
13750 );
13751 assert_eq!(
13752 f.run(&[b"CMS.INFO", b"p"]),
13753 "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13754 );
13755 assert_eq!(
13758 f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13759 "-CMS: key already exists\r\n"
13760 );
13761 assert_eq!(
13762 f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13763 "-CMS: invalid width\r\n"
13764 );
13765 assert_eq!(
13766 f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13767 "-CMS: invalid depth\r\n"
13768 );
13769 assert_eq!(
13770 f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13771 "-CMS: invalid overestimation value\r\n"
13772 );
13773 assert_eq!(
13774 f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13775 "-CMS: invalid prob value\r\n"
13776 );
13777 assert_eq!(
13781 f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13782 "-CMS: invalid init arguments\r\n"
13783 );
13784 assert_eq!(
13788 f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13789 "-CMS: Insufficient memory to create the key\r\n"
13790 );
13791 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13792 }
13793
13794 #[test]
13797 fn increments_are_parsed_whole_and_the_counters_saturate() {
13798 let mut f = Fixture::new();
13799 f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13800 assert_eq!(
13801 f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13802 "*2\r\n:3\r\n:4\r\n"
13803 );
13804 assert_eq!(
13807 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13808 "*2\r\n:4\r\n:5\r\n"
13809 );
13810 assert_eq!(
13812 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13813 "-CMS: Cannot parse number\r\n"
13814 );
13815 assert_eq!(
13816 f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
13817 "-CMS: Number cannot be negative\r\n"
13818 );
13819 assert_eq!(
13820 f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
13821 "*2\r\n:5\r\n:4\r\n"
13822 );
13823 f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
13826 assert_eq!(
13827 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
13828 "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
13829 );
13830 assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
13831 f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
13834 f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
13835 f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
13836 assert_eq!(
13837 f.run(&[b"CMS.INFO", b"w"]),
13838 "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
13839 );
13840 assert!(
13843 f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
13844 .contains("wrong number of arguments")
13845 );
13846 assert_eq!(
13847 f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
13848 "-CMS: key does not exist\r\n"
13849 );
13850 assert_eq!(
13851 f.run(&[b"CMS.QUERY", b"nope", b"a"]),
13852 "-CMS: key does not exist\r\n"
13853 );
13854 }
13855
13856 #[test]
13859 fn a_merge_lands_whole_or_not_at_all() {
13860 let mut f = Fixture::new();
13861 for name in [&b"m1"[..], b"m2", b"dst"] {
13862 f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
13863 }
13864 f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
13865 f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
13866 assert_eq!(
13867 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13868 "+OK\r\n"
13869 );
13870 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13871 assert_eq!(
13874 f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13875 "+OK\r\n"
13876 );
13877 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13878 assert_eq!(
13879 f.run(&[
13880 b"CMS.MERGE",
13881 b"dst",
13882 b"2",
13883 b"m1",
13884 b"m2",
13885 b"WEIGHTS",
13886 b"2",
13887 b"3"
13888 ]),
13889 "+OK\r\n"
13890 );
13891 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13892 assert_eq!(
13895 f.run(&[
13896 b"CMS.MERGE",
13897 b"dst",
13898 b"1",
13899 b"m1",
13900 b"WEIGHTS",
13901 b"4611686018427387904"
13902 ]),
13903 "-CMS: MERGE overflow\r\n"
13904 );
13905 assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13906 f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
13909 assert_eq!(
13910 f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
13911 "-CMS: key does not exist\r\n"
13912 );
13913 assert_eq!(
13914 f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
13915 "-CMS: Number of keys must be positive\r\n"
13916 );
13917 assert_eq!(
13918 f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
13919 "-CMS: wrong number of keys\r\n"
13920 );
13921 assert_eq!(
13922 f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
13923 "-CMS: wrong number of keys/weights\r\n"
13924 );
13925 assert_eq!(
13926 f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
13927 "-CMS: width/depth is not equal\r\n"
13928 );
13929 assert_eq!(
13930 f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
13931 "-CMS: key does not exist\r\n"
13932 );
13933 }
13934
13935 #[test]
13938 fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13939 let mut f = Fixture::new();
13940 f.run(&[b"SET", b"s", b"text"]);
13941 for cmd in [
13942 vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
13943 vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
13944 vec![&b"CMS.QUERY"[..], b"s", b"a"],
13945 vec![&b"CMS.INFO"[..], b"s"],
13946 vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
13947 ] {
13948 let name = String::from_utf8_lossy(cmd[0]).into_owned();
13949 let reply = f.run(&cmd);
13950 assert!(
13953 reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
13954 "{name}: {reply}"
13955 );
13956 }
13957 f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
13958 assert_eq!(
13962 f.run(&[b"COPY", b"c", b"c2"]),
13963 "-ERR not supported for this module key\r\n"
13964 );
13965 assert_eq!(
13966 f.run(&[b"DUMP", b"c"]),
13967 "-ERR DUMP is not supported for this module key\r\n"
13968 );
13969 f.run(&[b"G.NADD", b"g", b"a"]);
13971 assert_eq!(
13972 f.run(&[b"COPY", b"g", b"g2"]),
13973 "-ERR COPY is not supported for a graph\r\n"
13974 );
13975 assert_eq!(
13976 f.run(&[b"DUMP", b"g"]),
13977 "-ERR DUMP is not supported for a graph\r\n"
13978 );
13979 assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
13982 assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
13983 assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
13984 assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
13985 assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
13986 }
13987
13988 #[test]
13993 fn a_reserve_takes_three_arguments_or_six() {
13994 let mut f = Fixture::new();
13995 assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
13996 assert_eq!(
13997 f.run(&[b"TOPK.INFO", b"t"]),
13998 "*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"
13999 );
14000 for cmd in [
14003 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
14004 vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
14005 ] {
14006 assert!(f.run(&cmd).contains("wrong number of arguments"));
14007 }
14008 assert_eq!(
14009 f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
14010 "+OK\r\n"
14011 );
14012 assert_eq!(
14015 f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
14016 "-TopK: key already exists\r\n"
14017 );
14018 assert_eq!(
14019 f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
14020 "-TopK: invalid k\r\n"
14021 );
14022 assert_eq!(
14023 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
14024 "-TopK: invalid width\r\n"
14025 );
14026 assert_eq!(
14027 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
14028 "-TopK: invalid depth\r\n"
14029 );
14030 assert_eq!(
14032 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
14033 "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
14034 );
14035 assert_eq!(
14036 f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
14037 "+OK\r\n"
14038 );
14039 assert_eq!(
14041 f.run(&[
14042 b"TOPK.RESERVE",
14043 b"w",
14044 b"1",
14045 b"4294967295",
14046 b"4294967295",
14047 b"0.9"
14048 ]),
14049 "-ERR Insufficient memory to create topk data structure\r\n"
14050 );
14051 }
14052
14053 #[test]
14055 fn the_kept_set_is_what_query_and_list_answer_from() {
14056 let mut f = Fixture::new();
14057 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
14058 assert_eq!(
14061 f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
14062 "*2\r\n$-1\r\n$-1\r\n"
14063 );
14064 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
14065 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
14068 assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
14069 assert_eq!(
14070 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
14071 "*3\r\n:1\r\n:0\r\n:1\r\n"
14072 );
14073 assert_eq!(
14075 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14076 "*3\r\n:11\r\n:1\r\n:6\r\n"
14077 );
14078 assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
14079 assert_eq!(
14080 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
14081 "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
14082 );
14083 assert_eq!(
14086 f.run(&[b"TOPK.LIST", b"t", b"w"]),
14087 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14088 );
14089 assert_eq!(
14090 f.run(&[b"TOPK.LIST", b"t", b""]),
14091 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14092 );
14093 assert_eq!(
14094 f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
14095 "-WITHCOUNT keyword expected\r\n"
14096 );
14097 assert_eq!(
14100 f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
14101 "-WITHCOUNT keyword expected\r\n"
14102 );
14103 assert_eq!(
14104 f.run(&[b"TOPK.LIST", b"missing"]),
14105 "-TopK: key does not exist\r\n"
14106 );
14107 f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
14109 assert_eq!(
14110 f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
14111 "*1\r\n$-1\r\n"
14112 );
14113 assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
14114 assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
14115 }
14116
14117 #[test]
14120 fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
14121 let mut f = Fixture::new();
14122 f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
14123 assert_eq!(
14127 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
14128 format!(
14129 "*2\r\n$-1\r\n-{}\r\n",
14130 "TopK: increment must be an integer greater or equal to 0 and smaller or equal to 100,000"
14131 )
14132 );
14133 assert_eq!(
14134 f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14135 "*3\r\n:3\r\n:0\r\n:0\r\n"
14136 );
14137 assert_eq!(
14139 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
14140 "*1\r\n$-1\r\n"
14141 );
14142 assert!(
14143 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
14144 .contains("smaller or equal to 100,000")
14145 );
14146 assert!(
14148 f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
14149 .contains("wrong number of arguments")
14150 );
14151 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
14152 }
14153
14154 #[test]
14156 fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
14157 let mut f = Fixture::new();
14158 f.run(&[b"HELLO", b"3"]);
14159 f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
14160 f.run(&[b"TOPK.ADD", b"t", b"a"]);
14161 assert_eq!(
14162 f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
14163 "*2\r\n#t\r\n#f\r\n"
14164 );
14165 assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
14167 assert_eq!(
14168 f.run(&[b"TOPK.INFO", b"t"]),
14169 "%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"
14170 );
14171 assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
14172 }
14173
14174 #[test]
14177 fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14178 let mut f = Fixture::new();
14179 f.run(&[b"SET", b"s", b"text"]);
14180 for cmd in [
14181 vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
14182 vec![&b"TOPK.ADD"[..], b"s", b"a"],
14183 vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
14184 vec![&b"TOPK.QUERY"[..], b"s", b"a"],
14185 vec![&b"TOPK.COUNT"[..], b"s", b"a"],
14186 vec![&b"TOPK.LIST"[..], b"s"],
14187 vec![&b"TOPK.INFO"[..], b"s"],
14188 ] {
14189 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14190 let reply = f.run(&cmd);
14191 assert!(
14192 reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
14193 "{name}: {reply}"
14194 );
14195 }
14196 f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
14197 assert_eq!(
14198 f.run(&[b"COPY", b"t", b"t2"]),
14199 "-ERR not supported for this module key\r\n"
14200 );
14201 assert_eq!(
14202 f.run(&[b"DUMP", b"t"]),
14203 "-ERR DUMP is not supported for this module key\r\n"
14204 );
14205 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14206 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14207 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14208 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
14209 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14210 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14211 assert_eq!(
14214 f.run(&[b"TOPK.INFO", b"t3"]),
14215 "-TopK: key does not exist\r\n"
14216 );
14217 }
14218
14219 #[test]
14224 fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
14225 let mut f = Fixture::new();
14226 assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
14227 assert_eq!(
14229 f.run(&[b"TDIGEST.INFO", b"t"]),
14230 "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
14231 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
14232 +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
14233 );
14234 assert_eq!(
14235 f.run(&[b"TDIGEST.CREATE", b"t"]),
14236 "-ERR T-Digest: key already exists\r\n"
14237 );
14238 assert!(
14240 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
14241 .contains("wrong number of arguments")
14242 );
14243 assert_eq!(
14244 f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
14245 "+OK\r\n"
14246 );
14247 assert_eq!(
14248 f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
14249 "+OK\r\n"
14250 );
14251 assert_eq!(
14255 f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
14256 "-ERR T-Digest: error parsing compression parameter\r\n"
14257 );
14258 assert_eq!(
14259 f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
14260 "-ERR T-Digest: wrong keyword\r\n"
14261 );
14262 assert_eq!(
14263 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
14264 "-ERR T-Digest: error parsing compression parameter\r\n"
14265 );
14266 assert_eq!(
14267 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
14268 "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
14269 );
14270 assert_eq!(
14273 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
14274 "-ERR T-Digest: allocation failed\r\n"
14275 );
14276 assert_eq!(
14278 f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
14279 "-ERR T-Digest: allocation failed\r\n"
14280 );
14281 assert_eq!(
14284 f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
14285 "-ERR T-Digest: key already exists\r\n"
14286 );
14287 }
14288
14289 #[test]
14292 fn the_quantile_family_answers_what_the_module_answers() {
14293 let mut f = Fixture::new();
14294 f.run(&[b"TDIGEST.CREATE", b"s"]);
14295 assert_eq!(
14296 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
14297 "+OK\r\n"
14298 );
14299 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
14300 assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
14301 assert_eq!(
14303 f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
14304 "*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"
14305 );
14306 assert_eq!(
14307 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14308 "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14309 );
14310 assert_eq!(
14313 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14314 "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14315 );
14316 assert_eq!(
14317 f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14318 "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14319 );
14320 assert_eq!(
14321 f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14322 "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14323 );
14324 assert_eq!(
14325 f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14326 "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14327 );
14328 assert_eq!(
14329 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14330 "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14331 );
14332 assert_eq!(
14333 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14334 "$3\r\n2.5\r\n"
14335 );
14336 assert_eq!(
14337 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14338 "$3\r\n2.5\r\n"
14339 );
14340 assert_eq!(
14342 f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14343 "-ERR T-Digest: quantile should be in [0,1]\r\n"
14344 );
14345 assert_eq!(
14346 f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14347 "-ERR T-Digest: error parsing quantile\r\n"
14348 );
14349 assert_eq!(
14350 f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14351 "-ERR T-Digest: error parsing cdf\r\n"
14352 );
14353 assert_eq!(
14354 f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14355 "-ERR T-Digest: error parsing value\r\n"
14356 );
14357 assert_eq!(
14358 f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14359 "-ERR T-Digest: rank needs to be non negative\r\n"
14360 );
14361 assert_eq!(
14362 f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14363 "-ERR T-Digest: error parsing rank\r\n"
14364 );
14365 assert_eq!(
14368 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14369 "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14370 );
14371 assert_eq!(
14372 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14373 "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14374 );
14375 assert_eq!(
14376 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14377 "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14378 );
14379 assert_eq!(
14380 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14381 "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14382 );
14383 }
14384
14385 #[test]
14388 fn an_empty_digest_has_an_answer_for_everything() {
14389 let mut f = Fixture::new();
14390 f.run(&[b"TDIGEST.CREATE", b"e"]);
14391 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14392 assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14393 assert_eq!(
14394 f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14395 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14396 );
14397 assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14398 assert_eq!(
14399 f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14400 "$3\r\nnan\r\n"
14401 );
14402 assert_eq!(
14405 f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14406 "*2\r\n:-2\r\n:-2\r\n"
14407 );
14408 assert_eq!(
14409 f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14410 "*2\r\n:-2\r\n:-2\r\n"
14411 );
14412 assert_eq!(
14413 f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14414 "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14415 );
14416 f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14418 assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14419 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14420 f.run(&[b"TDIGEST.CREATE", b"e2"]);
14423 assert_eq!(
14424 f.run(&[b"TDIGEST.INFO", b"e"]),
14425 f.run(&[b"TDIGEST.INFO", b"e2"])
14426 );
14427 }
14428
14429 #[test]
14432 fn a_sample_is_read_the_way_redis_reads_a_double() {
14433 let mut f = Fixture::new();
14434 f.run(&[b"TDIGEST.CREATE", b"a"]);
14435 for bad in [
14438 &b"nan"[..],
14439 b"1e400",
14440 b"-1e400",
14441 b"1e309",
14442 b"1e-400",
14443 b"",
14444 b" 1",
14445 b"1 ",
14446 b"1e",
14447 b"--1",
14448 ] {
14449 assert_eq!(
14450 f.run(&[b"TDIGEST.ADD", b"a", bad]),
14451 "-ERR T-Digest: error parsing val parameter\r\n",
14452 "{}",
14453 String::from_utf8_lossy(bad)
14454 );
14455 }
14456 for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14459 assert_eq!(
14460 f.run(&[b"TDIGEST.ADD", b"a", word]),
14461 "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14462 "{}",
14463 String::from_utf8_lossy(word)
14464 );
14465 }
14466 for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14469 assert_eq!(
14470 f.run(&[b"TDIGEST.ADD", b"a", good]),
14471 "+OK\r\n",
14472 "{}",
14473 String::from_utf8_lossy(good)
14474 );
14475 }
14476 assert!(
14478 f.run(&[b"TDIGEST.INFO", b"a"])
14479 .contains("Observations\r\n:6\r\n")
14480 );
14481 assert_eq!(
14484 f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14485 "-ERR T-Digest: error parsing val parameter\r\n"
14486 );
14487 assert!(
14488 f.run(&[b"TDIGEST.INFO", b"a"])
14489 .contains("Observations\r\n:6\r\n")
14490 );
14491 }
14492
14493 #[test]
14496 fn a_merge_sweeps_the_destination_between_its_inputs() {
14497 let mut f = Fixture::new();
14498 f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14499 f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14500 f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14501 f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14502 assert_eq!(
14503 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14504 "+OK\r\n"
14505 );
14506 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14511 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14512 assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14513 assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14514 assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14515 assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14516 assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14517 assert!(
14520 f.run(&[b"TDIGEST.INFO", b"m1"])
14521 .contains("Merged nodes\r\n:3\r\n")
14522 );
14523 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14526 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14527 assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14528 assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14529 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14532 let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14533 assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14534 assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14535 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14537 assert!(
14538 f.run(&[b"TDIGEST.INFO", b"d"])
14539 .contains("Compression\r\n:500\r\n")
14540 );
14541 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14543 assert!(
14544 f.run(&[b"TDIGEST.INFO", b"d"])
14545 .contains("Observations\r\n:12\r\n")
14546 );
14547 assert_eq!(
14549 f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14550 "-ERR T-Digest: error parsing numkeys\r\n"
14551 );
14552 assert_eq!(
14553 f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14554 "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14555 );
14556 assert!(
14557 f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14558 .contains("wrong number of arguments")
14559 );
14560 assert!(
14561 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14562 .contains("wrong number of arguments")
14563 );
14564 assert_eq!(
14565 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14566 "-ERR T-Digest: wrong keyword\r\n"
14567 );
14568 assert_eq!(
14571 f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14572 "-ERR T-Digest: key does not exist\r\n"
14573 );
14574 assert!(
14575 f.run(&[b"TDIGEST.INFO", b"d"])
14576 .contains("Observations\r\n:12\r\n")
14577 );
14578 assert_eq!(
14581 f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14582 "-ERR T-Digest: key does not exist\r\n"
14583 );
14584 }
14585
14586 #[test]
14588 fn a_digest_answers_doubles_and_a_map_on_resp3() {
14589 let mut f = Fixture::new();
14590 f.run(&[b"HELLO", b"3"]);
14591 f.run(&[b"TDIGEST.CREATE", b"s"]);
14592 f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14593 assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14594 assert_eq!(
14595 f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14596 "*2\r\n,1\r\n,4\r\n"
14597 );
14598 assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14599 assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14601 assert_eq!(
14602 f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14603 "*1\r\n,-inf\r\n"
14604 );
14605 f.run(&[b"TDIGEST.CREATE", b"e"]);
14606 assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14607 assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14609 assert_eq!(
14612 f.run(&[b"TDIGEST.INFO", b"s"]),
14613 "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14614 +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14615 +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14616 );
14617 }
14618
14619 #[test]
14622 fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14623 let mut f = Fixture::new();
14624 f.run(&[b"SET", b"s", b"text"]);
14625 for cmd in [
14626 vec![&b"TDIGEST.CREATE"[..], b"s"],
14627 vec![&b"TDIGEST.RESET"[..], b"s"],
14628 vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14629 vec![&b"TDIGEST.MIN"[..], b"s"],
14630 vec![&b"TDIGEST.MAX"[..], b"s"],
14631 vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14632 vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14633 vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14634 vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14635 vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14636 vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14637 vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14638 vec![&b"TDIGEST.INFO"[..], b"s"],
14639 ] {
14640 let name = String::from_utf8_lossy(cmd[0]).into_owned();
14641 let reply = f.run(&cmd);
14642 assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14643 }
14644 f.run(&[b"TDIGEST.CREATE", b"t"]);
14646 assert!(
14647 f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14648 .starts_with("-WRONGTYPE")
14649 );
14650 assert!(
14651 f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14652 .starts_with("-WRONGTYPE")
14653 );
14654 assert_eq!(
14655 f.run(&[b"COPY", b"t", b"t2"]),
14656 "-ERR not supported for this module key\r\n"
14657 );
14658 assert_eq!(
14659 f.run(&[b"DUMP", b"t"]),
14660 "-ERR DUMP is not supported for this module key\r\n"
14661 );
14662 assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14663 assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14664 assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14665 assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14666 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14667 assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14668 assert_eq!(
14671 f.run(&[b"TDIGEST.INFO", b"t3"]),
14672 "-ERR T-Digest: key does not exist\r\n"
14673 );
14674 assert_eq!(
14677 f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14678 "-ERR T-Digest: key does not exist\r\n"
14679 );
14680 }
14681
14682 fn without_memory(reply: &str) -> String {
14692 let head = "+memoryUsage\r\n:";
14693 let at = reply.find(head).expect("every TS.INFO reports memory");
14694 let rest = &reply[at + head.len()..];
14695 let end = rest.find("\r\n").expect("and it is a whole number");
14696 format!("{}{}", &reply[..at + head.len()], &rest[end..])
14697 }
14698
14699 #[test]
14702 fn a_series_is_made_empty_and_reports_on_itself() {
14703 let mut f = Fixture::new();
14704 assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
14705 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14706 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
14707 assert_eq!(
14711 without_memory(&f.run(&[b"TS.INFO", b"t"])),
14712 "*28\r\n\
14713 +totalSamples\r\n:0\r\n\
14714 +memoryUsage\r\n:\r\n\
14715 +firstTimestamp\r\n:0\r\n\
14716 +lastTimestamp\r\n:0\r\n\
14717 +retentionTime\r\n:0\r\n\
14718 +chunkCount\r\n:1\r\n\
14719 +chunkSize\r\n:4096\r\n\
14720 +chunkType\r\n+compressed\r\n\
14721 +duplicatePolicy\r\n+block\r\n\
14722 +labels\r\n*0\r\n\
14723 +sourceKey\r\n$-1\r\n\
14724 +rules\r\n*0\r\n\
14725 +ignoreMaxTimeDiff\r\n:0\r\n\
14726 +ignoreMaxValDiff\r\n$1\r\n0\r\n"
14727 );
14728 assert_eq!(
14731 f.run(&[b"TS.CREATE", b"t"]),
14732 "-ERR TSDB: key already exists\r\n"
14733 );
14734 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14735 assert_eq!(
14736 f.run(&[b"TS.CREATE", b"str"]),
14737 "-ERR TSDB: key already exists\r\n"
14738 );
14739 assert_eq!(
14742 f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
14743 "-ERR TSDB: Couldn't parse RETENTION\r\n"
14744 );
14745 let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14751 assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
14752 assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
14753 assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
14754 assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
14755 assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
14756 assert_eq!(
14757 f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
14758 "-ERR TSDB: the key is not a TSDB key\r\n"
14759 );
14760 assert_eq!(
14762 f.run(&[b"TS.INFO", b"nope"]),
14763 "-ERR TSDB: the key does not exist\r\n"
14764 );
14765 assert_eq!(
14766 f.run(&[b"TS.GET", b"nope"]),
14767 "-ERR TSDB: the key does not exist\r\n"
14768 );
14769 assert_eq!(
14770 f.run(&[b"TS.ALTER", b"nope"]),
14771 "-ERR TSDB: the key does not exist\r\n"
14772 );
14773 assert_eq!(
14774 f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
14775 "-ERR TSDB: the key does not exist\r\n"
14776 );
14777 }
14778
14779 #[test]
14782 fn the_options_are_a_keyword_scan_and_not_a_grammar() {
14783 let mut f = Fixture::new();
14784 assert_eq!(
14785 f.run(&[
14786 b"TS.CREATE",
14787 b"t",
14788 b"RETENTION",
14789 b"5000",
14790 b"ENCODING",
14791 b"UNCOMPRESSED",
14792 b"CHUNK_SIZE",
14793 b"128",
14794 b"DUPLICATE_POLICY",
14795 b"LAST",
14796 b"IGNORE",
14797 b"10",
14798 b"0.5",
14799 b"LABELS",
14800 b"room",
14801 b"kitchen"
14802 ]),
14803 "+OK\r\n"
14804 );
14805 let info = f.run(&[b"TS.INFO", b"t"]);
14806 assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
14807 assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
14808 assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
14809 assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
14810 assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
14811 assert!(
14814 info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
14815 "{info}"
14816 );
14817 assert!(
14818 info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
14819 "{info}"
14820 );
14821
14822 assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
14824 assert_eq!(
14828 f.run(&[
14829 b"TS.CREATE",
14830 b"g",
14831 b"LABELS",
14832 b"a",
14833 b"b",
14834 b"RETENTION",
14835 b"5"
14836 ]),
14837 "+OK\r\n"
14838 );
14839 let greedy = f.run(&[b"TS.INFO", b"g"]);
14840 assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
14841 assert!(
14842 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"),
14843 "{greedy}"
14844 );
14845
14846 assert_eq!(
14848 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
14849 "-ERR TSDB: Couldn't parse LABELS\r\n"
14850 );
14851 assert_eq!(
14852 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
14853 "-ERR TSDB: Couldn't parse LABELS\r\n"
14854 );
14855 assert_eq!(
14856 f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
14857 "-ERR TSDB: Couldn't parse RETENTION\r\n"
14858 );
14859 assert_eq!(
14862 f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
14863 "-TSDB: Couldn't parse RETENTION\r\n"
14864 );
14865 assert_eq!(
14866 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
14867 "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
14868 );
14869 assert_eq!(
14870 f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
14871 "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
14872 );
14873 assert_eq!(
14874 f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
14875 "-ERR TSDB: unknown ENCODING parameter\r\n"
14876 );
14877 assert!(
14880 f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
14881 .contains("wrong number of arguments for 'ts.create' command")
14882 );
14883 assert_eq!(
14884 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
14885 "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
14886 );
14887 assert_eq!(
14888 f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
14889 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14890 );
14891 assert_eq!(
14892 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
14893 "-ERR TSDB: Couldn't parse IGNORE\r\n"
14894 );
14895 assert_eq!(
14896 f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
14897 "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
14898 );
14899 assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
14900
14901 assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
14904 let after = f.run(&[b"TS.INFO", b"t"]);
14905 assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
14906 assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
14907 assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
14908 assert_eq!(
14909 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
14910 "-ERR TSDB: unknown ENCODING parameter\r\n"
14911 );
14912 assert_eq!(
14914 f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
14915 "+OK\r\n"
14916 );
14917 assert!(
14918 f.run(&[b"TS.INFO", b"t"])
14919 .contains("+chunkType\r\n+uncompressed\r\n")
14920 );
14921 }
14922
14923 #[test]
14926 fn samples_land_where_they_are_put_and_the_newest_comes_back() {
14927 let mut f = Fixture::new();
14928 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
14929 assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14931 assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
14932 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
14935 assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
14936 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
14937 assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
14940 assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
14941
14942 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14945 assert_eq!(
14946 f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
14947 "-ERR TSDB: invalid value\r\n"
14948 );
14949 for bad in [
14953 &b".5"[..],
14954 b"1.",
14955 b"+1",
14956 b" 1",
14957 b"0x10",
14958 b"inf",
14959 b"1e400",
14960 b"--1",
14961 b"1e",
14962 ] {
14963 assert_eq!(
14964 f.run(&[b"TS.ADD", b"v", b"1", bad]),
14965 "-ERR TSDB: invalid value\r\n",
14966 "{}",
14967 String::from_utf8_lossy(bad)
14968 );
14969 }
14970 assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
14972
14973 assert_eq!(
14976 f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
14977 "-ERR TSDB: invalid timestamp\r\n"
14978 );
14979 assert_eq!(
14980 f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
14981 "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
14982 );
14983
14984 assert_eq!(
14987 f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
14988 "-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"
14989 );
14990 assert_eq!(
14991 f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
14992 ":300\r\n"
14993 );
14994 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
14995 assert_eq!(
14998 f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
14999 ":1\r\n"
15000 );
15001 assert_eq!(
15002 f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
15003 "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
15004 );
15005
15006 assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
15009 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
15010 assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
15011 assert_eq!(
15012 f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
15013 "-ERR TSDB: Timestamp is older than retention\r\n"
15014 );
15015 assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
15017 assert!(
15018 f.run(&[b"TS.INFO", b"r"])
15019 .contains("+totalSamples\r\n:1\r\n")
15020 );
15021
15022 assert_eq!(
15025 f.run(&[
15026 b"TS.CREATE",
15027 b"i",
15028 b"DUPLICATE_POLICY",
15029 b"LAST",
15030 b"IGNORE",
15031 b"10",
15032 b"0.5"
15033 ]),
15034 "+OK\r\n"
15035 );
15036 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
15037 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
15038 assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
15039 }
15040
15041 #[test]
15044 fn a_madd_answers_each_triple_and_creates_nothing() {
15045 let mut f = Fixture::new();
15046 assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
15047 assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
15048 assert_eq!(
15049 f.run(&[
15050 b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
15051 ]),
15052 "*3\r\n:100\r\n:100\r\n:200\r\n"
15053 );
15054 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15057 assert_eq!(
15058 f.run(&[
15059 b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
15060 ]),
15061 "*3\r\n\
15062 -ERR TSDB: the key is not a TSDB key\r\n\
15063 -ERR TSDB: the key is not a TSDB key\r\n\
15064 :300\r\n"
15065 );
15066 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
15067 assert_eq!(
15069 f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
15070 "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
15071 );
15072 assert!(
15074 f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
15075 .contains("wrong number of arguments for 'ts.madd' command")
15076 );
15077 }
15078
15079 #[test]
15081 fn an_increment_walks_the_newest_value_up_and_down() {
15082 let mut f = Fixture::new();
15083 assert_eq!(
15084 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15085 ":100\r\n"
15086 );
15087 assert_eq!(
15088 f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15089 ":100\r\n"
15090 );
15091 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
15094 assert_eq!(
15095 f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
15096 ":200\r\n"
15097 );
15098 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
15099 assert_eq!(
15102 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
15103 "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
15104 );
15105 assert_eq!(
15109 f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
15110 ":1\r\n"
15111 );
15112 assert_eq!(
15113 f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
15114 ":1\r\n"
15115 );
15116 assert_eq!(
15117 f.run(&[b"TS.INCRBY", b"t", b"nan"]),
15118 "-ERR TSDB: invalid increase/decrease value\r\n"
15119 );
15120 assert_eq!(
15121 f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
15122 "-ERR TSDB: invalid increase/decrease value\r\n"
15123 );
15124 assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15127 assert_eq!(
15128 f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
15129 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15130 );
15131 assert_eq!(
15136 f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
15137 "-ERR TSDB: invalid timestamp\r\n"
15138 );
15139 assert_eq!(
15142 f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
15143 format!(":{}\r\n", f.server.now_ms())
15144 );
15145 assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
15147 assert_eq!(
15148 f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
15149 "-ERR TSDB: cannot increment/decrement NaN value\r\n"
15150 );
15151 }
15152
15153 #[test]
15155 fn deleting_takes_out_a_span_and_answers_how_many_went() {
15156 let mut f = Fixture::new();
15157 for at in [b"100".as_slice(), b"200", b"300", b"400"] {
15158 f.run(&[b"TS.ADD", b"t", at, b"1"]);
15159 }
15160 assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
15161 assert!(
15162 f.run(&[b"TS.INFO", b"t"])
15163 .contains("+totalSamples\r\n:2\r\n")
15164 );
15165 assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
15167 assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
15169 let empty = f.run(&[b"TS.INFO", b"t"]);
15172 assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
15173 assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
15174 assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
15175 assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
15176 assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
15177 assert_eq!(
15179 f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
15180 "-ERR TSDB: wrong fromTimestamp\r\n"
15181 );
15182 assert_eq!(
15183 f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
15184 "-ERR TSDB: wrong toTimestamp\r\n"
15185 );
15186 assert_eq!(
15187 f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
15188 "-ERR TSDB: wrong fromTimestamp\r\n"
15189 );
15190 }
15191
15192 #[test]
15195 fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
15196 let mut f = Fixture::new();
15197 f.out = Out::new(Proto::Resp3);
15198 assert_eq!(
15199 f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
15200 "+OK\r\n"
15201 );
15202 assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
15203 assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
15205 assert_eq!(
15206 without_memory(&f.run(&[b"TS.INFO", b"t"])),
15207 "%14\r\n\
15208 +totalSamples\r\n:1\r\n\
15209 +memoryUsage\r\n:\r\n\
15210 +firstTimestamp\r\n:100\r\n\
15211 +lastTimestamp\r\n:100\r\n\
15212 +retentionTime\r\n:0\r\n\
15213 +chunkCount\r\n:1\r\n\
15214 +chunkSize\r\n:4096\r\n\
15215 +chunkType\r\n+compressed\r\n\
15216 +duplicatePolicy\r\n+block\r\n\
15217 +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
15218 +sourceKey\r\n_\r\n\
15219 +rules\r\n%0\r\n\
15220 +ignoreMaxTimeDiff\r\n:0\r\n\
15221 +ignoreMaxValDiff\r\n,0\r\n"
15222 );
15223 }
15224
15225 #[test]
15228 fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
15229 let mut f = Fixture::new();
15230 for (at, v) in [
15231 (b"100".as_slice(), b"1".as_slice()),
15232 (b"200", b"2"),
15233 (b"300", b"3"),
15234 (b"400", b"4"),
15235 ] {
15236 f.run(&[b"TS.ADD", b"t", at, v]);
15237 }
15238 assert_eq!(
15239 f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
15240 "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
15241 *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
15242 );
15243 assert_eq!(
15245 f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
15246 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15247 );
15248 assert_eq!(
15251 f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
15252 "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
15253 );
15254 assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
15256 assert_eq!(
15258 f.run(&[
15259 b"TS.RANGE",
15260 b"t",
15261 b"-",
15262 b"+",
15263 b"FILTER_BY_VALUE",
15264 b"2",
15265 b"3"
15266 ]),
15267 "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15268 );
15269 assert_eq!(
15270 f.run(&[
15271 b"TS.RANGE",
15272 b"t",
15273 b"-",
15274 b"+",
15275 b"FILTER_BY_TS",
15276 b"100",
15277 b"400"
15278 ]),
15279 "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
15280 );
15281 assert_eq!(
15283 f.run(&[
15284 b"TS.RANGE",
15285 b"t",
15286 b"-",
15287 b"+",
15288 b"ZZZ",
15289 b"FILTER_BY_TS",
15290 b"400"
15291 ]),
15292 "*1\r\n*2\r\n:400\r\n+4\r\n"
15293 );
15294 assert_eq!(
15296 f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
15297 "*1\r\n*2\r\n:100\r\n+1\r\n"
15298 );
15299 }
15300
15301 #[test]
15303 fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
15304 let mut f = Fixture::new();
15305 for (at, v) in [
15306 (b"100".as_slice(), b"1".as_slice()),
15307 (b"200", b"2"),
15308 (b"300", b"3"),
15309 (b"400", b"4"),
15310 ] {
15311 f.run(&[b"TS.ADD", b"t", at, v]);
15312 }
15313 assert_eq!(
15314 f.run(&[
15315 b"TS.RANGE",
15316 b"t",
15317 b"-",
15318 b"+",
15319 b"AGGREGATION",
15320 b"avg",
15321 b"200"
15322 ]),
15323 "*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"
15324 );
15325 assert_eq!(
15328 f.run(&[
15329 b"TS.RANGE",
15330 b"t",
15331 b"-",
15332 b"+",
15333 b"AGGREGATION",
15334 b"min,max,count",
15335 b"200"
15336 ]),
15337 "*3\r\n\
15338 *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15339 *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15340 *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15341 );
15342 assert_eq!(
15344 f.run(&[
15345 b"TS.RANGE",
15346 b"t",
15347 b"-",
15348 b"+",
15349 b"AGGREGATION",
15350 b"avg",
15351 b"200",
15352 b"BUCKETTIMESTAMP",
15353 b"+"
15354 ]),
15355 "*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"
15356 );
15357 assert_eq!(
15359 f.run(&[
15360 b"TS.RANGE",
15361 b"t",
15362 b"100",
15363 b"400",
15364 b"ALIGN",
15365 b"100",
15366 b"AGGREGATION",
15367 b"sum",
15368 b"200"
15369 ]),
15370 "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15371 );
15372 assert_eq!(
15375 f.run(&[
15376 b"TS.RANGE",
15377 b"t",
15378 b"-",
15379 b"+",
15380 b"AGGREGATION",
15381 b"count",
15382 b"200"
15383 ]),
15384 "*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"
15385 );
15386 assert_eq!(
15387 f.run(&[
15388 b"TS.RANGE",
15389 b"t",
15390 b"-",
15391 b"+",
15392 b"AGGREGATION",
15393 b"count",
15394 b"200",
15395 b"COUNT",
15396 b"1"
15397 ]),
15398 "*1\r\n*2\r\n:0\r\n+1\r\n"
15399 );
15400 }
15401
15402 #[test]
15405 fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15406 let mut f = Fixture::new();
15407 for (at, v) in [
15408 (b"0".as_slice(), b"1".as_slice()),
15409 (b"100", b"2"),
15410 (b"500", b"nan"),
15411 (b"600", b"3"),
15412 ] {
15413 f.run(&[b"TS.ADD", b"g", at, v]);
15414 }
15415 assert_eq!(
15418 f.run(&[
15419 b"TS.RANGE",
15420 b"g",
15421 b"-",
15422 b"+",
15423 b"AGGREGATION",
15424 b"avg",
15425 b"100"
15426 ]),
15427 "*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"
15428 );
15429 assert_eq!(
15431 f.run(&[
15432 b"TS.RANGE",
15433 b"g",
15434 b"-",
15435 b"+",
15436 b"AGGREGATION",
15437 b"sum",
15438 b"100",
15439 b"EMPTY"
15440 ]),
15441 "*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\
15442 *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\
15443 *2\r\n:600\r\n+3\r\n"
15444 );
15445 assert_eq!(
15450 f.run(&[
15451 b"TS.RANGE",
15452 b"g",
15453 b"-",
15454 b"+",
15455 b"AGGREGATION",
15456 b"last",
15457 b"100",
15458 b"EMPTY"
15459 ]),
15460 "*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\
15461 *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\
15462 *2\r\n:600\r\n+3\r\n"
15463 );
15464 assert_eq!(
15465 f.run(&[
15466 b"TS.REVRANGE",
15467 b"g",
15468 b"-",
15469 b"+",
15470 b"AGGREGATION",
15471 b"last",
15472 b"100",
15473 b"EMPTY"
15474 ]),
15475 "*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\
15476 *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\
15477 *2\r\n:0\r\n+1\r\n"
15478 );
15479 assert_eq!(
15482 f.run(&[
15483 b"TS.RANGE",
15484 b"g",
15485 b"500",
15486 b"600",
15487 b"AGGREGATION",
15488 b"last",
15489 b"100",
15490 b"EMPTY"
15491 ]),
15492 "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15493 );
15494 }
15495
15496 #[test]
15499 fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15500 let mut f = Fixture::new();
15501 f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15502 f.run(&[b"SET", b"str", b"x"]);
15503 let cases: &[(&[&[u8]], &str)] = &[
15504 (
15505 &[b"TS.RANGE", b"t"],
15506 "-ERR wrong number of arguments for 'ts.range' command\r\n",
15507 ),
15508 (
15510 &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15511 "-ERR TSDB: the key does not exist\r\n",
15512 ),
15513 (
15514 &[b"TS.RANGE", b"str", b"-", b"+"],
15515 "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15516 ),
15517 (
15518 &[b"TS.RANGE", b"t", b"abc", b"+"],
15519 "-ERR TSDB: wrong fromTimestamp\r\n",
15520 ),
15521 (
15522 &[b"TS.RANGE", b"t", b"-", b"abc"],
15523 "-ERR TSDB: wrong toTimestamp\r\n",
15524 ),
15525 (
15526 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15527 "-ERR TSDB: COUNT argument is missing\r\n",
15528 ),
15529 (
15530 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15531 "-ERR TSDB: Couldn't parse COUNT\r\n",
15532 ),
15533 (
15534 &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15535 "-ERR TSDB: Invalid COUNT value\r\n",
15536 ),
15537 (
15538 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15539 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15540 ),
15541 (
15542 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15543 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15544 ),
15545 (
15546 &[
15547 b"TS.RANGE",
15548 b"t",
15549 b"-",
15550 b"+",
15551 b"AGGREGATION",
15552 b"nope",
15553 b"100",
15554 ],
15555 "-ERR TSDB: Unknown aggregation type\r\n",
15556 ),
15557 (
15558 &[
15559 b"TS.RANGE",
15560 b"t",
15561 b"-",
15562 b"+",
15563 b"AGGREGATION",
15564 b"avg,,min",
15565 b"100",
15566 ],
15567 "-ERR TSDB: Empty aggregation type in list\r\n",
15568 ),
15569 (
15571 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15572 "-ERR TSDB: Unknown aggregation type\r\n",
15573 ),
15574 (
15575 &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15576 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15577 ),
15578 (
15579 &[
15580 b"TS.RANGE",
15581 b"t",
15582 b"-",
15583 b"+",
15584 b"AGGREGATION",
15585 b"avg",
15586 b"100",
15587 b"X",
15588 b"EMPTY",
15589 ],
15590 "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15591 ),
15592 (
15593 &[
15594 b"TS.RANGE",
15595 b"t",
15596 b"-",
15597 b"+",
15598 b"AGGREGATION",
15599 b"avg",
15600 b"100",
15601 b"BUCKETTIMESTAMP",
15602 b"z",
15603 ],
15604 "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15605 ),
15606 (
15607 &[
15608 b"TS.RANGE",
15609 b"t",
15610 b"-",
15611 b"+",
15612 b"AGGREGATION",
15613 b"avg",
15614 b"100",
15615 b"X",
15616 b"Y",
15617 b"BUCKETTIMESTAMP",
15618 b"-",
15619 ],
15620 "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15621 AGGREGATION flag\r\n",
15622 ),
15623 (
15624 &[
15625 b"TS.RANGE",
15626 b"t",
15627 b"-",
15628 b"+",
15629 b"ALIGN",
15630 b"z",
15631 b"AGGREGATION",
15632 b"avg",
15633 b"100",
15634 ],
15635 "-ERR TSDB: unknown ALIGN parameter\r\n",
15636 ),
15637 (
15638 &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15639 "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15640 ),
15641 (
15642 &[
15643 b"TS.RANGE",
15644 b"t",
15645 b"-",
15646 b"+",
15647 b"ALIGN",
15648 b"-",
15649 b"AGGREGATION",
15650 b"avg",
15651 b"100",
15652 ],
15653 "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15654 ),
15655 (
15656 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15657 "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15658 ),
15659 (
15660 &[
15661 b"TS.RANGE",
15662 b"t",
15663 b"-",
15664 b"+",
15665 b"FILTER_BY_VALUE",
15666 b"x",
15667 b"2",
15668 ],
15669 "-ERR TSDB: Couldn't parse MIN\r\n",
15670 ),
15671 (
15672 &[
15673 b"TS.RANGE",
15674 b"t",
15675 b"-",
15676 b"+",
15677 b"FILTER_BY_VALUE",
15678 b"1",
15679 b"y",
15680 ],
15681 "-ERR TSDB: Couldn't parse MAX\r\n",
15682 ),
15683 (
15684 &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15685 "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15686 ),
15687 ];
15688 for (argv, want) in cases {
15689 let got = f.run(argv);
15690 assert_eq!(&got, want, "{:?}", argv.last());
15691 }
15692 f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
15696 f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
15697 assert_eq!(
15698 f.run(&[
15699 b"TS.RANGE",
15700 b"wide",
15701 b"-",
15702 b"+",
15703 b"AGGREGATION",
15704 b"avg",
15705 b"1",
15706 b"EMPTY"
15707 ]),
15708 "-ERR TSDB: the requested range holds too many empty buckets\r\n"
15709 );
15710 }
15711
15712 #[test]
15714 fn resp3_writes_a_read_value_as_a_double() {
15715 let mut f = Fixture::new();
15716 f.out = Out::new(Proto::Resp3);
15717 for (at, v) in [
15718 (b"0".as_slice(), b"1".as_slice()),
15719 (b"100", b"2"),
15720 (b"500", b"nan"),
15721 (b"600", b"3"),
15722 ] {
15723 f.run(&[b"TS.ADD", b"g", at, v]);
15724 }
15725 assert_eq!(
15726 f.run(&[
15727 b"TS.RANGE",
15728 b"g",
15729 b"0",
15730 b"100",
15731 b"AGGREGATION",
15732 b"avg,min",
15733 b"200"
15734 ]),
15735 "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
15736 );
15737 assert_eq!(
15738 f.run(&[
15739 b"TS.RANGE",
15740 b"g",
15741 b"500",
15742 b"600",
15743 b"AGGREGATION",
15744 b"last",
15745 b"100",
15746 b"EMPTY"
15747 ]),
15748 "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
15749 );
15750 }
15751
15752 fn joined() -> Fixture {
15755 let mut f = Fixture::new();
15756 f.run(&[b"TS.CREATE", b"z"]);
15757 for (at, v) in [
15758 (b"10".as_slice(), b"1".as_slice()),
15759 (b"20", b"2"),
15760 (b"40", b"4"),
15761 (b"50", b"5"),
15762 ] {
15763 f.run(&[b"TS.ADD", b"x", at, v]);
15764 }
15765 for (at, v) in [
15766 (b"20".as_slice(), b"20".as_slice()),
15767 (b"30", b"30"),
15768 (b"50", b"50"),
15769 (b"60", b"60"),
15770 ] {
15771 f.run(&[b"TS.ADD", b"y", at, v]);
15772 }
15773 f
15774 }
15775
15776 #[test]
15780 fn an_nrange_joins_its_keys_on_the_timestamp() {
15781 let mut f = joined();
15782 assert_eq!(
15784 f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
15785 "*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\
15786 *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
15787 );
15788 assert_eq!(
15790 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
15791 "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
15792 *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15793 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15794 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15795 *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
15796 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15797 );
15798 assert_eq!(
15801 f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
15802 "*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"
15803 );
15804 assert_eq!(
15805 f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
15806 "*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"
15807 );
15808 assert_eq!(
15811 f.run(&[
15812 b"TS.NREVRANGE",
15813 b"2",
15814 b"x",
15815 b"y",
15816 b"-",
15817 b"+",
15818 b"COUNT",
15819 b"1"
15820 ]),
15821 "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15822 );
15823 assert_eq!(
15824 f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
15825 "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
15826 );
15827 assert_eq!(
15829 f.run(&[
15830 b"TS.NRANGE",
15831 b"2",
15832 b"x",
15833 b"y",
15834 b"-",
15835 b"+",
15836 b"FILTER_BY_VALUE",
15837 b"2",
15838 b"30"
15839 ]),
15840 "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15841 *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15842 *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15843 *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
15844 );
15845 }
15846
15847 #[test]
15851 fn an_nrange_aggregation_names_one_reduction_a_key() {
15852 let mut f = joined();
15853 assert_eq!(
15854 f.run(&[
15855 b"TS.NRANGE",
15856 b"2",
15857 b"x",
15858 b"y",
15859 b"-",
15860 b"+",
15861 b"AGGREGATION",
15862 b"sum",
15863 b"sum",
15864 b"20"
15865 ]),
15866 "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
15867 *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
15868 *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
15869 *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15870 );
15871 assert_eq!(
15873 f.run(&[
15874 b"TS.NRANGE",
15875 b"2",
15876 b"x",
15877 b"y",
15878 b"-",
15879 b"+",
15880 b"AGGREGATION",
15881 b"sum,count",
15882 b"avg",
15883 b"20"
15884 ]),
15885 "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
15886 *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
15887 *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
15888 *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
15889 );
15890 assert_eq!(
15893 f.run(&[
15894 b"TS.NRANGE",
15895 b"2",
15896 b"x",
15897 b"y",
15898 b"-",
15899 b"+",
15900 b"AGGREGATION",
15901 b"avg",
15902 b"sum",
15903 b"100",
15904 b"EMPTY",
15905 b"BUCKETTIMESTAMP",
15906 b"end"
15907 ]),
15908 "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
15909 );
15910 assert_eq!(
15913 f.run(&[
15914 b"TS.NRANGE",
15915 b"2",
15916 b"x",
15917 b"y",
15918 b"-",
15919 b"+",
15920 b"AGGREGATION",
15921 b"avg",
15922 b"COUNT",
15923 b"100"
15924 ]),
15925 "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
15926 );
15927 }
15928
15929 #[test]
15932 fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
15933 let mut f = joined();
15934 f.run(&[b"SET", b"str", b"hi"]);
15935 let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
15936 let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
15937 must be equal to numkeys\r\n";
15938 let cases: &[(&[&[u8]], &str)] = &[
15939 (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
15940 (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
15941 (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
15942 (
15945 &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
15946 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15947 ),
15948 (
15949 &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
15950 "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15951 ),
15952 (
15955 &[
15956 b"TS.NRANGE",
15957 b"2",
15958 b"x",
15959 b"y",
15960 b"abc",
15961 b"+",
15962 b"AGGREGATION",
15963 b"nope",
15964 b"sum",
15965 b"100",
15966 ],
15967 "-ERR TSDB: Unknown aggregation type\r\n",
15968 ),
15969 (
15970 &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
15971 "-ERR TSDB: wrong fromTimestamp\r\n",
15972 ),
15973 (
15974 &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
15975 "-ERR TSDB: wrong toTimestamp\r\n",
15976 ),
15977 (
15981 &[
15982 b"TS.NRANGE",
15983 b"2",
15984 b"x",
15985 b"y",
15986 b"-",
15987 b"+",
15988 b"AGGREGATION",
15989 b"avg",
15990 ],
15991 numkeys,
15992 ),
15993 (
15994 &[
15995 b"TS.NRANGE",
15996 b"2",
15997 b"x",
15998 b"y",
15999 b"-",
16000 b"+",
16001 b"AGGREGATION",
16002 b"100",
16003 b"sum",
16004 b"100",
16005 ],
16006 numkeys,
16007 ),
16008 (
16009 &[
16010 b"TS.NRANGE",
16011 b"2",
16012 b"x",
16013 b"y",
16014 b"-",
16015 b"+",
16016 b"AGGREGATION",
16017 b"avg",
16018 b"sum",
16019 b"sum",
16020 b"100",
16021 ],
16022 numkeys,
16023 ),
16024 (
16025 &[
16026 b"TS.NRANGE",
16027 b"2",
16028 b"x",
16029 b"y",
16030 b"-",
16031 b"+",
16032 b"AGGREGATION",
16033 b"avg",
16034 b"sum",
16035 b"abc",
16036 ],
16037 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16038 ),
16039 (
16040 &[
16041 b"TS.NRANGE",
16042 b"2",
16043 b"x",
16044 b"y",
16045 b"-",
16046 b"+",
16047 b"AGGREGATION",
16048 b"avg",
16049 b"sum",
16050 b"0",
16051 ],
16052 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16053 ),
16054 (
16057 &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
16058 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16059 ),
16060 (
16061 &[
16062 b"TS.NRANGE",
16063 b"1",
16064 b"x",
16065 b"-",
16066 b"+",
16067 b"AGGREGATION",
16068 b"100",
16069 b"200",
16070 ],
16071 "-ERR TSDB: Unknown aggregation type\r\n",
16072 ),
16073 (
16075 &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
16076 "-ERR TSDB: the key does not exist\r\n",
16077 ),
16078 (
16079 &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
16080 "-ERR WRONGTYPE Operation against a key \
16081 holding the wrong kind of value\r\n",
16082 ),
16083 ];
16084 for (argv, want) in cases {
16085 let got = f.run(argv);
16086 assert_eq!(&got, want, "{argv:?}");
16087 }
16088 }
16089
16090 #[test]
16092 fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
16093 let mut f = joined();
16094 assert_eq!(
16095 f.run(&[b"TS.READ", b"x", b"-"]),
16096 "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
16097 *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16098 );
16099 assert_eq!(
16102 f.run(&[b"TS.READ", b"x", b"+"]),
16103 "*1\r\n*2\r\n:50\r\n+5\r\n"
16104 );
16105 assert_eq!(
16106 f.run(&[b"TS.READ", b"x", b"25"]),
16107 "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16108 );
16109 assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
16112 assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
16113 assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
16114 assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
16115 assert_eq!(
16119 f.run(&[b"TS.READ", b"x", b"abc"]),
16120 "-TSDB: invalid timestamp\r\n"
16121 );
16122 assert_eq!(
16123 f.run(&[b"TS.READ", b"x", b"-1"]),
16124 "-TSDB: invalid timestamp\r\n"
16125 );
16126 f.run(&[b"SET", b"str", b"hi"]);
16127 assert_eq!(
16128 f.run(&[b"TS.READ", b"str", b"-"]),
16129 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
16130 );
16131 assert_eq!(
16134 f.run(&[b"TS.READ", b"x"]),
16135 "-ERR wrong number of arguments for 'ts.read' command\r\n"
16136 );
16137 assert_eq!(
16138 f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
16139 "-ERR wrong number of arguments for 'ts.read' command\r\n"
16140 );
16141 }
16142
16143 #[test]
16146 fn getkeys_reads_the_count_of_a_joined_read() {
16147 let mut f = Fixture::new();
16148 assert_eq!(
16149 f.run(&[
16150 b"COMMAND",
16151 b"GETKEYS",
16152 b"TS.NRANGE",
16153 b"2",
16154 b"a",
16155 b"b",
16156 b"-",
16157 b"+"
16158 ]),
16159 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
16160 );
16161 assert_eq!(
16162 f.run(&[
16163 b"COMMAND",
16164 b"GETKEYS",
16165 b"TS.NREVRANGE",
16166 b"1",
16167 b"a",
16168 b"-",
16169 b"+"
16170 ]),
16171 "*1\r\n$1\r\na\r\n"
16172 );
16173 for n in [b"0".as_slice(), b"9", b"abc"] {
16176 assert_eq!(
16177 f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
16178 "-ERR Invalid arguments specified for command\r\n"
16179 );
16180 }
16181 }
16182
16183 fn labelled() -> Fixture {
16185 let mut f = Fixture::new();
16186 f.run(&[
16187 b"TS.CREATE",
16188 b"a",
16189 b"LABELS",
16190 b"room",
16191 b"kitchen",
16192 b"x",
16193 b"1",
16194 ]);
16195 f.run(&[
16196 b"TS.CREATE",
16197 b"b",
16198 b"LABELS",
16199 b"room",
16200 b"bedroom",
16201 b"x",
16202 b"2",
16203 ]);
16204 f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
16205 f.run(&[b"TS.CREATE", b"d"]);
16206 f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
16207 f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
16208 f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
16209 f
16210 }
16211
16212 #[test]
16215 fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
16216 let mut f = labelled();
16217 let cases: &[(&[&[u8]], &str)] = &[
16218 (
16221 &[b"TS.QUERYINDEX", b"room=kitchen"],
16222 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16223 ),
16224 (
16225 &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
16226 "*1\r\n$1\r\na\r\n",
16227 ),
16228 (
16229 &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
16230 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
16231 ),
16232 (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
16235 (
16237 &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
16238 "*1\r\n$1\r\nc\r\n",
16239 ),
16240 (
16241 &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
16242 "*1\r\n$1\r\na\r\n",
16243 ),
16244 (
16245 &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
16246 "-ERR TSDB: please provide at least one matcher\r\n",
16247 ),
16248 (
16251 &[b"TS.QUERYINDEX", b"room==kitchen"],
16252 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16253 ),
16254 (
16255 &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
16256 "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16257 ),
16258 (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
16259 (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
16262 (
16263 &[b"TS.QUERYINDEX", b"=(1)"],
16264 "-ERR TSDB: failed parsing labels\r\n",
16265 ),
16266 (
16267 &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
16268 "-ERR TSDB: failed parsing labels\r\n",
16269 ),
16270 (
16271 &[b"TS.QUERYINDEX", b"room=(kitchen"],
16272 "-ERR TSDB: failed parsing labels\r\n",
16273 ),
16274 (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
16275 (
16276 &[b"TS.QUERYINDEX", b"nonsense"],
16277 "-ERR TSDB: failed parsing labels\r\n",
16278 ),
16279 (
16281 &[b"TS.QUERYINDEX", b"room!=kitchen"],
16282 "-ERR TSDB: please provide at least one matcher\r\n",
16283 ),
16284 (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
16286 (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
16287 (
16288 &[b"TS.QUERYINDEX"],
16289 "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
16290 ),
16291 ];
16292 for (argv, want) in cases {
16293 let got = f.run(argv);
16294 assert_eq!(&got, want, "{:?}", argv.last());
16295 }
16296 }
16297
16298 #[test]
16300 fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
16301 let mut f = labelled();
16302 let cases: &[(&[&[u8]], &str)] = &[
16303 (
16304 &[b"TS.QUERYLABELS", b"LABELS"],
16305 "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16306 ),
16307 (
16308 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16309 "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16310 ),
16311 (
16312 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16313 "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16314 ),
16315 (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16318 (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16319 (
16320 &[b"TS.QUERYLABELS", b"VALUES"],
16321 "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16322 ),
16323 (
16324 &[b"TS.QUERYLABELS", b"ZZZ"],
16325 "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16326 ),
16327 (
16328 &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16329 "-ERR TSDB: unknown argument, expected FILTER\r\n",
16330 ),
16331 (
16332 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16333 "-ERR TSDB: FILTER given with no filter expressions\r\n",
16334 ),
16335 (
16339 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16340 "-ERR TSDB: please provide at least one matcher\r\n",
16341 ),
16342 (
16343 &[
16344 b"TS.QUERYLABELS",
16345 b"LABELS",
16346 b"FILTER",
16347 b"room=kitchen",
16348 b"x=",
16349 ],
16350 "*1\r\n$4\r\nroom\r\n",
16351 ),
16352 ];
16353 for (argv, want) in cases {
16354 let got = f.run(argv);
16355 assert_eq!(&got, want, "{:?}", argv.last());
16356 }
16357 }
16358
16359 #[test]
16362 fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16363 let mut f = labelled();
16364 let cases: &[(&[&[u8]], &str)] = &[
16365 (
16368 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16369 "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16370 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16371 ),
16372 (
16373 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16374 "*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\
16375 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16376 *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",
16377 ),
16378 (
16380 &[
16381 b"TS.MGET",
16382 b"SELECTED_LABELS",
16383 b"x",
16384 b"FILTER",
16385 b"room=kitchen",
16386 ],
16387 "*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\
16388 *2\r\n:100\r\n+1.5\r\n\
16389 *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",
16390 ),
16391 (
16394 &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16395 "*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",
16396 ),
16397 (
16398 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16399 "*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\
16400 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16401 ),
16402 (
16405 &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16406 "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16407 ),
16408 (
16409 &[b"TS.MGET", b"a", b"b", b"c"],
16410 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16411 ),
16412 (
16413 &[b"TS.MGET", b"FILTER"],
16414 "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16415 ),
16416 (
16419 &[
16420 b"TS.MGET",
16421 b"WITHLABELS",
16422 b"SELECTED_LABELS",
16423 b"x",
16424 b"FILTER",
16425 b"bad",
16426 ],
16427 "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16428 ),
16429 (
16430 &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16431 "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16432 ),
16433 ];
16434 for (argv, want) in cases {
16435 let got = f.run(argv);
16436 assert_eq!(&got, want, "{:?}", argv.last());
16437 }
16438 }
16439
16440 #[test]
16443 fn resp3_writes_the_label_surface_as_sets_and_maps() {
16444 let mut f = labelled();
16445 f.out = Out::new(Proto::Resp3);
16446 let cases: &[(&[&[u8]], &str)] = &[
16447 (
16448 &[b"TS.QUERYINDEX", b"room=kitchen"],
16449 "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16450 ),
16451 (
16452 &[b"TS.QUERYLABELS", b"LABELS"],
16453 "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16454 ),
16455 (
16456 &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16457 "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16458 ),
16459 (
16462 &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16463 "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16464 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16465 ),
16466 (
16467 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16468 "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16469 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16470 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16471 ),
16472 (
16473 &[
16474 b"TS.MGET",
16475 b"SELECTED_LABELS",
16476 b"x",
16477 b"FILTER",
16478 b"room=kitchen",
16479 ],
16480 "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16481 *2\r\n:100\r\n,1.5\r\n\
16482 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16483 ),
16484 (
16487 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16488 "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16489 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16490 ),
16491 ];
16492 for (argv, want) in cases {
16493 let got = f.run(argv);
16494 assert_eq!(&got, want, "{:?}", argv.last());
16495 }
16496 }
16497
16498 fn spanned() -> Fixture {
16501 let mut f = labelled();
16502 f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16503 f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16504 f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16505 f
16506 }
16507
16508 #[test]
16511 fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16512 let mut f = spanned();
16513 let cases: &[(&[&[u8]], &str)] = &[
16514 (
16515 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16516 "*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\
16517 *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",
16518 ),
16519 (
16522 &[
16523 b"TS.MREVRANGE",
16524 b"-",
16525 b"+",
16526 b"WITHLABELS",
16527 b"FILTER",
16528 b"room=kitchen",
16529 ],
16530 "*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\
16531 *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\
16532 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16533 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16534 ),
16535 (
16538 &[
16539 b"TS.MRANGE",
16540 b"-",
16541 b"+",
16542 b"SELECTED_LABELS",
16543 b"x",
16544 b"FILTER",
16545 b"room=kitchen",
16546 ],
16547 "*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\
16548 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16549 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16550 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16551 ),
16552 (
16555 &[
16556 b"TS.MRANGE",
16557 b"-",
16558 b"+",
16559 b"FILTER",
16560 b"room=kitchen",
16561 b"GROUPBY",
16562 b"room",
16563 b"REDUCE",
16564 b"sum",
16565 ],
16566 "*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\
16567 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16568 ),
16569 (
16572 &[
16573 b"TS.MRANGE",
16574 b"-",
16575 b"+",
16576 b"WITHLABELS",
16577 b"FILTER",
16578 b"room=kitchen",
16579 b"GROUPBY",
16580 b"room",
16581 b"REDUCE",
16582 b"max",
16583 ],
16584 "*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\
16585 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16586 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16587 *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",
16588 ),
16589 (
16591 &[
16592 b"TS.MREVRANGE",
16593 b"-",
16594 b"+",
16595 b"COUNT",
16596 b"1",
16597 b"FILTER",
16598 b"room=kitchen",
16599 b"GROUPBY",
16600 b"room",
16601 b"REDUCE",
16602 b"count",
16603 ],
16604 "*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",
16605 ),
16606 (
16608 &[
16609 b"TS.MRANGE",
16610 b"-",
16611 b"+",
16612 b"FILTER",
16613 b"room=kitchen",
16614 b"GROUPBY",
16615 b"nope",
16616 b"REDUCE",
16617 b"sum",
16618 ],
16619 "*0\r\n",
16620 ),
16621 (
16622 &[
16623 b"TS.MRANGE",
16624 b"-",
16625 b"+",
16626 b"AGGREGATION",
16627 b"sum,avg",
16628 b"100",
16629 b"FILTER",
16630 b"room=bedroom",
16631 ],
16632 "*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",
16633 ),
16634 (
16636 &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16637 "-ERR TSDB: missing FILTER argument\r\n",
16638 ),
16639 (
16640 &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16641 "-ERR TSDB: missing labels for filter argument\r\n",
16642 ),
16643 (
16644 &[
16645 b"TS.MRANGE",
16646 b"-",
16647 b"+",
16648 b"GROUPBY",
16649 b"room",
16650 b"REDUCE",
16651 b"sum",
16652 b"FILTER",
16653 b"room=kitchen",
16654 ],
16655 "-ERR TSDB: GROUPBY should always come after filter\r\n",
16656 ),
16657 (
16660 &[
16661 b"TS.MRANGE",
16662 b"-",
16663 b"+",
16664 b"FILTER",
16665 b"room=kitchen",
16666 b"GROUPBY",
16667 b"room",
16668 b"REDUCE",
16669 b"sum",
16670 b"x",
16671 ],
16672 "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16673 ),
16674 (
16676 &[
16677 b"TS.MRANGE",
16678 b"-",
16679 b"+",
16680 b"FILTER",
16681 b"nope",
16682 b"GROUPBY",
16683 b"room",
16684 b"REDUCE",
16685 b"sum",
16686 b"x",
16687 ],
16688 "-ERR TSDB: failed parsing labels\r\n",
16689 ),
16690 (
16691 &[
16692 b"TS.MRANGE",
16693 b"-",
16694 b"+",
16695 b"FILTER",
16696 b"room=kitchen",
16697 b"GROUPBY",
16698 b"room",
16699 b"REDUCE",
16700 b"twa",
16701 ],
16702 "-ERR TSDB: Invalid reducer type\r\n",
16703 ),
16704 (
16705 &[
16706 b"TS.MRANGE",
16707 b"-",
16708 b"+",
16709 b"AGGREGATION",
16710 b"sum,avg",
16711 b"100",
16712 b"FILTER",
16713 b"room=kitchen",
16714 b"GROUPBY",
16715 b"room",
16716 b"REDUCE",
16717 b"sum",
16718 ],
16719 "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
16720 ),
16721 (
16724 &[
16725 b"TS.MRANGE",
16726 b"-",
16727 b"+",
16728 b"SELECTED_LABELS",
16729 b"COUNT",
16730 b"FILTER",
16731 b"room=kitchen",
16732 ],
16733 "-ERR TSDB: Couldn't parse COUNT\r\n",
16734 ),
16735 ];
16736 for (argv, want) in cases {
16737 let got = f.run(argv);
16738 assert_eq!(&got, want, "{argv:?}");
16739 }
16740 }
16741
16742 #[test]
16745 fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
16746 let mut f = spanned();
16747 f.out = Out::new(Proto::Resp3);
16748 let cases: &[(&[&[u8]], &str)] = &[
16749 (
16750 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
16751 "%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\
16752 *1\r\n*2\r\n:200\r\n,2\r\n",
16753 ),
16754 (
16757 &[
16758 b"TS.MRANGE",
16759 b"-",
16760 b"+",
16761 b"AGGREGATION",
16762 b"sum,avg",
16763 b"100",
16764 b"FILTER",
16765 b"room=bedroom",
16766 ],
16767 "%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\
16768 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
16769 ),
16770 (
16771 &[
16772 b"TS.MRANGE",
16773 b"-",
16774 b"+",
16775 b"FILTER",
16776 b"room=kitchen",
16777 b"GROUPBY",
16778 b"room",
16779 b"REDUCE",
16780 b"sum",
16781 ],
16782 "%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\
16783 $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\
16784 *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",
16785 ),
16786 (
16789 &[
16790 b"TS.MRANGE",
16791 b"-",
16792 b"+",
16793 b"WITHLABELS",
16794 b"FILTER",
16795 b"room=kitchen",
16796 b"GROUPBY",
16797 b"room",
16798 b"REDUCE",
16799 b"max",
16800 ],
16801 "%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\
16802 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
16803 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16804 *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",
16805 ),
16806 (
16807 &[
16808 b"TS.MRANGE",
16809 b"-",
16810 b"+",
16811 b"FILTER",
16812 b"room=kitchen",
16813 b"GROUPBY",
16814 b"nope",
16815 b"REDUCE",
16816 b"sum",
16817 ],
16818 "%0\r\n",
16819 ),
16820 ];
16821 for (argv, want) in cases {
16822 let got = f.run(argv);
16823 assert_eq!(&got, want, "{argv:?}");
16824 }
16825 }
16826
16827 #[test]
16829 fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
16830 let mut f = Fixture::new();
16831 f.run(&[b"TS.CREATE", b"src"]);
16832 f.run(&[b"TS.CREATE", b"dst"]);
16833 f.run(&[b"SET", b"plain", b"v"]);
16834 let cases: &[(&[&[u8]], &str)] = &[
16835 (
16839 &[
16840 b"TS.CREATERULE",
16841 b"src",
16842 b"dst",
16843 b"AGGREGATION",
16844 b"nope",
16845 b"x",
16846 ],
16847 "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16848 ),
16849 (
16850 &[
16851 b"TS.CREATERULE",
16852 b"src",
16853 b"dst",
16854 b"AGGREGATION",
16855 b"nope",
16856 b"10",
16857 ],
16858 "-ERR TSDB: Unknown aggregation type\r\n",
16859 ),
16860 (
16861 &[
16862 b"TS.CREATERULE",
16863 b"src",
16864 b"dst",
16865 b"AGGREGATION",
16866 b"avg",
16867 b"0",
16868 ],
16869 "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16870 ),
16871 (
16872 &[
16873 b"TS.CREATERULE",
16874 b"src",
16875 b"dst",
16876 b"AGGREGATION",
16877 b"avg",
16878 b"10",
16879 b"x",
16880 ],
16881 "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
16882 ),
16883 (
16884 &[
16885 b"TS.CREATERULE",
16886 b"src",
16887 b"src",
16888 b"AGGREGATION",
16889 b"avg",
16890 b"10",
16891 ],
16892 "-ERR TSDB: the source key and destination key should be different\r\n",
16893 ),
16894 (
16898 &[
16899 b"TS.CREATERULE",
16900 b"nope",
16901 b"plain",
16902 b"AGGREGATION",
16903 b"avg",
16904 b"10",
16905 ],
16906 "-ERR TSDB: the key does not exist\r\n",
16907 ),
16908 (
16909 &[
16910 b"TS.CREATERULE",
16911 b"src",
16912 b"nope",
16913 b"AGGREGATION",
16914 b"avg",
16915 b"10",
16916 ],
16917 "-ERR TSDB: the key does not exist\r\n",
16918 ),
16919 (
16922 &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
16923 "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
16924 ),
16925 (
16926 &[
16927 b"TS.CREATERULE",
16928 b"src",
16929 b"dst",
16930 b"AGGREGATION",
16931 b"avg",
16932 b"10",
16933 ],
16934 "+OK\r\n",
16935 ),
16936 (
16939 &[
16940 b"TS.CREATERULE",
16941 b"src",
16942 b"dst",
16943 b"AGGREGATION",
16944 b"avg",
16945 b"10",
16946 ],
16947 "-ERR TSDB: the destination key already has a src rule\r\n",
16948 ),
16949 (
16952 &[
16953 b"TS.CREATERULE",
16954 b"dst",
16955 b"src",
16956 b"AGGREGATION",
16957 b"avg",
16958 b"10",
16959 ],
16960 "-ERR TSDB: the source key already has a source rule\r\n",
16961 ),
16962 (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
16963 (
16964 &[b"TS.DELETERULE", b"src", b"dst"],
16965 "-ERR TSDB: compaction rule does not exist\r\n",
16966 ),
16967 (
16971 &[b"TS.DELETERULE", b"src", b"nope"],
16972 "-ERR TSDB: compaction rule does not exist\r\n",
16973 ),
16974 (
16975 &[b"TS.DELETERULE", b"nope", b"dst"],
16976 "-ERR TSDB: the key does not exist\r\n",
16977 ),
16978 ];
16979 for (argv, want) in cases {
16980 let got = f.run(argv);
16981 assert_eq!(&got, want, "{argv:?}");
16982 }
16983 }
16984
16985 #[test]
16987 fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
16988 let mut f = Fixture::new();
16989 f.run(&[b"TS.CREATE", b"src"]);
16990 f.run(&[b"TS.CREATE", b"dst"]);
16991 f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
16994 f.run(&[
16995 b"TS.CREATERULE",
16996 b"src",
16997 b"dst",
16998 b"AGGREGATION",
16999 b"sum",
17000 b"100",
17001 ]);
17002 f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
17003 assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
17004 assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
17007 f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
17008 assert_eq!(
17009 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17010 "*1\r\n*2\r\n:0\r\n+2\r\n"
17011 );
17012 f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
17015 assert_eq!(
17016 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17017 "*1\r\n*2\r\n:0\r\n+11\r\n"
17018 );
17019 assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
17022 assert_eq!(
17023 f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17024 "*1\r\n*2\r\n:0\r\n+8\r\n"
17025 );
17026 assert_eq!(
17027 f.run(&[b"TS.GET", b"dst", b"LATEST"]),
17028 "*2\r\n:100\r\n+4\r\n"
17029 );
17030 assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
17032 f.run(&[b"DEL", b"dst"]);
17033 assert_eq!(
17034 f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
17035 "-ERR TSDB: compaction rule does not exist\r\n"
17036 );
17037 }
17038
17039 #[test]
17042 fn xadd_ids_only_ever_go_up() {
17043 let mut f = Fixture::new();
17044 assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
17046 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
17048 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
17049 assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
17050 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17051
17052 assert!(
17053 f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
17054 .contains("equal or smaller")
17055 );
17056 assert!(
17057 f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
17058 .contains("must be greater than 0-0")
17059 );
17060 assert!(
17061 f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
17062 .contains("Invalid stream ID")
17063 );
17064 assert!(
17067 f.run(&[b"XADD", b"s", b"*", b"a"])
17068 .contains("wrong number of arguments")
17069 );
17070
17071 assert_eq!(
17074 f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
17075 "$-1\r\n"
17076 );
17077 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
17078 assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
17079 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
17080 }
17081
17082 #[test]
17085 fn trimming_reads_its_options_the_way_redis_does() {
17086 let mut f = Fixture::new();
17087 for i in 1..=10u32 {
17088 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17089 }
17090 assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
17091 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17092 assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
17093 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17094
17095 assert!(
17099 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
17100 .contains("not an integer")
17101 );
17102 assert!(
17103 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
17104 .contains("MAXLEN argument must be >= 0")
17105 );
17106 assert!(
17109 f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
17110 .contains("without specifying a trimming strategy")
17111 );
17112 assert!(
17113 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
17114 .contains("without the special ~ option")
17115 );
17116 assert!(
17117 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
17118 .contains("at the same time are not compatible")
17119 );
17120 assert!(
17122 f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
17123 .contains("syntax error")
17124 );
17125 assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
17126 }
17127
17128 #[test]
17130 fn xrange_looks_the_key_up_before_it_reads_the_count() {
17131 let mut f = Fixture::new();
17132 f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
17133 f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
17134
17135 assert_eq!(
17136 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17137 "*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\
17138 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17139 );
17140 assert_eq!(
17141 f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
17142 "*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"
17143 );
17144 assert_eq!(
17148 f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
17149 "*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\
17150 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17151 );
17152 assert_eq!(
17153 f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
17154 "*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"
17155 );
17156 assert!(
17157 f.run(&[b"XRANGE", b"s", b"(-", b"+"])
17158 .contains("Invalid stream ID")
17159 );
17160
17161 assert_eq!(
17165 f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
17166 "*0\r\n"
17167 );
17168 assert_eq!(
17169 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
17170 "*-1\r\n"
17171 );
17172 f.run(&[b"SET", b"str", b"v"]);
17173 assert!(
17174 f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
17175 .starts_with("-WRONGTYPE")
17176 );
17177 assert_eq!(
17179 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
17180 "*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"
17181 );
17182 }
17183
17184 #[test]
17186 fn a_bad_id_late_in_the_list_stops_the_whole_command() {
17187 let mut f = Fixture::new();
17188 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17189 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17190 assert!(
17191 f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
17192 .contains("Invalid stream ID")
17193 );
17194 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17195 assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
17196 assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
17197 assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
17198 assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
17199 }
17200
17201 #[test]
17203 fn xgroup_has_an_arity_per_subcommand() {
17204 let mut f = Fixture::new();
17205 assert!(
17206 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17207 .contains("requires the key")
17208 );
17209 assert_eq!(
17210 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
17211 "+OK\r\n"
17212 );
17213 assert!(
17216 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17217 .starts_with("-BUSYGROUP")
17218 );
17219 assert_eq!(
17220 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17221 ":1\r\n"
17222 );
17223 assert_eq!(
17224 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17225 ":0\r\n"
17226 );
17227 assert_eq!(
17228 f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
17229 ":0\r\n"
17230 );
17231
17232 let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
17234 assert!(
17235 short.contains("wrong number of arguments for 'xgroup|destroy' command"),
17236 "{short}"
17237 );
17238 let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
17240 assert!(
17241 odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
17242 "{odd}"
17243 );
17244 assert!(
17245 f.run(&[b"XGROUP", b"NOSUCH", b"s"])
17246 .contains("Try XGROUP HELP")
17247 );
17248
17249 assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
17250 assert!(
17251 f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
17252 .starts_with("-NOGROUP")
17253 );
17254 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
17255 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
17256 assert!(
17257 f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
17258 .contains("requires the key")
17259 );
17260 }
17261
17262 #[test]
17264 fn xreadgroup_hands_out_and_xack_takes_back() {
17265 let mut f = Fixture::new();
17266 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17267 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17268 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17269
17270 let first = f.run(&[
17271 b"XREADGROUP",
17272 b"GROUP",
17273 b"g",
17274 b"c1",
17275 b"COUNT",
17276 b"1",
17277 b"STREAMS",
17278 b"s",
17279 b">",
17280 ]);
17281 assert_eq!(
17282 first,
17283 "*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"
17284 );
17285 assert_eq!(
17288 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
17289 "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
17290 );
17291 assert_eq!(
17292 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17293 "*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"
17294 );
17295
17296 assert_eq!(
17297 f.run(&[b"XPENDING", b"s", b"g"]),
17298 "*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"
17299 );
17300 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
17301 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
17302 assert_eq!(
17304 f.run(&[b"XPENDING", b"s", b"g"]),
17305 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
17306 );
17307
17308 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17311 f.run(&[b"XDEL", b"s", b"2-1"]);
17312 assert_eq!(
17313 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17314 "*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"
17315 );
17316
17317 assert!(
17320 f.run(&[
17321 b"XREADGROUP",
17322 b"GROUP",
17323 b"nope",
17324 b"c",
17325 b"STREAMS",
17326 b"s",
17327 b"+"
17328 ])
17329 .starts_with("-NOGROUP")
17330 );
17331 assert!(
17332 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17333 .contains("meaningless in the context of XREADGROUP")
17334 );
17335 assert!(
17336 f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17337 .contains("only supported by XREADGROUP")
17338 );
17339 assert!(
17340 f.run(&[
17341 b"XREADGROUP",
17342 b"GROUP",
17343 b"g",
17344 b"c",
17345 b"STREAMS",
17346 b"s",
17347 b"a",
17348 b"b"
17349 ])
17350 .contains("Unbalanced 'xreadgroup' list of streams")
17351 );
17352 }
17353
17354 #[test]
17357 fn xread_with_no_block_writes_the_null_itself() {
17358 let mut f = Fixture::new();
17359 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17360 assert_eq!(
17361 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17362 "*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"
17363 );
17364 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17367 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17368 f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17369 assert_eq!(
17370 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17371 "*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"
17372 );
17373 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17375 assert_eq!(
17377 f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17378 "*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"
17379 );
17380 assert_eq!(
17383 f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17384 "*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"
17385 );
17386 assert!(
17388 f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17389 .contains("not an integer")
17390 );
17391 assert!(
17392 f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17393 .contains("timeout is negative")
17394 );
17395 assert!(
17396 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17397 .contains("Unbalanced 'xread' list of streams")
17398 );
17399 }
17400
17401 #[test]
17403 fn a_blocked_xread_wakes_on_the_next_entry() {
17404 let mut f = Fixture::new();
17405 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17406 let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17407 assert_eq!(flow, Flow::Block);
17408 assert!(reply.is_empty());
17409
17410 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17413 assert_eq!(flow, Flow::Block);
17414 assert_eq!(f.server.waiters().len(), 2);
17415
17416 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17417 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";
17418 for at in 0..2 {
17419 let mut out = Out::new(Proto::Resp2);
17420 assert!(f.server.serve_waiter(at, 0, &mut out));
17421 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17422 }
17423
17424 f.server.waiters_mut().forget(7);
17427 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17428 assert_eq!(flow, Flow::Block);
17429 let mut out = Out::new(Proto::Resp2);
17430 assert!(!f.server.serve_waiter(0, 0, &mut out));
17431 assert!(out.as_slice().is_empty());
17432 assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
17433 assert_eq!(
17434 core::str::from_utf8(out.as_slice()).expect("ascii"),
17435 "*-1\r\n"
17436 );
17437 }
17438
17439 #[test]
17441 fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17442 let mut f = Fixture::new();
17443 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17444 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17445 let (flow, _) = f.flow(&[
17446 b"XREADGROUP",
17447 b"GROUP",
17448 b"g",
17449 b"c",
17450 b"BLOCK",
17451 b"0",
17452 b"STREAMS",
17453 b"s",
17454 b">",
17455 ]);
17456 assert_eq!(flow, Flow::Block);
17457
17458 f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17459 let mut out = Out::new(Proto::Resp2);
17460 assert!(f.server.serve_waiter(0, 0, &mut out));
17461 assert_eq!(
17464 core::str::from_utf8(out.as_slice()).expect("ascii"),
17465 "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17466 );
17467 }
17468
17469 #[test]
17471 fn xclaim_reads_ids_until_one_will_not_parse() {
17472 let mut f = Fixture::new();
17473 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17474 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17475 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17476 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17477
17478 assert!(
17481 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17482 .contains("Unrecognized XCLAIM option '-'")
17483 );
17484 assert_eq!(
17485 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17486 "*1\r\n$3\r\n1-1\r\n"
17487 );
17488 f.run(&[b"XDEL", b"s", b"2-1"]);
17491 assert_eq!(
17492 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17493 "*0\r\n"
17494 );
17495 assert!(
17496 f.run(&[b"XPENDING", b"s", b"g"])
17497 .starts_with("*4\r\n:1\r\n")
17498 );
17499 assert!(
17500 f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17501 .starts_with("-NOGROUP")
17502 );
17503 assert!(
17504 f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17505 .contains("Invalid min-idle-time argument for XCLAIM")
17506 );
17507 }
17508
17509 #[test]
17511 fn xautoclaim_reports_what_it_dropped() {
17512 let mut f = Fixture::new();
17513 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17514 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17515 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17516 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17517 f.run(&[b"XDEL", b"s", b"1-1"]);
17518
17519 assert_eq!(
17522 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17523 "*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"
17524 );
17525 assert!(
17526 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17527 .contains("COUNT must be > 0")
17528 );
17529 assert!(
17530 f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17531 .starts_with("-NOGROUP")
17532 );
17533 }
17534
17535 #[test]
17537 fn xdelex_answers_one_integer_an_id() {
17538 let mut f = Fixture::new();
17539 for i in 1..=4 {
17540 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17541 }
17542 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17543 f.run(&[
17544 b"XREADGROUP",
17545 b"GROUP",
17546 b"g",
17547 b"c",
17548 b"COUNT",
17549 b"2",
17550 b"STREAMS",
17551 b"s",
17552 b">",
17553 ]);
17554
17555 assert_eq!(
17557 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17558 "*2\r\n:1\r\n:-1\r\n"
17559 );
17560 assert!(
17563 f.run(&[b"XPENDING", b"s", b"g"])
17564 .starts_with("*4\r\n:2\r\n")
17565 );
17566 assert_eq!(
17568 f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17569 "*1\r\n:1\r\n"
17570 );
17571 assert_eq!(
17573 f.run(&[b"XPENDING", b"s", b"g"]),
17574 "*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"
17575 );
17576
17577 assert_eq!(
17581 f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17582 "*2\r\n:2\r\n:2\r\n"
17583 );
17584
17585 assert_eq!(
17587 f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17588 "*2\r\n:-1\r\n:-1\r\n"
17589 );
17590 assert!(
17592 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17593 .starts_with("-ERR Invalid stream ID")
17594 );
17595 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17596
17597 assert!(
17598 f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17599 .contains("Number of IDs must be a positive integer")
17600 );
17601 assert!(
17602 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17603 .contains("The `numids` parameter must match the number of arguments")
17604 );
17605 assert!(
17608 f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17609 .starts_with("-ERR syntax error")
17610 );
17611 assert!(
17612 f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17613 .starts_with("-ERR syntax error")
17614 );
17615 f.run(&[b"SET", b"str", b"v"]);
17617 assert!(
17618 f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17619 .starts_with("-WRONGTYPE")
17620 );
17621 }
17622
17623 #[test]
17625 fn xackdel_reports_what_the_group_was_holding() {
17626 let mut f = Fixture::new();
17627 for i in 1..=3 {
17628 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17629 }
17630 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17631 f.run(&[
17632 b"XREADGROUP",
17633 b"GROUP",
17634 b"g",
17635 b"c",
17636 b"COUNT",
17637 b"1",
17638 b"STREAMS",
17639 b"s",
17640 b">",
17641 ]);
17642
17643 assert_eq!(
17647 f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17648 "*2\r\n:1\r\n:-1\r\n"
17649 );
17650 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17651
17652 assert_eq!(
17654 f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17655 "*1\r\n:-1\r\n"
17656 );
17657 assert_eq!(
17658 f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17659 "*1\r\n:-1\r\n"
17660 );
17661
17662 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17665 f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17666 assert_eq!(
17667 f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17668 "*1\r\n:2\r\n"
17669 );
17670 assert_eq!(
17671 f.run(&[b"XPENDING", b"s", b"g"]),
17672 "*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"
17673 );
17674 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17675 }
17676
17677 #[test]
17679 fn xnack_releases_an_entry_for_the_next_claim() {
17680 let mut f = Fixture::new();
17681 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17682 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17683 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17684 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17685 f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17688
17689 assert_eq!(
17690 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
17691 ":1\r\n"
17692 );
17693 assert_eq!(
17697 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
17698 "*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"
17699 );
17700 assert_eq!(
17702 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17703 "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
17704 );
17705 assert_eq!(
17707 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17708 "*-1\r\n"
17709 );
17710 assert_eq!(
17712 f.run(&[
17713 b"XAUTOCLAIM",
17714 b"s",
17715 b"g",
17716 b"c2",
17717 b"99999999",
17718 b"-",
17719 b"JUSTID"
17720 ]),
17721 "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
17722 );
17723
17724 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17728 assert!(
17729 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17730 .contains(":-1\r\n:1\r\n")
17731 );
17732 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17734 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17735 assert!(
17736 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17737 .contains(":-1\r\n:0\r\n")
17738 );
17739 f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
17741 assert!(
17742 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17743 .contains(":9223372036854775807\r\n")
17744 );
17745 f.run(&[
17746 b"XNACK",
17747 b"s",
17748 b"g",
17749 b"FATAL",
17750 b"IDS",
17751 b"1",
17752 b"1-1",
17753 b"RETRYCOUNT",
17754 b"3",
17755 ]);
17756 assert!(
17757 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17758 .contains(":-1\r\n:3\r\n")
17759 );
17760
17761 f.run(&[b"XACK", b"s", b"g", b"2-1"]);
17765 assert_eq!(
17766 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
17767 ":0\r\n"
17768 );
17769 assert_eq!(
17770 f.run(&[
17771 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
17772 ]),
17773 ":1\r\n"
17774 );
17775 assert!(
17776 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17777 .contains(":-1\r\n:0\r\n")
17778 );
17779 assert_eq!(
17781 f.run(&[
17782 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
17783 ]),
17784 ":0\r\n"
17785 );
17786
17787 assert_eq!(
17790 f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
17791 "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
17792 );
17793 assert!(
17794 f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
17795 .starts_with("-ERR")
17796 );
17797 assert!(
17799 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
17800 .contains("numids must be a positive integer")
17801 );
17802 assert!(
17803 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
17804 .contains("number of IDs doesn't match numids")
17805 );
17806 assert!(
17809 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
17810 .contains("Unrecognized XNACK option '2-1'")
17811 );
17812 }
17813
17814 #[test]
17816 fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
17817 let mut f = Fixture::new();
17818 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17819 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17820 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17821 f.run(&[
17822 b"XREADGROUP",
17823 b"GROUP",
17824 b"g",
17825 b"c1",
17826 b"COUNT",
17827 b"1",
17828 b"STREAMS",
17829 b"s",
17830 b">",
17831 ]);
17832
17833 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17834 assert!(info.starts_with("*20\r\n"), "{info}");
17837 assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
17838 assert!(
17839 info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
17840 "{info}"
17841 );
17842 assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
17843 assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
17844
17845 let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
17846 assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
17847 assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
17848 assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
17849 assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
17850
17851 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
17855 let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
17856 assert!(consumers.starts_with("*2\r\n"), "{consumers}");
17857 assert!(
17858 consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
17859 "{consumers}"
17860 );
17861 let c1 = consumers.find("c1").unwrap();
17863 let c2 = consumers.find("c2").unwrap();
17864 assert!(c1 < c2, "{consumers}");
17865
17866 let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
17867 assert!(full.starts_with("*18\r\n"), "{full}");
17868 assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
17869 assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
17870
17871 assert!(
17872 f.run(&[b"XINFO", b"STREAM", b"missing"])
17873 .contains("no such key")
17874 );
17875 assert!(
17876 f.run(&[b"XINFO", b"GROUPS", b"missing"])
17877 .contains("no such key")
17878 );
17879 assert!(
17880 f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
17881 .starts_with("-NOGROUP")
17882 );
17883 assert!(
17884 f.run(&[b"XINFO", b"NOSUCH", b"s"])
17885 .contains("Try XINFO HELP")
17886 );
17887 assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
17888 assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
17889 }
17890
17891 #[test]
17893 fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
17894 let mut f = Fixture::new();
17895 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17896 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17897 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17898
17899 let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
17900 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");
17901 assert_eq!(
17902 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17903 "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
17904 );
17905 assert_eq!(
17907 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
17908 "*0\r\n"
17909 );
17910 assert_eq!(
17911 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
17912 list
17913 );
17914 assert!(
17916 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
17917 .contains("syntax error")
17918 );
17919 assert!(
17920 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
17921 .contains("syntax error")
17922 );
17923 assert_eq!(
17924 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
17925 "*0\r\n"
17926 );
17927 assert!(
17928 f.run(&[b"XPENDING", b"missing", b"g"])
17929 .starts_with("-NOGROUP")
17930 );
17931 }
17932
17933 #[test]
17935 fn xsetid_will_not_go_below_what_is_there() {
17936 let mut f = Fixture::new();
17937 f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
17938 assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
17939 assert_eq!(
17940 f.run(&[
17941 b"XSETID",
17942 b"s",
17943 b"10-1",
17944 b"ENTRIESADDED",
17945 b"7",
17946 b"MAXDELETEDID",
17947 b"9-1"
17948 ]),
17949 "+OK\r\n"
17950 );
17951 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17952 assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
17953 assert!(
17954 info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
17955 "{info}"
17956 );
17957
17958 assert!(
17959 f.run(&[b"XSETID", b"s", b"1-1"])
17960 .contains("smaller than the target stream top item")
17961 );
17962 assert!(
17963 f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
17964 .contains("entries_added must be positive")
17965 );
17966 assert!(
17967 f.run(&[b"XSETID", b"missing", b"1-1"])
17968 .contains("no such key")
17969 );
17970 }
17971
17972 #[test]
17974 fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
17975 let mut f = Fixture::new();
17976 f.run(&[b"HELLO", b"3"]);
17977 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17978 assert_eq!(
17981 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17982 "%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"
17983 );
17984 assert_eq!(
17987 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17988 "*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"
17989 );
17990 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
17991 }
17992
17993 struct Mem {
17999 blobs: Vec<Vec<u8>>,
18000 }
18001
18002 impl yo_kv::cold::Blocks for Mem {
18003 fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
18004 self.blobs.push(bytes.to_vec());
18005 Ok(yo_common::Addr::new(
18006 yo_common::Space::Log,
18007 (self.blobs.len() - 1) as u64,
18008 ))
18009 }
18010
18011 fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
18012 self.blobs
18013 .get(at.offset() as usize)
18014 .map(Vec::as_slice)
18015 .ok_or_else(|| {
18016 yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
18017 })
18018 }
18019
18020 fn bytes(&self) -> u64 {
18021 self.blobs.iter().map(|b| b.len() as u64).sum()
18022 }
18023 }
18024
18025 fn filled(attach: bool) -> (Fixture, usize) {
18029 let mut f = Fixture::new();
18030 if attach {
18031 f.server
18032 .striped(0)
18033 .stripe_mut(0)
18034 .attach(Box::new(Mem { blobs: Vec::new() }));
18035 }
18036 let val = vec![b'v'; 256];
18037 for i in 0..24000u32 {
18038 let k = format!("key:{i:08}");
18039 f.run(&[b"SET", k.as_bytes(), &val]);
18040 }
18041 let full = f.server.memory_bytes();
18042 assert!(full > 3 * 1024 * 1024, "the arena is several segments");
18043 (f, full)
18044 }
18045
18046 fn press(f: &mut Fixture, limit: usize) {
18052 let val = vec![b'v'; 256];
18053 for i in 0..3000u32 {
18054 let k = format!("new:{i:08}");
18055 assert_eq!(
18056 f.run(&[b"SET", k.as_bytes(), &val]),
18057 "+OK\r\n",
18058 "write {i} was refused"
18059 );
18060 f.server.refresh_memory();
18061 if f.server.memory_bytes() <= limit {
18062 return;
18063 }
18064 }
18065 panic!(
18066 "it never got under: {} against {limit}",
18067 f.server.memory_bytes()
18068 );
18069 }
18070
18071 #[test]
18072 fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
18073 let mut f = Fixture::new();
18074 assert_eq!(
18075 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18076 "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
18077 "no limit is the default"
18078 );
18079 for (typed, bytes) in [
18082 (&b"0"[..], "0"),
18083 (b"1024", "1024"),
18084 (b"1k", "1000"),
18085 (b"1gb", "1073741824"),
18086 (b"-1", "-1"),
18087 ] {
18088 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
18089 assert_eq!(
18090 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18091 format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
18092 "set {}",
18093 String::from_utf8_lossy(typed)
18094 );
18095 }
18096 for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
18097 assert_eq!(
18098 f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
18099 "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
18100 "refused {}",
18101 String::from_utf8_lossy(bad)
18102 );
18103 }
18104 let info = f.run(&[b"INFO", b"memory"]);
18106 assert!(info.contains("maxstore:-1"), "{info}");
18107 assert!(info.contains("yo_memory_regime:evict"), "{info}");
18108 assert!(info.contains("yo_store_bytes:0"), "{info}");
18109 }
18110
18111 #[test]
18112 fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
18113 let (mut f, full) = filled(true);
18117 let keys = f.run(&[b"DBSIZE"]);
18118 assert!(
18119 f.run(&[b"INFO", b"memory"])
18120 .contains("yo_memory_regime:migrate"),
18121 "a database with somewhere to put values migrates"
18122 );
18123
18124 let limit = full - 2 * 1024 * 1024;
18125 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18126 f.run(&[
18127 b"CONFIG",
18128 b"SET",
18129 b"maxmemory",
18130 limit.to_string().as_bytes(),
18131 ]);
18132 press(&mut f, limit);
18133
18134 assert!(
18135 f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18136 "nothing was thrown away"
18137 );
18138 let after: usize = f.run(&[b"DBSIZE"])[1..]
18139 .trim_end()
18140 .parse()
18141 .expect("a count");
18142 let before: usize = keys[1..].trim_end().parse().expect("a count");
18143 assert!(after > before, "the keys that came in are all still here");
18144 assert!(
18145 f.server.store_bytes() > 0,
18146 "and what came out of memory went to the file"
18147 );
18148 let val = format!("$256\r\n{}\r\n", "v".repeat(256));
18151 assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
18152 assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
18153 }
18154
18155 #[test]
18156 fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
18157 let (mut f, full) = filled(true);
18161 f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
18162 assert!(
18163 f.run(&[b"INFO", b"memory"])
18164 .contains("yo_memory_regime:evict"),
18165 "nothing may go to the file"
18166 );
18167
18168 let limit = full - 2 * 1024 * 1024;
18169 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18170 f.run(&[
18171 b"CONFIG",
18172 b"SET",
18173 b"maxmemory",
18174 limit.to_string().as_bytes(),
18175 ]);
18176 press(&mut f, limit);
18177
18178 assert!(
18179 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18180 "keys were thrown away, which is what was asked for"
18181 );
18182 assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
18183 }
18184
18185 #[test]
18186 fn a_full_file_goes_back_to_evicting() {
18187 let (mut f, full) = filled(true);
18191 f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
18192 let limit = full - 2 * 1024 * 1024;
18193 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18194 f.run(&[
18195 b"CONFIG",
18196 b"SET",
18197 b"maxmemory",
18198 limit.to_string().as_bytes(),
18199 ]);
18200 press(&mut f, limit);
18201
18202 assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
18203 assert!(
18204 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18205 "and then it started evicting"
18206 );
18207 assert!(
18208 f.run(&[b"INFO", b"memory"])
18209 .contains("yo_memory_regime:evict"),
18210 "and it says so"
18211 );
18212 }
18213 #[test]
18229 fn the_string_group_answers_the_same_however_many_stripes_there_are() {
18230 let script: &[&[&[u8]]] = &[
18231 &[b"SET", b"k1", b"v1"],
18234 &[b"SET", b"k2", b"v2"],
18235 &[b"GET", b"k1"],
18236 &[b"GET", b"nothing"],
18237 &[b"GETSET", b"k1", b"v1b"],
18238 &[b"SETNX", b"k1", b"no"],
18239 &[b"SETNX", b"k3", b"yes"],
18240 &[b"APPEND", b"k3", b"!"],
18241 &[b"STRLEN", b"k3"],
18242 &[b"SETRANGE", b"k3", b"1", b"XY"],
18243 &[b"GETRANGE", b"k3", b"0", b"-1"],
18244 &[b"INCR", b"n1"],
18245 &[b"INCRBY", b"n1", b"41"],
18246 &[b"DECRBY", b"n1", b"2"],
18247 &[b"INCRBYFLOAT", b"f1", b"1.5"],
18248 &[b"SETEX", b"e1", b"100", b"v"],
18249 &[b"PSETEX", b"e2", b"100000", b"v"],
18250 &[b"GETEX", b"e1", b"PERSIST"],
18251 &[b"GETDEL", b"k2"],
18252 &[b"GET", b"k2"],
18253 &[b"DIGEST", b"k1"],
18254 &[b"DELEX", b"k3"],
18255 &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
18258 &[b"MGET", b"a", b"b", b"c", b"missing"],
18259 &[b"MSETNX", b"d", b"4", b"e", b"5"],
18260 &[b"MSETNX", b"e", b"6", b"f", b"7"],
18261 &[b"MGET", b"d", b"e", b"f"],
18262 &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
18263 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
18264 &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
18265 &[b"MGET", b"g", b"h"],
18266 &[b"SET", b"s1", b"ohmytext"],
18267 &[b"SET", b"s2", b"mynewtext"],
18268 &[b"LCS", b"s1", b"s2"],
18269 &[b"LCS", b"s1", b"s2", b"LEN"],
18270 &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
18271 &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
18272 &[b"LCS", b"s1", b"gone"],
18273 &[b"MSET", b"odd"],
18275 &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
18276 &[b"MGET"],
18277 ];
18278
18279 let mut one = Fixture::new();
18280 let mut many = Fixture::striped(8);
18281 for parts in script {
18282 let a = one.run(parts);
18283 let b = many.run(parts);
18284 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18285 }
18286 }
18287
18288 #[test]
18293 fn a_striped_database_spreads_the_keys_it_is_given() {
18294 let mut f = Fixture::striped(8);
18295 for i in 0..256 {
18296 let key = format!("key:{i}");
18297 f.run(&[b"SET", key.as_bytes(), b"v"]);
18298 }
18299 assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
18300 }
18301
18302 #[test]
18305 fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
18306 let mut one = Fixture::new();
18307 let mut many = Fixture::striped(8);
18308 for f in [&mut one, &mut many] {
18309 f.run(&[b"SET", b"str", b"v"]);
18310 f.server
18316 .striped(0)
18317 .at(b"list")
18318 .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18319 .expect("a new list");
18320 }
18321 assert_eq!(
18322 one.run(&[b"MGET", b"str", b"list", b"gone"]),
18323 many.run(&[b"MGET", b"str", b"list", b"gone"])
18324 );
18325 }
18326
18327 #[test]
18335 fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18336 let script: &[&[&[u8]]] = &[
18337 &[b"SET", b"k1", b"v1"],
18338 &[b"SET", b"k2", b"v2"],
18339 &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18340 &[b"TYPE", b"k1"],
18341 &[b"TYPE", b"gone"],
18342 &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18343 &[b"EXPIRE", b"k1", b"100"],
18344 &[b"TTL", b"k1"],
18345 &[b"EXPIRE", b"k1", b"200", b"NX"],
18346 &[b"PERSIST", b"k1"],
18347 &[b"TTL", b"k1"],
18348 &[b"PEXPIREAT", b"k2", b"1900000000000"],
18349 &[b"EXPIRETIME", b"k2"],
18350 &[b"PEXPIRETIME", b"k2"],
18351 &[b"PERSIST", b"k2"],
18352 &[b"OBJECT", b"ENCODING", b"k1"],
18353 &[b"OBJECT", b"REFCOUNT", b"k1"],
18354 &[b"OBJECT", b"IDLETIME", b"k1"],
18355 &[b"OBJECT", b"FREQ", b"k1"],
18356 &[b"OBJECT", b"ENCODING", b"gone"],
18357 &[b"OBJECT", b"HELP"],
18358 &[b"RENAME", b"k1", b"k9"],
18359 &[b"GET", b"k9"],
18360 &[b"RENAME", b"gone", b"x"],
18361 &[b"RENAMENX", b"k9", b"k2"],
18362 &[b"RENAMENX", b"k9", b"k8"],
18363 &[b"GET", b"k8"],
18364 &[b"COPY", b"k8", b"c1"],
18365 &[b"COPY", b"k8", b"c1"],
18366 &[b"COPY", b"k8", b"c1", b"REPLACE"],
18367 &[b"COPY", b"k8", b"k8"],
18368 &[b"COPY", b"gone", b"c2"],
18369 &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18370 &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18371 &[b"MOVE", b"c1", b"1"],
18372 &[b"MOVE", b"c1", b"1"],
18373 &[b"MOVE", b"k8", b"0"],
18374 &[b"DEL", b"k2", b"gone"],
18375 &[b"UNLINK", b"k8", b"k8"],
18376 &[b"DBSIZE"],
18377 ];
18378
18379 let mut one = Fixture::new();
18380 let mut many = Fixture::striped(8);
18381 for parts in script {
18382 let a = one.run(parts);
18383 let b = many.run(parts);
18384 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18385 }
18386
18387 for f in [&mut one, &mut many] {
18393 f.run(&[b"SET", b"d1", b"payload"]);
18394 let payload = f
18395 .server
18396 .striped(0)
18397 .at(b"d1")
18398 .dump(b"d1")
18399 .expect("a key that is there");
18400 assert!(
18401 f.run(&[b"DUMP", b"d1"])
18402 .starts_with(&format!("${}", payload.len())),
18403 "a payload of the length the store gave"
18404 );
18405 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18406 assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18407 assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18408 assert_eq!(
18409 f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18410 "-BUSYKEY Target key name already exists.\r\n"
18411 );
18412 assert_eq!(
18413 f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18414 "-ERR DUMP payload version or checksum are wrong\r\n"
18415 );
18416 }
18417 }
18418
18419 #[test]
18426 fn a_scan_of_a_striped_database_walks_all_of_it() {
18427 let mut f = Fixture::striped(8);
18428 for i in 0..500 {
18429 let key = format!("key:{i}");
18430 f.run(&[b"SET", key.as_bytes(), b"v"]);
18431 }
18432
18433 let mut seen = Vec::new();
18434 let mut cursor = "0".to_owned();
18435 let mut calls = 0;
18436 loop {
18437 let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18438 let (next, keys) = scan_reply(&reply);
18439 seen.extend(keys);
18440 cursor = next;
18441 calls += 1;
18442 assert!(calls < 5_000, "a scan that will not finish");
18443 if cursor == "0" {
18444 break;
18445 }
18446 }
18447 seen.sort();
18448 assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18449 assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18450
18451 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18455 let (_, keys) = scan_reply(&reply);
18456 assert_eq!(keys.len(), 10, "key:40 through key:49");
18457 let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18458 let (_, keys) = scan_reply(&reply);
18459 assert!(keys.is_empty(), "nothing here is a list");
18460 }
18461
18462 #[test]
18468 fn a_random_key_can_come_from_any_stripe() {
18469 let mut f = Fixture::striped(8);
18470 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18471 for i in 0..200 {
18472 let key = format!("key:{i}");
18473 f.run(&[b"SET", key.as_bytes(), b"v"]);
18474 }
18475 let mut homes = std::collections::HashSet::new();
18476 for _ in 0..200 {
18477 let got = f.run(&[b"RANDOMKEY"]);
18478 let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18479 assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18480 homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18481 }
18482 assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18483 }
18484
18485 fn apart(f: &mut Fixture, src: &str) -> String {
18489 let home = f.server.striped(0).stripe_of(src.as_bytes());
18490 for i in 0..1_000 {
18491 let dst = format!("dst:{i}");
18492 if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18493 return dst;
18494 }
18495 }
18496 panic!("eight stripes and a thousand keys all landed in one place");
18497 }
18498
18499 #[test]
18502 fn a_rename_across_stripes_takes_everything_with_it() {
18503 let mut f = Fixture::striped(8);
18504 let dst = apart(&mut f, "src");
18505 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18506
18507 f.run(&[b"SET", src, b"v"]);
18508 f.run(&[b"EXPIRE", src, b"100"]);
18509 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18510 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18511 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18512 assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18513
18514 f.server
18519 .striped(0)
18520 .at(src)
18521 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18522 .expect("a new list");
18523 assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18524 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18525 assert_eq!(
18526 f.server.striped(0).at(dst).llen(dst).expect("a list"),
18527 2,
18528 "the members are on the stripe the key moved to"
18529 );
18530
18531 f.run(&[b"SET", src, b"v"]);
18534 assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18535 assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18536 assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18537 }
18538
18539 #[test]
18541 fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18542 let mut f = Fixture::striped(8);
18543 let dst = apart(&mut f, "src");
18544 let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18545
18546 f.run(&[b"SET", src, b"v"]);
18547 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18548 assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18549 assert_eq!(
18550 f.run(&[b"COPY", src, dst]),
18551 ":0\r\n",
18552 "the destination is taken"
18553 );
18554 f.run(&[b"SET", src, b"w"]);
18555 assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18556 assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18557
18558 f.run(&[b"DEL", src, dst]);
18562 f.server
18563 .striped(0)
18564 .at(src)
18565 .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18566 .expect("a new list");
18567 assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18568 f.server
18569 .striped(0)
18570 .at(src)
18571 .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18572 .expect("a list that is there");
18573 assert_eq!(f.server.striped(0).at(src).llen(src).expect("a list"), 3);
18574 assert_eq!(f.server.striped(0).at(dst).llen(dst).expect("a list"), 2);
18575 }
18576
18577 #[test]
18585 fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18586 let script: &[&[&[u8]]] = &[
18587 &[b"SET", b"k1", b"foobar"],
18588 &[b"SETBIT", b"b1", b"7", b"1"],
18589 &[b"SETBIT", b"b1", b"7", b"0"],
18590 &[b"GETBIT", b"k1", b"6"],
18591 &[b"GETBIT", b"k1", b"100"],
18592 &[b"BITCOUNT", b"k1"],
18593 &[b"BITCOUNT", b"k1", b"0", b"0"],
18594 &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18595 &[b"BITPOS", b"k1", b"1"],
18596 &[b"BITPOS", b"k1", b"0", b"2"],
18597 &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18598 &[
18599 b"BITFIELD",
18600 b"bf",
18601 b"SET",
18602 b"u8",
18603 b"0",
18604 b"255",
18605 b"GET",
18606 b"u8",
18607 b"0",
18608 ],
18609 &[
18610 b"BITFIELD",
18611 b"bf",
18612 b"OVERFLOW",
18613 b"SAT",
18614 b"INCRBY",
18615 b"u8",
18616 b"0",
18617 b"10",
18618 ],
18619 &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18620 &[b"SET", b"s1", b"abc"],
18623 &[b"SET", b"s2", b"abd"],
18624 &[b"SET", b"s3", b"a"],
18625 &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18626 &[b"GET", b"d1"],
18627 &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18628 &[b"GET", b"d2"],
18629 &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18630 &[b"STRLEN", b"d3"],
18631 &[b"BITOP", b"NOT", b"d4", b"s1"],
18632 &[b"STRLEN", b"d4"],
18633 &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18634 &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18635 &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18636 &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18637 &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18640 &[b"EXISTS", b"d1"],
18641 &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18642 &[b"GET", b"d9"],
18643 &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18647 &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18648 &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18649 &[b"BITOP", b"DIFF", b"d1", b"s1"],
18650 &[b"BITOP", b"NOPE", b"d1", b"s1"],
18651 &[b"BITCOUNT", b"list"],
18652 &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18653 ];
18654
18655 let mut one = Fixture::new();
18656 let mut many = Fixture::striped(8);
18657 for f in [&mut one, &mut many] {
18658 plant_list(f, b"list");
18659 }
18660 for parts in script {
18661 let a = one.run(parts);
18662 let b = many.run(parts);
18663 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18664 }
18665 }
18666
18667 fn plant_list(f: &mut Fixture, key: &[u8]) {
18673 f.server
18674 .striped(0)
18675 .at(key)
18676 .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18677 .expect("a new list");
18678 }
18679
18680 #[test]
18687 fn a_bitop_across_stripes_reads_every_source() {
18688 let mut f = Fixture::striped(8);
18689 let other = apart(&mut f, "src");
18690 let (src, far) = (b"src".as_slice(), other.as_bytes());
18691 assert_ne!(
18692 f.server.striped(0).stripe_of(src),
18693 f.server.striped(0).stripe_of(far),
18694 "the two keys are the point of the test"
18695 );
18696
18697 f.run(&[b"SET", src, b"abc"]);
18698 f.run(&[b"SET", far, b"abd"]);
18699 assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
18700 assert_eq!(
18701 f.run(&[b"GET", far]),
18702 "$3\r\nab`\r\n",
18703 "a destination that is also a source"
18704 );
18705 f.run(&[b"SET", far, b"abd"]);
18706 assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
18707 assert_eq!(
18708 f.run(&[b"GET", src]),
18709 "$3\r\n\0\0\x07\r\n",
18710 "and the other way round"
18711 );
18712
18713 f.run(&[b"SET", src, b"abc"]);
18717 f.run(&[b"DEL", far]);
18718 assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
18719 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
18720 f.run(&[b"SET", src, b"abc"]);
18721 f.run(&[b"DEL", far]);
18722 plant_list(&mut f, far);
18723 assert_eq!(
18724 f.run(&[b"BITOP", b"OR", b"out", src, far]),
18725 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18726 );
18727 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
18728 }
18729
18730 #[test]
18732 fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
18733 let script: &[&[&[u8]]] = &[
18734 &[b"PFADD", b"h1", b"a", b"b", b"c"],
18735 &[b"PFADD", b"h1", b"a"],
18736 &[b"PFADD", b"h2"],
18737 &[b"PFADD", b"h2", b"c", b"d", b"e"],
18738 &[b"PFCOUNT", b"h1"],
18739 &[b"PFCOUNT", b"h2"],
18740 &[b"PFCOUNT", b"missing"],
18741 &[b"PFCOUNT", b"h1", b"h2"],
18743 &[b"PFCOUNT", b"h1", b"missing"],
18744 &[b"PFMERGE", b"m", b"h1", b"h2"],
18745 &[b"PFCOUNT", b"m"],
18746 &[b"STRLEN", b"m"],
18747 &[b"PFMERGE", b"m"],
18748 &[b"PFCOUNT", b"m"],
18749 &[b"PFMERGE", b"m2", b"missing"],
18750 &[b"PFCOUNT", b"m2"],
18751 &[b"PFDEBUG", b"ENCODING", b"h1"],
18754 &[b"PFDEBUG", b"DECODE", b"h1"],
18755 &[b"PFDEBUG", b"TODENSE", b"h1"],
18756 &[b"PFDEBUG", b"ENCODING", b"h1"],
18757 &[b"PFDEBUG", b"TODENSE", b"h1"],
18758 &[b"PFCOUNT", b"h1", b"h2"],
18759 &[b"PFSELFTEST"],
18760 &[b"SET", b"plain", b"not a sketch at all"],
18762 &[b"PFADD", b"plain", b"a"],
18763 &[b"PFCOUNT", b"plain"],
18764 &[b"PFCOUNT", b"h1", b"plain"],
18765 &[b"PFMERGE", b"plain", b"h1"],
18766 &[b"PFMERGE", b"m", b"plain"],
18767 &[b"PFDEBUG", b"ENCODING", b"gone"],
18768 &[b"PFDEBUG", b"NOPE", b"h1"],
18769 ];
18770
18771 let mut one = Fixture::new();
18772 let mut many = Fixture::striped(8);
18773 for parts in script {
18774 let a = one.run(parts);
18775 let b = many.run(parts);
18776 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18777 }
18778 }
18779
18780 #[test]
18789 fn the_set_group_answers_the_same_however_many_stripes_there_are() {
18790 const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
18791 let script: &[&[&[u8]]] = &[
18792 &[b"SADD", b"s1", b"a", b"b", b"c"],
18793 &[b"SADD", b"s1", b"a"],
18794 &[b"SADD", b"s2", b"b", b"c", b"d"],
18795 &[b"SADD", b"ints", b"1", b"2", b"3"],
18796 &[b"SCARD", b"s1"],
18797 &[b"SISMEMBER", b"s1", b"a"],
18798 &[b"SISMEMBER", b"s1", b"z"],
18799 &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
18800 &[b"SMEMBERS", b"s1"],
18801 &[b"SREM", b"s1", b"c"],
18802 &[b"SADD", b"s1", b"c"],
18803 &[b"SSCAN", b"s1", b"0"],
18804 &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
18805 &[b"SADD", b"one", b"m"],
18808 &[b"SRANDMEMBER", b"one"],
18809 &[b"SRANDMEMBER", b"one", b"-3"],
18810 &[b"SRANDMEMBER", b"gone"],
18811 &[b"SPOP", b"one"],
18812 &[b"SPOP", b"one"],
18813 &[b"SPOP", b"gone", b"2"],
18814 &[b"SMOVE", b"s1", b"s2", b"a"],
18816 &[b"SMOVE", b"s1", b"s2", b"zzz"],
18817 &[b"SMOVE", b"gone", b"s2", b"a"],
18818 &[b"SMEMBERS", b"s1"],
18819 &[b"SMEMBERS", b"s2"],
18820 &[b"SINTER", b"s1", b"s2"],
18822 &[b"SUNION", b"s1", b"s2"],
18823 &[b"SDIFF", b"s2", b"s1"],
18824 &[b"SINTER", b"s1", b"gone"],
18825 &[b"SUNION", b"s1", b"gone"],
18826 &[b"SDIFF", b"gone", b"s1"],
18827 &[b"SINTER", b"ints", b"s1"],
18828 &[b"SINTERCARD", b"2", b"s1", b"s2"],
18829 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
18830 &[b"SUNIONCARD", b"2", b"s1", b"s2"],
18831 &[b"SDIFFCARD", b"2", b"s2", b"s1"],
18832 &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
18833 &[b"SMEMBERS", b"d1"],
18834 &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
18835 &[b"SCARD", b"d2"],
18836 &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
18837 &[b"SCARD", b"d3"],
18838 &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
18841 &[b"EXISTS", b"d4"],
18842 &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
18844 &[b"SCARD", b"s2"],
18845 &[b"SET", b"str", b"v"],
18847 &[b"SADD", b"str", b"a"],
18848 &[b"SINTER", b"s1", b"str"],
18849 &[b"SINTERSTORE", b"d5", b"s1", b"str"],
18850 &[b"EXISTS", b"d5"],
18851 &[b"SMOVE", b"str", b"s2", b"a"],
18852 &[b"SMOVE", b"s1", b"str", b"b"],
18853 &[b"SMOVE", b"gone", b"str", b"b"],
18854 &[b"SINTERCARD", b"0", b"s1"],
18855 &[b"SINTERCARD", b"3", b"s1", b"s2"],
18856 &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
18857 &[b"SPOP", b"s1", b"-1"],
18858 ];
18859
18860 let mut one = Fixture::new();
18861 let mut many = Fixture::striped(8);
18862 for parts in script {
18863 let a = one.run(parts);
18864 let b = many.run(parts);
18865 let name = String::from_utf8_lossy(parts[0]).to_uppercase();
18866 if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
18867 assert_eq!(sorted(&a), sorted(&b), "{name}");
18868 } else {
18869 assert_eq!(a, b, "{name}");
18870 }
18871 }
18872 }
18873
18874 #[test]
18876 fn a_set_operation_across_stripes_reads_every_set() {
18877 let mut f = Fixture::striped(8);
18878 let second = apart(&mut f, "s1");
18879 let third = apart(&mut f, &second);
18880 let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
18881
18882 f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
18883 f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
18884 assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
18885 assert_eq!(
18886 sorted(&f.run(&[b"SUNION", s1, s2])),
18887 ["a", "b", "c", "d"],
18888 "a union of two stripes is both of them"
18889 );
18890 assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
18891 assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
18892 assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
18893 assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
18894
18895 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
18897 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
18898 assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
18899 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
18900 assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
18901 assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
18902
18903 assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
18906 assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
18907 f.run(&[b"SET", s3, b"v"]);
18908 assert_eq!(
18909 f.run(&[b"SINTER", s1, s3]),
18910 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18911 );
18912 assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
18913 }
18914
18915 #[test]
18917 fn a_move_across_stripes_takes_the_member_with_it() {
18918 let mut f = Fixture::striped(8);
18919 let other = apart(&mut f, "src");
18920 let (src, dst) = (b"src".as_slice(), other.as_bytes());
18921
18922 f.run(&[b"SADD", src, b"a", b"b"]);
18923 f.run(&[b"SADD", dst, b"c"]);
18924 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
18925 assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
18926 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
18927 assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
18928
18929 f.run(&[b"DEL", dst]);
18932 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
18933 assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
18934 assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
18935
18936 f.run(&[b"SET", dst, b"v"]);
18940 assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
18941 f.run(&[b"SADD", src, b"b"]);
18942 assert_eq!(
18943 f.run(&[b"SMOVE", src, dst, b"b"]),
18944 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18945 );
18946 }
18947
18948 #[test]
18950 fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
18951 let mut f = Fixture::striped(8);
18952 let other = apart(&mut f, "src");
18953 let (src, far) = (b"src".as_slice(), other.as_bytes());
18954
18955 for i in 0..150 {
18956 let ele = format!("e:{i}");
18957 f.run(&[b"PFADD", src, ele.as_bytes()]);
18958 }
18959 for i in 150..200 {
18960 let ele = format!("e:{i}");
18961 f.run(&[b"PFADD", far, ele.as_bytes()]);
18962 }
18963 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
18966 assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
18967 assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
18968
18969 let dest = apart(&mut f, &other);
18972 assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
18973 assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
18974 assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
18975 assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
18976 assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
18977 }
18978
18979 #[test]
18986 fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
18987 let script: &[&[&[u8]]] = &[
18988 &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
18989 &[b"ZADD", b"z1", b"NX", b"9", b"a"],
18990 &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
18991 &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
18992 &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
18993 &[b"ZINCRBY", b"z1", b"1.5", b"b"],
18994 &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
18995 &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
18996 &[b"ZADD", b"one", b"1", b"m"],
18997 &[b"ZCARD", b"z1"],
18998 &[b"ZCARD", b"gone"],
18999 &[b"ZSCORE", b"z1", b"a"],
19000 &[b"ZSCORE", b"z1", b"zz"],
19001 &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
19002 &[b"ZRANK", b"z1", b"c"],
19003 &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
19004 &[b"ZREVRANK", b"z1", b"c"],
19005 &[b"ZRANK", b"z1", b"gone"],
19006 &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
19007 &[b"ZCOUNT", b"z1", b"(1", b"3"],
19008 &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
19009 &[b"ZRANGE", b"z1", b"0", b"-1"],
19011 &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
19012 &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
19013 &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
19014 &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
19015 &[b"ZREVRANGE", b"z1", b"0", b"-1"],
19016 &[
19017 b"ZRANGEBYSCORE",
19018 b"z1",
19019 b"-inf",
19020 b"+inf",
19021 b"LIMIT",
19022 b"1",
19023 b"1",
19024 ],
19025 &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
19026 &[b"ZSCAN", b"z1", b"0"],
19027 &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
19028 &[b"ZRANDMEMBER", b"one"],
19031 &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
19032 &[b"ZRANDMEMBER", b"gone"],
19033 &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
19035 &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
19036 &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
19037 &[b"EXISTS", b"d0"],
19038 &[b"ZUNION", b"2", b"z1", b"z2"],
19040 &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
19041 &[
19042 b"ZUNION",
19043 b"2",
19044 b"z1",
19045 b"z2",
19046 b"WEIGHTS",
19047 b"2",
19048 b"3",
19049 b"AGGREGATE",
19050 b"MAX",
19051 b"WITHSCORES",
19052 ],
19053 &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
19054 &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
19055 &[b"ZDIFF", b"2", b"gone", b"z1"],
19056 &[b"ZINTERCARD", b"2", b"z1", b"z2"],
19057 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
19058 &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
19059 &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
19060 &[
19061 b"ZINTERSTORE",
19062 b"d2",
19063 b"2",
19064 b"z1",
19065 b"z2",
19066 b"AGGREGATE",
19067 b"MIN",
19068 ],
19069 &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
19070 &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
19071 &[b"ZCARD", b"d3"],
19072 &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
19075 &[b"EXISTS", b"d4"],
19076 &[b"SADD", b"plain", b"a", b"x"],
19079 &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
19080 &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
19081 &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
19083 &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
19084 &[b"ZREM", b"d5", b"x", b"nothere"],
19086 &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
19087 &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
19088 &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
19089 &[b"ZPOPMIN", b"z1"],
19090 &[b"ZPOPMAX", b"z1", b"2"],
19091 &[b"ZPOPMIN", b"gone"],
19092 &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
19093 &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
19094 &[b"SET", b"str", b"v"],
19096 &[b"ZADD", b"str", b"1", b"a"],
19097 &[b"ZSCORE", b"str", b"a"],
19098 &[b"ZADD", b"z1", b"nan", b"a"],
19099 &[b"ZUNION", b"2", b"z1", b"str"],
19100 &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
19101 &[b"EXISTS", b"d6"],
19102 &[b"ZINTERCARD", b"0", b"z1"],
19103 &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
19104 &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
19105 &[b"ZMPOP", b"1", b"str", b"MIN"],
19106 &[b"ZPOPMIN", b"z1", b"-1"],
19107 ];
19108
19109 let mut one = Fixture::new();
19110 let mut many = Fixture::striped(8);
19111 for parts in script {
19112 let a = one.run(parts);
19113 let b = many.run(parts);
19114 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19115 }
19116 }
19117
19118 #[test]
19120 fn a_sorted_set_operation_across_stripes_reads_every_input() {
19121 let mut f = Fixture::striped(8);
19122 let second = apart(&mut f, "z1");
19123 let third = apart(&mut f, &second);
19124 let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
19125
19126 f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
19127 f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
19128 assert_eq!(
19131 f.run(&[b"ZUNION", b"2", z1, z2]),
19132 "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
19133 );
19134 assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
19135 assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
19136 assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
19137 assert_eq!(
19138 f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
19139 ":1\r\n"
19140 );
19141
19142 assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
19145 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
19146 assert_eq!(
19147 f.run(&[
19148 b"ZUNIONSTORE",
19149 z3,
19150 b"2",
19151 z1,
19152 z2,
19153 b"WEIGHTS",
19154 b"2",
19155 b"3",
19156 b"AGGREGATE",
19157 b"MAX"
19158 ]),
19159 ":3\r\n"
19160 );
19161 assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
19162 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
19163 assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
19164 assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
19165 assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
19166
19167 let popped = format!(
19170 "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
19171 second.len()
19172 );
19173 assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
19174 f.run(&[b"ZADD", z2, b"3", b"b"]);
19175
19176 assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
19179 assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
19180 f.run(&[b"SET", z3, b"v"]);
19181 assert_eq!(
19182 f.run(&[b"ZUNION", b"2", z1, z3]),
19183 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19184 );
19185 assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
19186
19187 assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
19191 assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
19192 assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
19193 }
19194
19195 #[test]
19197 fn a_range_store_across_stripes_copies_the_window() {
19198 let mut f = Fixture::striped(8);
19199 let other = apart(&mut f, "src");
19200 let third = apart(&mut f, &other);
19201 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19202
19203 f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
19204 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
19205 assert_eq!(
19206 f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
19207 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
19208 );
19209 assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
19210
19211 assert_eq!(
19214 f.run(&[
19215 b"ZRANGESTORE",
19216 dst,
19217 src,
19218 b"+inf",
19219 b"-inf",
19220 b"BYSCORE",
19221 b"REV",
19222 b"LIMIT",
19223 b"0",
19224 b"2"
19225 ]),
19226 ":2\r\n"
19227 );
19228 assert_eq!(
19229 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19230 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19231 );
19232
19233 assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
19236 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19237 f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
19238 f.run(&[b"SET", plain, b"v"]);
19239 assert_eq!(
19240 f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
19241 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19242 );
19243 assert_eq!(
19244 f.run(&[b"ZCARD", dst]),
19245 ":3\r\n",
19246 "and left the destination"
19247 );
19248 }
19249
19250 #[test]
19257 fn the_list_group_answers_the_same_however_many_stripes_there_are() {
19258 let script: &[&[&[u8]]] = &[
19259 &[b"RPUSH", b"l1", b"a", b"b", b"c"],
19260 &[b"LPUSH", b"l1", b"z"],
19261 &[b"RPUSHX", b"l1", b"d"],
19262 &[b"LPUSHX", b"gone", b"x"],
19263 &[b"RPUSHX", b"gone", b"x"],
19264 &[b"LLEN", b"l1"],
19265 &[b"LLEN", b"gone"],
19266 &[b"LRANGE", b"l1", b"0", b"-1"],
19267 &[b"LRANGE", b"l1", b"1", b"2"],
19268 &[b"LRANGE", b"l1", b"5", b"9"],
19269 &[b"LINDEX", b"l1", b"0"],
19270 &[b"LINDEX", b"l1", b"-1"],
19271 &[b"LINDEX", b"l1", b"99"],
19272 &[b"LSET", b"l1", b"0", b"y"],
19273 &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
19274 &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
19275 &[b"LPOS", b"l1", b"b"],
19276 &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
19277 &[b"LPOS", b"l1", b"nothere"],
19278 &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
19279 &[b"LREM", b"l1", b"1", b"aa"],
19280 &[b"LTRIM", b"l1", b"0", b"3"],
19281 &[b"LRANGE", b"l1", b"0", b"-1"],
19282 &[b"LPOP", b"l1"],
19283 &[b"RPOP", b"l1"],
19284 &[b"LPOP", b"l1", b"2"],
19285 &[b"LPOP", b"gone"],
19286 &[b"LPOP", b"gone", b"2"],
19287 &[b"EXISTS", b"l1"],
19288 &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
19291 &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
19292 &[b"RPOPLPUSH", b"src", b"dst"],
19293 &[b"LRANGE", b"dst", b"0", b"-1"],
19294 &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
19295 &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
19296 &[
19297 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
19298 ],
19299 &[
19300 b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
19301 ],
19302 &[b"LRANGE", b"dst", b"0", b"-1"],
19303 &[
19304 b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
19305 ],
19306 &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19307 &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19308 &[b"LMPOP", b"1", b"gone", b"LEFT"],
19309 &[b"RPUSH", b"q", b"a", b"b", b"c"],
19312 &[b"BLPOP", b"gone", b"q", b"0"],
19313 &[b"BRPOP", b"q", b"0"],
19314 &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19315 &[b"RPUSH", b"q", b"x", b"y", b"z"],
19316 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19317 &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19318 &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19319 &[b"BLPOP", b"q", b"0"],
19320 &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19321 &[b"SET", b"plain", b"v"],
19323 &[b"LPUSH", b"plain", b"a"],
19324 &[b"LLEN", b"plain"],
19325 &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19326 &[b"LRANGE", b"dst", b"0", b"-1"],
19327 &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19328 &[b"LSET", b"gone", b"0", b"v"],
19329 &[b"LSET", b"dst", b"99", b"v"],
19330 &[b"LPOP", b"dst", b"-1"],
19331 &[b"LMPOP", b"0", b"dst", b"LEFT"],
19332 &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19333 ];
19334
19335 let mut one = Fixture::new();
19336 let mut many = Fixture::striped(8);
19337 for parts in script {
19338 let a = one.run(parts);
19339 let b = many.run(parts);
19340 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19341 }
19342 }
19343
19344 #[test]
19346 fn a_list_move_across_stripes_takes_the_elements_with_it() {
19347 let mut f = Fixture::striped(8);
19348 let other = apart(&mut f, "src");
19349 let third = apart(&mut f, &other);
19350 let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19351
19352 f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19353 assert_eq!(
19354 f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19355 "$1\r\na\r\n"
19356 );
19357 assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19358 assert_eq!(
19359 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19360 "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19361 "one went on each end of the destination"
19362 );
19363 assert_eq!(
19364 f.run(&[b"LRANGE", src, b"0", b"-1"]),
19365 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19366 );
19367
19368 assert_eq!(
19370 f.run(&[
19371 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19372 ]),
19373 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19374 );
19375 assert_eq!(
19376 f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19377 "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19378 );
19379 assert_eq!(
19380 f.run(&[b"EXISTS", src]),
19381 ":0\r\n",
19382 "and the source is gone with its last element"
19383 );
19384
19385 f.run(&[b"RPUSH", src, b"e", b"f"]);
19388 assert_eq!(
19389 f.run(&[
19390 b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19391 ]),
19392 "*-1\r\n"
19393 );
19394 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19395 assert_eq!(
19396 f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19397 "$-1\r\n"
19398 );
19399 assert_eq!(
19400 f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19401 "*-1\r\n"
19402 );
19403
19404 f.run(&[b"SET", plain, b"v"]);
19408 assert_eq!(
19409 f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19410 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19411 );
19412 assert_eq!(
19413 f.run(&[b"LLEN", src]),
19414 ":2\r\n",
19415 "and left the source alone"
19416 );
19417 assert_eq!(
19418 f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19419 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19420 );
19421 assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19422 }
19423
19424 #[test]
19430 fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19431 let mut f = Fixture::striped(8);
19432 let other = apart(&mut f, "q");
19433 let (q, far) = (b"q".as_slice(), other.as_bytes());
19434
19435 assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19436 assert_eq!(f.server.waiters().len(), 1);
19437 f.run(&[b"RPUSH", far, b"v"]);
19438 let mut out = Out::new(Proto::Resp2);
19439 assert!(f.server.serve_waiter(0, 0, &mut out));
19440 let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19441 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19442 assert_eq!(
19443 f.run(&[b"EXISTS", far]),
19444 ":0\r\n",
19445 "and it took the element with it"
19446 );
19447
19448 f.server.waiters_mut().forget(7);
19451 assert_eq!(
19452 f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19453 Flow::Block
19454 );
19455 f.run(&[b"RPUSH", q, b"w"]);
19456 let mut out = Out::new(Proto::Resp2);
19457 assert!(f.server.serve_waiter(0, 0, &mut out));
19458 assert_eq!(
19459 core::str::from_utf8(out.as_slice()).expect("ascii"),
19460 "$1\r\nw\r\n"
19461 );
19462 assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19463 }
19464
19465 #[test]
19471 fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19472 let script: &[&[&[u8]]] = &[
19473 &[b"XADD", b"s", b"1-1", b"a", b"1"],
19474 &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19475 &[b"XADD", b"s", b"3-1", b"d", b"4"],
19476 &[b"XADD", b"s", b"1-1", b"e", b"5"],
19477 &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19478 &[b"XLEN", b"s"],
19479 &[b"XLEN", b"gone"],
19480 &[b"XRANGE", b"s", b"-", b"+"],
19481 &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19482 &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19483 &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19484 &[b"XREVRANGE", b"s", b"+", b"-"],
19485 &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19486 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19487 &[b"XREAD", b"STREAMS", b"s", b"$"],
19488 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19490 &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19491 &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19492 &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19493 &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19494 &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19495 &[
19496 b"XREADGROUP",
19497 b"GROUP",
19498 b"g",
19499 b"c1",
19500 b"COUNT",
19501 b"1",
19502 b"STREAMS",
19503 b"s",
19504 b"0",
19505 ],
19506 &[
19507 b"XREADGROUP",
19508 b"GROUP",
19509 b"nope",
19510 b"c1",
19511 b"STREAMS",
19512 b"s",
19513 b">",
19514 ],
19515 &[b"XPENDING", b"s", b"g"],
19516 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19517 &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19518 &[b"XPENDING", b"s", b"nope"],
19519 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19520 &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19521 &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19522 &[b"XACK", b"s", b"g", b"1-1"],
19523 &[b"XACK", b"s", b"g", b"1-1"],
19524 &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19525 &[b"XPENDING", b"s", b"g"],
19526 &[b"XINFO", b"STREAM", b"s"],
19527 &[b"XINFO", b"GROUPS", b"s"],
19528 &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19529 &[b"XINFO", b"STREAM", b"gone"],
19530 &[b"XDEL", b"s", b"3-1"],
19532 &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19533 &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19534 &[b"XADD", b"s", b"9-1", b"z", b"9"],
19535 &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19536 &[b"XTRIM", b"s", b"MINID", b"9"],
19537 &[b"XSETID", b"s", b"99-1"],
19538 &[b"XSETID", b"s", b"1-1"],
19539 &[b"XLEN", b"s"],
19540 &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19541 &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19542 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19543 &[b"XGROUP", b"DESTROY", b"s", b"g"],
19544 &[b"SET", b"plain", b"v"],
19546 &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19547 &[b"XLEN", b"plain"],
19548 &[b"XREAD", b"STREAMS", b"plain", b"0"],
19549 &[b"XRANGE", b"s", b"bogus", b"+"],
19550 &[b"XADD", b"s", b"1-1", b"a"],
19551 &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19552 &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19553 ];
19554
19555 let mut one = Fixture::new();
19556 let mut many = Fixture::striped(8);
19557 for parts in script {
19558 let a = one.run(parts);
19559 let b = many.run(parts);
19560 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19561 }
19562 }
19563
19564 #[test]
19570 fn a_stream_read_across_stripes_reads_every_key() {
19571 let mut f = Fixture::striped(8);
19572 let other = apart(&mut f, "s1");
19573 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19574
19575 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19576 f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19577 let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19578 assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19579 assert!(got.contains("1-1"), "the first one is in there: {got}");
19580 assert!(got.contains("2-1"), "and so is the second: {got}");
19581
19582 f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19585 let got = f.run(&[
19586 b"XREADGROUP",
19587 b"GROUP",
19588 b"g",
19589 b"c",
19590 b"STREAMS",
19591 s1,
19592 s2,
19593 b">",
19594 b">",
19595 ]);
19596 assert!(got.starts_with("-NOGROUP"), "{got}");
19597 assert_eq!(
19598 f.run(&[b"XPENDING", s1, b"g"]),
19599 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19600 "and read nothing from the key that did have the group"
19601 );
19602
19603 f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19604 let got = f.run(&[
19605 b"XREADGROUP",
19606 b"GROUP",
19607 b"g",
19608 b"c",
19609 b"STREAMS",
19610 s1,
19611 s2,
19612 b">",
19613 b">",
19614 ]);
19615 assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19616 }
19617
19618 #[test]
19620 fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19621 let mut f = Fixture::striped(8);
19622 let other = apart(&mut f, "s1");
19623 let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19624 f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19625 f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19626
19627 assert_eq!(
19628 f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19629 .0,
19630 Flow::Block
19631 );
19632 f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19633 let mut out = Out::new(Proto::Resp2);
19634 assert!(f.server.serve_waiter(0, 0, &mut out));
19635 let want = format!(
19636 "*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",
19637 other.len()
19638 );
19639 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19640 }
19641
19642 #[test]
19644 fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19645 let script: &[&[&[u8]]] = &[
19646 &[
19647 b"JSON.SET",
19648 b"d",
19649 b"$",
19650 br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19651 ],
19652 &[b"JSON.SET", b"d", b"$.a", b"2"],
19653 &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19654 &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19655 &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19656 &[b"JSON.GET", b"d"],
19657 &[b"JSON.GET", b"d", b"$.b"],
19658 &[b"JSON.GET", b"gone", b"$"],
19659 &[b"JSON.TYPE", b"d", b"$.b"],
19660 &[b"JSON.TYPE", b"d", b"$.s"],
19661 &[b"JSON.TOGGLE", b"d", b"$.t"],
19662 &[b"JSON.ARRLEN", b"d", b"$.b"],
19663 &[b"JSON.OBJLEN", b"d", b"$"],
19664 &[b"JSON.OBJKEYS", b"d", b"$"],
19665 &[b"JSON.STRLEN", b"d", b"$.s"],
19666 &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19667 &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19668 &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19669 &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19670 &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19671 &[b"JSON.ARRPOP", b"d", b"$.b"],
19672 &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19673 &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19674 &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19675 &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19676 &[b"JSON.RESP", b"d", b"$.b"],
19677 &[b"JSON.DEBUG", b"MEMORY", b"d"],
19678 &[b"JSON.CLEAR", b"d", b"$.b"],
19679 &[b"JSON.DEL", b"d", b"$.m"],
19680 &[b"JSON.FORGET", b"d", b"$.nothere"],
19681 &[
19683 b"JSON.MSET",
19684 b"m1",
19685 b"$",
19686 b"1",
19687 b"m2",
19688 b"$",
19689 b"2",
19690 b"m3",
19691 b"$",
19692 b"3",
19693 ],
19694 &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
19695 &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
19696 &[b"JSON.GET", b"m1", b"$"],
19697 &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
19698 &[b"JSON.GET", b"m2", b"$"],
19699 &[b"SET", b"plain", b"v"],
19701 &[b"JSON.GET", b"plain", b"$"],
19702 &[b"JSON.SET", b"plain", b"$", b"1"],
19703 &[b"JSON.MGET", b"m1", b"plain", b"$"],
19704 &[b"JSON.SET", b"d", b"$.b", b"["],
19705 &[b"JSON.DEL", b"plain"],
19706 ];
19707
19708 let mut one = Fixture::new();
19709 let mut many = Fixture::striped(8);
19710 for parts in script {
19711 let a = one.run(parts);
19712 let b = many.run(parts);
19713 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19714 }
19715 }
19716
19717 #[test]
19724 fn a_json_multi_write_across_stripes_reaches_every_key() {
19725 let mut f = Fixture::striped(8);
19726 let second = apart(&mut f, "m1");
19727 let third = apart(&mut f, &second);
19728 let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
19729
19730 assert_eq!(
19731 f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
19732 "+OK\r\n"
19733 );
19734 assert_eq!(
19735 f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
19736 "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
19737 );
19738
19739 assert_eq!(
19742 f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
19743 "-this is not the start of a value, at byte 0 of the JSON text\r\n"
19744 );
19745 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
19746
19747 assert_eq!(
19751 f.run(&[
19752 b"JSON.MSET",
19753 m1,
19754 b"$",
19755 b"9",
19756 m2,
19757 b"$.deep",
19758 b"9",
19759 m3,
19760 b"$",
19761 b"7"
19762 ]),
19763 "$-1\r\n"
19764 );
19765 assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
19766 assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
19767 assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
19768 }
19769
19770 #[test]
19772 fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
19773 let script: &[&[&[u8]]] = &[
19774 &[
19775 b"GEOADD",
19776 b"g",
19777 b"13.361389",
19778 b"38.115556",
19779 b"palermo",
19780 b"15.087269",
19781 b"37.502669",
19782 b"catania",
19783 ],
19784 &[
19785 b"GEOADD",
19786 b"g",
19787 b"NX",
19788 b"13.361389",
19789 b"38.115556",
19790 b"palermo",
19791 ],
19792 &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
19793 &[b"GEOPOS", b"g", b"palermo", b"nothere"],
19794 &[b"GEOHASH", b"g", b"palermo", b"catania"],
19795 &[b"GEODIST", b"g", b"palermo", b"catania"],
19796 &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
19797 &[b"GEODIST", b"g", b"palermo", b"nothere"],
19798 &[
19799 b"GEOSEARCH",
19800 b"g",
19801 b"FROMLONLAT",
19802 b"15",
19803 b"37",
19804 b"BYRADIUS",
19805 b"200",
19806 b"KM",
19807 b"ASC",
19808 b"WITHCOORD",
19809 b"WITHDIST",
19810 b"WITHHASH",
19811 ],
19812 &[
19813 b"GEOSEARCH",
19814 b"g",
19815 b"FROMMEMBER",
19816 b"palermo",
19817 b"BYBOX",
19818 b"400",
19819 b"400",
19820 b"KM",
19821 b"DESC",
19822 ],
19823 &[
19824 b"GEORADIUS",
19825 b"g",
19826 b"15",
19827 b"37",
19828 b"200",
19829 b"KM",
19830 b"COUNT",
19831 b"1",
19832 ],
19833 &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
19834 &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
19835 &[
19836 b"GEOSEARCHSTORE",
19837 b"dst",
19838 b"g",
19839 b"FROMLONLAT",
19840 b"15",
19841 b"37",
19842 b"BYRADIUS",
19843 b"200",
19844 b"KM",
19845 ],
19846 &[b"ZRANGE", b"dst", b"0", b"-1"],
19847 &[
19848 b"GEOSEARCHSTORE",
19849 b"dst",
19850 b"g",
19851 b"FROMLONLAT",
19852 b"15",
19853 b"37",
19854 b"BYRADIUS",
19855 b"1",
19856 b"M",
19857 b"STOREDIST",
19858 ],
19859 &[b"EXISTS", b"dst"],
19860 &[
19861 b"GEORADIUS",
19862 b"g",
19863 b"15",
19864 b"37",
19865 b"200",
19866 b"KM",
19867 b"STORE",
19868 b"dst",
19869 ],
19870 &[b"ZCARD", b"dst"],
19871 &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
19873 &[b"SET", b"plain", b"v"],
19874 &[b"GEOPOS", b"plain", b"a"],
19875 &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
19876 &[
19877 b"GEOSEARCHSTORE",
19878 b"dst",
19879 b"g",
19880 b"FROMLONLAT",
19881 b"15",
19882 b"37",
19883 b"BYRADIUS",
19884 b"200",
19885 b"KM",
19886 b"WITHCOORD",
19887 ],
19888 ];
19889
19890 let mut one = Fixture::new();
19891 let mut many = Fixture::striped(8);
19892 for parts in script {
19893 let a = one.run(parts);
19894 let b = many.run(parts);
19895 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19896 }
19897 }
19898
19899 #[test]
19901 fn a_geo_search_store_across_stripes_writes_what_it_found() {
19902 let mut f = Fixture::striped(8);
19903 let other = apart(&mut f, "g");
19904 let third = apart(&mut f, &other);
19905 let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
19906
19907 f.run(&[
19908 b"GEOADD",
19909 g,
19910 b"13.361389",
19911 b"38.115556",
19912 b"palermo",
19913 b"15.087269",
19914 b"37.502669",
19915 b"catania",
19916 ]);
19917 assert_eq!(
19918 f.run(&[
19919 b"GEOSEARCHSTORE",
19920 dst,
19921 g,
19922 b"FROMLONLAT",
19923 b"15",
19924 b"37",
19925 b"BYRADIUS",
19926 b"200",
19927 b"KM",
19928 b"ASC",
19929 ]),
19930 ":2\r\n"
19931 );
19932 assert_eq!(
19933 f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19934 "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
19935 "the geohash is the score, so the order is not the search order"
19936 );
19937 assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
19938
19939 assert_eq!(
19942 f.run(&[
19943 b"GEOSEARCHSTORE",
19944 dst,
19945 g,
19946 b"FROMMEMBER",
19947 b"palermo",
19948 b"BYRADIUS",
19949 b"200",
19950 b"KM",
19951 b"STOREDIST",
19952 ]),
19953 ":2\r\n"
19954 );
19955 assert_eq!(
19956 f.run(&[b"ZSCORE", dst, b"palermo"]),
19957 "$1\r\n0\r\n",
19958 "the centre is nought away from itself"
19959 );
19960
19961 assert_eq!(
19965 f.run(&[
19966 b"GEOSEARCHSTORE",
19967 dst,
19968 g,
19969 b"FROMLONLAT",
19970 b"0",
19971 b"0",
19972 b"BYRADIUS",
19973 b"1",
19974 b"M",
19975 ]),
19976 ":0\r\n"
19977 );
19978 assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19979 f.run(&[
19980 b"GEOSEARCHSTORE",
19981 dst,
19982 g,
19983 b"FROMLONLAT",
19984 b"15",
19985 b"37",
19986 b"BYRADIUS",
19987 b"200",
19988 b"KM",
19989 ]);
19990 f.run(&[b"SET", plain, b"v"]);
19991 assert_eq!(
19992 f.run(&[
19993 b"GEOSEARCHSTORE",
19994 dst,
19995 plain,
19996 b"FROMLONLAT",
19997 b"15",
19998 b"37",
19999 b"BYRADIUS",
20000 b"200",
20001 b"KM",
20002 ]),
20003 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
20004 );
20005 assert_eq!(
20006 f.run(&[b"ZCARD", dst]),
20007 ":2\r\n",
20008 "and left the destination"
20009 );
20010 }
20011
20012 #[test]
20018 fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
20019 let script: &[&[&[u8]]] = &[
20020 &[
20021 b"TS.CREATE",
20022 b"ts:a",
20023 b"LABELS",
20024 b"sensor",
20025 b"a",
20026 b"room",
20027 b"1",
20028 ],
20029 &[b"TS.CREATE", b"ts:a"],
20030 &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
20031 &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
20032 &[
20033 b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
20034 ],
20035 &[
20036 b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
20037 ],
20038 &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
20039 &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
20040 &[b"TS.GET", b"ts:a"],
20041 &[b"TS.GET", b"gone"],
20042 &[b"TS.RANGE", b"ts:a", b"-", b"+"],
20043 &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
20044 &[
20045 b"TS.RANGE",
20046 b"ts:a",
20047 b"-",
20048 b"+",
20049 b"AGGREGATION",
20050 b"avg",
20051 b"2000",
20052 ],
20053 &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
20054 &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20055 &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20056 &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
20057 &[b"TS.READ", b"ts:a", b"0"],
20058 &[b"TS.READ", b"ts:a", b"+"],
20059 &[b"TS.QUERYINDEX", b"sensor=a"],
20061 &[b"TS.QUERYINDEX", b"room=1"],
20062 &[b"TS.QUERYINDEX", b"room=9"],
20063 &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
20064 &[
20065 b"TS.QUERYLABELS",
20066 b"VALUES",
20067 b"sensor",
20068 b"FILTER",
20069 b"room=1",
20070 ],
20071 &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
20072 &[
20073 b"TS.MGET",
20074 b"SELECTED_LABELS",
20075 b"sensor",
20076 b"FILTER",
20077 b"sensor=a",
20078 ],
20079 &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
20080 &[
20081 b"TS.MREVRANGE",
20082 b"-",
20083 b"+",
20084 b"WITHLABELS",
20085 b"FILTER",
20086 b"sensor=a",
20087 ],
20088 &[
20089 b"TS.MRANGE",
20090 b"-",
20091 b"+",
20092 b"FILTER",
20093 b"room=1",
20094 b"GROUPBY",
20095 b"room",
20096 b"REDUCE",
20097 b"max",
20098 ],
20099 &[b"TS.INFO", b"ts:a"],
20100 &[
20102 b"TS.CREATERULE",
20103 b"ts:a",
20104 b"ts:down",
20105 b"AGGREGATION",
20106 b"avg",
20107 b"1000",
20108 ],
20109 &[b"TS.CREATE", b"ts:down"],
20110 &[
20111 b"TS.CREATERULE",
20112 b"ts:a",
20113 b"ts:down",
20114 b"AGGREGATION",
20115 b"avg",
20116 b"1000",
20117 ],
20118 &[b"TS.ADD", b"ts:a", b"5000", b"4"],
20119 &[b"TS.ADD", b"ts:a", b"6000", b"5"],
20120 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20121 &[b"TS.GET", b"ts:down", b"LATEST"],
20122 &[b"TS.INFO", b"ts:down"],
20123 &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
20124 &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20125 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20126 &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20127 &[b"TS.DEL", b"ts:a", b"0", b"1000"],
20128 &[b"SET", b"plain", b"v"],
20130 &[b"TS.ADD", b"plain", b"1", b"1"],
20131 &[b"TS.GET", b"plain"],
20132 &[b"TS.READ", b"plain", b"0"],
20133 &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
20134 &[b"TS.RANGE", b"gone", b"-", b"+"],
20135 &[b"TS.INFO", b"gone"],
20136 ];
20137
20138 let mut one = Fixture::new();
20139 let mut many = Fixture::striped(8);
20140 for parts in script {
20141 let a = one.run(parts);
20142 let b = many.run(parts);
20143 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20144 }
20145 }
20146
20147 #[test]
20157 fn a_compaction_rule_across_stripes_reaches_both_ends() {
20158 let mut many = Fixture::striped(8);
20159 let other = apart(&mut many, "src");
20160 let (src, dst) = (b"src".as_slice(), other.as_bytes());
20161 let mut one = Fixture::new();
20162 let mut both = |parts: &[&[u8]]| {
20163 let a = one.run(parts);
20164 let b = many.run(parts);
20165 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20166 a
20167 };
20168
20169 both(&[b"TS.CREATE", src]);
20170 both(&[b"TS.CREATE", dst]);
20171 assert_eq!(
20172 both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
20173 "+OK\r\n"
20174 );
20175 both(&[b"TS.ADD", src, b"1000", b"1"]);
20176 both(&[b"TS.ADD", src, b"1500", b"3"]);
20177 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20180 let open = both(&[b"TS.GET", dst, b"LATEST"]);
20181 assert!(open.contains(":1000"), "the open bucket is folded: {open}");
20182
20183 both(&[b"TS.ADD", src, b"2000", b"5"]);
20186 let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
20187 assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
20188 assert!(got.contains(":1000"), "{got}");
20189
20190 both(&[b"TS.DEL", src, b"1000", b"1999"]);
20192 assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20193
20194 assert!(
20197 both(&[b"TS.INFO", dst]).contains("src"),
20198 "the source is named"
20199 );
20200 assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
20201 assert_eq!(
20202 both(&[b"TS.DELETERULE", src, dst]),
20203 "-ERR TSDB: compaction rule does not exist\r\n"
20204 );
20205 }
20206
20207 #[test]
20209 fn a_label_query_across_stripes_finds_every_series() {
20210 let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
20211 let mut many = Fixture::striped(8);
20212 let mut homes: Vec<usize> = names
20213 .iter()
20214 .map(|name| many.server.striped(0).stripe_of(name))
20215 .collect();
20216 homes.sort_unstable();
20217 homes.dedup();
20218 assert!(homes.len() > 1, "the six keys are not all on one stripe");
20219
20220 let mut one = Fixture::new();
20221 let mut both = |parts: &[&[u8]]| {
20222 let a = one.run(parts);
20223 let b = many.run(parts);
20224 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20225 a
20226 };
20227 for name in &names {
20228 both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
20229 both(&[b"TS.ADD", name, b"1000", b"1"]);
20230 }
20231
20232 let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
20233 assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
20234 assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20235 assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20236 assert_eq!(
20237 both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
20238 "*1\r\n$4\r\nroom\r\n"
20239 );
20240 }
20241
20242 #[test]
20249 fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
20250 let script: &[&[&[u8]]] = &[
20251 &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
20252 &[b"HMSET", b"h", b"c", b"3"],
20253 &[b"HSETNX", b"h", b"a", b"9"],
20254 &[b"HSETNX", b"h", b"d", b"4"],
20255 &[b"HGET", b"h", b"a"],
20256 &[b"HGET", b"h", b"nope"],
20257 &[b"HMGET", b"h", b"a", b"nope"],
20258 &[b"HLEN", b"h"],
20259 &[b"HEXISTS", b"h", b"a"],
20260 &[b"HSTRLEN", b"h", b"a"],
20261 &[b"HGETALL", b"h"],
20262 &[b"HKEYS", b"h"],
20263 &[b"HVALS", b"h"],
20264 &[b"HINCRBY", b"h", b"a", b"5"],
20265 &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
20266 &[b"HSCAN", b"h", b"0"],
20267 &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
20268 &[b"HSCAN", b"h", b"0", b"NOVALUES"],
20269 &[b"HDEL", b"h", b"d"],
20270 &[b"HSET", b"one", b"f", b"v"],
20271 &[b"HRANDFIELD", b"one"],
20272 &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
20273 &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
20275 &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
20276 &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
20277 &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20278 &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20279 &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
20280 &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
20281 &[b"HGET", b"h", b"b"],
20282 &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
20284 &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
20285 &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
20286 &[b"HGET", b"h", b"e"],
20287 &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
20289 &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
20290 &[b"HGETALL", b"imp"],
20291 &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
20292 &[b"HIMPORT", b"DISCARD", b"fs"],
20293 &[b"SET", b"plain", b"v"],
20295 &[b"HSET", b"plain", b"a", b"1"],
20296 &[b"HGETALL", b"plain"],
20297 &[b"HGET", b"gone", b"a"],
20298 &[b"HINCRBY", b"h", b"a", b"nan"],
20299 ];
20300
20301 let mut one = Fixture::new();
20302 let mut many = Fixture::striped(8);
20303 one.server.set_clock_ms(1_700_000_000_000);
20307 many.server.set_clock_ms(1_700_000_000_000);
20308 for parts in script {
20309 let a = one.run(parts);
20310 let b = many.run(parts);
20311 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20312 }
20313 }
20314
20315 #[test]
20317 fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20318 let script: &[&[&[u8]]] = &[
20319 &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20320 &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20321 &[b"ARGET", b"a", b"1"],
20322 &[b"ARGET", b"a", b"99"],
20323 &[b"ARMGET", b"a", b"0", b"5", b"99"],
20324 &[b"ARGETRANGE", b"a", b"0", b"7"],
20325 &[b"ARLEN", b"a"],
20326 &[b"ARCOUNT", b"a"],
20327 &[b"ARINSERT", b"a", b"m", b"n"],
20328 &[b"ARSCAN", b"a", b"0", b"20"],
20329 &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20330 &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20331 &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20332 &[b"ARLASTITEMS", b"a", b"2"],
20333 &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20334 &[b"ARNEXT", b"a"],
20335 &[b"ARSEEK", b"a", b"3"],
20336 &[b"AROP", b"a", b"0", b"20", b"USED"],
20337 &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20338 &[b"ARINFO", b"a"],
20339 &[b"ARINFO", b"a", b"FULL"],
20340 &[b"ARDEL", b"a", b"0"],
20341 &[b"ARDELRANGE", b"a", b"1", b"2"],
20342 &[b"ARCOUNT", b"a"],
20343 &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20344 &[b"ARGETRANGE", b"r", b"0", b"9"],
20345 &[b"SET", b"plain", b"v"],
20347 &[b"ARGET", b"plain", b"0"],
20348 &[b"ARSET", b"plain", b"0", b"v"],
20349 &[b"ARGET", b"gone", b"0"],
20350 &[b"ARSET", b"a", b"bad", b"v"],
20351 ];
20352
20353 let mut one = Fixture::new();
20354 let mut many = Fixture::striped(8);
20355 for parts in script {
20356 let a = one.run(parts);
20357 let b = many.run(parts);
20358 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20359 }
20360 }
20361
20362 #[test]
20368 fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20369 let script: &[&[&[u8]]] = &[
20370 &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20371 &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20372 &[b"G.NADD", b"g", b"n3"],
20373 &[b"G.NGET", b"g", b"n1"],
20374 &[b"G.NGET", b"g", b"gone"],
20375 &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20376 &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20377 &[b"G.OUT", b"g", b"n1", b"knows"],
20378 &[b"G.IN", b"g", b"n2", b"knows"],
20379 &[b"G.DEG", b"g", b"n1", b"knows"],
20380 &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20381 &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20382 &[b"G.PATH", b"g", b"n1", b"n3"],
20383 &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20384 &[b"G.NDEL", b"g", b"n3"],
20385 &[b"G.NGET", b"g", b"n3"],
20386 &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20388 &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20389 &[b"VCARD", b"v"],
20390 &[b"VDIM", b"v"],
20391 &[b"VEMB", b"v", b"e1"],
20392 &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20393 &[b"VSIM", b"v", b"ELE", b"e1"],
20394 &[b"VISMEMBER", b"v", b"e1"],
20395 &[b"VISMEMBER", b"v", b"gone"],
20396 &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20397 &[b"VGETATTR", b"v", b"e1"],
20398 &[b"VRANGE", b"v", b"-", b"+"],
20399 &[b"VLINKS", b"v", b"e1"],
20400 &[b"VINFO", b"v"],
20401 &[b"VREM", b"v", b"e2"],
20402 &[b"VCARD", b"v"],
20403 &[b"SET", b"plain", b"v"],
20405 &[b"G.NGET", b"plain", b"n1"],
20406 &[b"VCARD", b"plain"],
20407 &[b"G.NADD", b"gone2", b"n"],
20408 &[b"VEMB", b"gone3", b"e"],
20409 ];
20410
20411 let mut one = Fixture::new();
20412 let mut many = Fixture::striped(8);
20413 for parts in script {
20414 let a = one.run(parts);
20415 let b = many.run(parts);
20416 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20417 }
20418 }
20419
20420 #[test]
20423 fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20424 let script: &[&[&[u8]]] = &[
20425 &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20427 &[b"BF.ADD", b"bf", b"a"],
20428 &[b"BF.ADD", b"bf", b"a"],
20429 &[b"BF.MADD", b"bf", b"b", b"c"],
20430 &[b"BF.EXISTS", b"bf", b"a"],
20431 &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20432 &[b"BF.CARD", b"bf"],
20433 &[b"BF.INFO", b"bf"],
20434 &[b"BF.INFO", b"bf", b"CAPACITY"],
20435 &[b"BF.DEBUG", b"bf"],
20436 &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20437 &[b"BF.EXISTS", b"made", b"x"],
20438 &[b"BF.SCANDUMP", b"bf", b"0"],
20439 &[b"CF.RESERVE", b"cf", b"100"],
20441 &[b"CF.ADD", b"cf", b"a"],
20442 &[b"CF.ADDNX", b"cf", b"a"],
20443 &[b"CF.COUNT", b"cf", b"a"],
20444 &[b"CF.EXISTS", b"cf", b"a"],
20445 &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20446 &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20447 &[b"CF.DEL", b"cf", b"a"],
20448 &[b"CF.COMPACT", b"cf"],
20449 &[b"CF.INFO", b"cf"],
20450 &[b"CF.DEBUG", b"cf"],
20451 &[b"CF.SCANDUMP", b"cf", b"0"],
20452 &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20454 &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20455 &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20456 &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20457 &[b"CMS.INFO", b"cms"],
20458 &[b"TOPK.RESERVE", b"tk", b"3"],
20460 &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20461 &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20462 &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20463 &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20464 &[b"TOPK.LIST", b"tk"],
20465 &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20466 &[b"TOPK.INFO", b"tk"],
20467 &[b"TDIGEST.CREATE", b"td"],
20469 &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20470 &[b"TDIGEST.MIN", b"td"],
20471 &[b"TDIGEST.MAX", b"td"],
20472 &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20473 &[b"TDIGEST.CDF", b"td", b"3"],
20474 &[b"TDIGEST.RANK", b"td", b"3"],
20475 &[b"TDIGEST.REVRANK", b"td", b"3"],
20476 &[b"TDIGEST.BYRANK", b"td", b"0"],
20477 &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20478 &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20479 &[b"TDIGEST.INFO", b"td"],
20480 &[b"TDIGEST.RESET", b"td"],
20481 &[b"TDIGEST.MIN", b"td"],
20482 &[b"SET", b"plain", b"v"],
20484 &[b"BF.ADD", b"plain", b"a"],
20485 &[b"CF.ADD", b"plain", b"a"],
20486 &[b"CMS.QUERY", b"plain", b"a"],
20487 &[b"TOPK.ADD", b"plain", b"a"],
20488 &[b"TDIGEST.ADD", b"plain", b"1"],
20489 &[b"CMS.INFO", b"gone"],
20490 &[b"TOPK.INFO", b"gone"],
20491 &[b"TDIGEST.INFO", b"gone"],
20492 ];
20493
20494 let mut one = Fixture::new();
20495 let mut many = Fixture::striped(8);
20496 for parts in script {
20497 let a = one.run(parts);
20498 let b = many.run(parts);
20499 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20500 }
20501 }
20502
20503 #[test]
20510 fn a_sketch_merge_across_stripes_reads_every_source() {
20511 let mut many = Fixture::striped(8);
20512 let other = apart(&mut many, "s1");
20513 let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20514 let mut one = Fixture::new();
20515 let mut both = |parts: &[&[u8]]| {
20516 let a = one.run(parts);
20517 let b = many.run(parts);
20518 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20519 a
20520 };
20521
20522 for key in [b"cd".as_slice(), s1, s2] {
20526 both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20527 }
20528 both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20529 both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20530 assert_eq!(
20531 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20532 "+OK\r\n",
20533 "the merge took both sources"
20534 );
20535 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20536 both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20538 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20539 both(&[b"SET", b"plain", b"v"]);
20541 assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20542 assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20543
20544 both(&[b"FLUSHALL"]);
20547 both(&[b"TDIGEST.CREATE", b"td"]);
20548 both(&[b"TDIGEST.CREATE", s1]);
20549 both(&[b"TDIGEST.CREATE", s2]);
20550 both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20551 both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20552 assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20553 assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20554 assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20555 }
20556
20557 #[test]
20564 fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20565 let script: &[&[&[u8]]] = &[
20566 &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20567 &[b"SORT", b"l"],
20568 &[b"SORT", b"l", b"DESC"],
20569 &[b"SORT", b"l", b"ALPHA"],
20570 &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20571 &[b"SORT_RO", b"l"],
20572 &[
20575 b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20576 ],
20577 &[b"SORT", b"l", b"BY", b"w_*"],
20578 &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20579 &[b"DEL", b"w_2"],
20580 &[b"SORT", b"l", b"BY", b"w_*"],
20581 &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20584 &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20585 &[b"HSET", b"h_1", b"f", b"9"],
20587 &[b"HSET", b"h_2", b"f", b"8"],
20588 &[b"HSET", b"h_3", b"f", b"7"],
20589 &[b"HSET", b"h_10", b"f", b"6"],
20590 &[b"SORT", b"l", b"BY", b"h_*->f"],
20591 &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20592 &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20594 &[b"LRANGE", b"out", b"0", b"-1"],
20595 &[b"SORT", b"l", b"STORE", b"l"],
20596 &[b"LRANGE", b"l", b"0", b"-1"],
20597 &[b"SORT", b"missing", b"STORE", b"out"],
20600 &[b"EXISTS", b"out"],
20601 &[b"SADD", b"s", b"c", b"a", b"b"],
20604 &[b"SORT", b"s", b"ALPHA"],
20605 &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20606 &[b"LRANGE", b"out", b"0", b"-1"],
20607 &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20608 &[b"SORT", b"z", b"BY", b"nosort"],
20609 &[b"SORT", b"z", b"ALPHA", b"DESC"],
20610 &[b"SET", b"str", b"v"],
20613 &[b"SORT", b"str"],
20614 &[b"RPUSH", b"words", b"one", b"two"],
20615 &[b"SORT", b"words"],
20616 &[b"SORT_RO", b"l", b"STORE", b"out"],
20617 ];
20618
20619 let mut one = Fixture::new();
20620 let mut many = Fixture::striped(8);
20621 for parts in script {
20622 let a = one.run(parts);
20623 let b = many.run(parts);
20624 assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20625 }
20626 }
20627
20628 #[test]
20635 fn a_sort_across_stripes_reads_every_pattern_key() {
20636 let mut f = Fixture::striped(8);
20637 let out = apart(&mut f, "l");
20638 let (list, dest) = (b"l".as_slice(), out.as_bytes());
20639
20640 f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20641 f.run(&[
20642 b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20643 ]);
20644 f.run(&[
20645 b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20646 ]);
20647
20648 let db = f.server.striped(0);
20651 let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20652 .iter()
20653 .map(|k| db.stripe_of(k.as_slice()))
20654 .collect();
20655 assert!(
20656 weights.iter().any(|s| *s != weights[0]),
20657 "the four weight keys all landed on one stripe, so this proves nothing"
20658 );
20659
20660 assert_eq!(
20661 f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
20662 "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
20663 "the order came off the weights and the answer off the data keys"
20664 );
20665 assert_eq!(
20666 f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20667 ":4\r\n"
20668 );
20669 assert_eq!(
20670 f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20671 "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20672 "the destination is on a stripe of its own and got the whole answer"
20673 );
20674 }
20675
20676 #[test]
20684 fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20685 let mut f = Fixture::striped(8);
20686 let other = apart(&mut f, "h");
20687 let (first, second) = (b"h".as_slice(), other.as_bytes());
20688
20689 assert_eq!(
20690 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
20691 "+OK\r\n"
20692 );
20693 assert_eq!(
20694 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
20695 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
20696 "the read comes off one stripe and has to answer for all of them"
20697 );
20698 for key in [first, second] {
20699 f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
20700 assert_eq!(
20701 f.run(&[b"OBJECT", b"ENCODING", key]),
20702 "$8\r\nlistpack\r\n",
20703 "two fields is still under the ladder"
20704 );
20705 f.run(&[b"HSET", key, b"c", b"3"]);
20706 assert_eq!(
20707 f.run(&[b"OBJECT", b"ENCODING", key]),
20708 "$9\r\nhashtable\r\n",
20709 "three fields is over it, on whichever stripe the key is on"
20710 );
20711 }
20712
20713 assert_eq!(
20716 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
20717 "+OK\r\n"
20718 );
20719 let db = f.server.striped(0);
20720 assert!(
20721 db.stripes_mut().all(|s| s.policy().name() == "allkeys-lru"),
20722 "a stripe kept the old policy"
20723 );
20724 }
20725}