Skip to main content

yo_resp/dispatch/
mod.rs

1//! From a decoded command to a written reply.
2//!
3//! This is the layer Y23 exists to keep thin. The wire and the embedded API
4//! both have to reach the same code, or there are two implementations of `INCR`
5//! and one of them is wrong. So `yo-kv` holds one method per command taking
6//! ordinary Rust values, and everything here is about the part that is only
7//! true on a socket: which keyword goes where, which combinations a real server
8//! refuses, and which of the two protocols the answer is spelled in.
9//!
10//! # What runs a command
11//!
12//! [`Server`] holds the databases. [`Session`] holds what one connection has
13//! chosen: which database, which name it gave itself, what its id is. The
14//! protocol version lives in the [`Out`] because that is what needs it, and
15//! `HELLO` changes it there.
16//!
17//! ```
18//! use yo_resp::{Argv, Limits, Out, Proto};
19//! use yo_resp::dispatch::{Args, Flow, Server, Session, execute};
20//!
21//! let mut server = Server::new();
22//! let mut session = Session::new(1);
23//! let mut out = Out::new(Proto::Resp2);
24//!
25//! let wire = b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n";
26//! let mut argv = Argv::new();
27//! argv.decode(wire, &Limits::default())?;
28//! let flow = execute(&mut server, &mut session, Args::new(&argv, wire), &mut out);
29//!
30//! assert_eq!(flow, Flow::Continue);
31//! assert_eq!(out.as_slice(), b"+OK\r\n");
32//! # Ok::<(), yo_resp::ProtocolError>(())
33//! ```
34//!
35//! # Errors are values until the last moment
36//!
37//! A command body returns a [`Result`], and this module turns the error into
38//! the line that goes on the wire. That is what keeps the same body usable from
39//! the embedded API, where an error is a value with a [`Code`] on it and not a
40//! sentence to be parsed.
41//!
42//! The reply buffer is rolled back to where it was before a failing command
43//! wrote anything, so a body that checks its arguments halfway through cannot
44//! leave half a reply in front of the error.
45//!
46//! # Nothing here allocates
47//!
48//! Arguments are slices of the connection's read buffer, keywords are compared
49//! in place, numbers are written straight into the reply, and the pairs of
50//! `MSET` reach the store as an iterator rather than a `Vec`. The two places
51//! that do allocate, an error message and the text of `INFO`, say so and wrap
52//! it, because a shard thread that allocates aborts.
53
54mod 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
103/// How many databases a server has.
104///
105/// Redis's default is sixteen and its `databases` setting can change it. Ours
106/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
107/// constant. Nothing in the design needs the number to be fixed; nothing yet
108/// needs it not to be.
109pub const DATABASES: usize = 16;
110
111/// Every database's bit in [`Server::dirty`], which is what a fresh server
112/// starts on so that the first maintenance turn asks all of them.
113///
114/// A `u64` holds sixteen bits with room to spare, and the assertion below is
115/// what turns raising [`DATABASES`] past sixty four into a build failure rather
116/// than a shift that silently drops the databases past the end.
117const ALL_DATABASES: u64 = if DATABASES == 64 {
118    u64::MAX
119} else {
120    (1u64 << DATABASES) - 1
121};
122const _: () = assert!(DATABASES <= 64);
123
124/// How many keys one command throws away before it leaves the rest to the next.
125///
126/// A bound and not a loop to the end, because this runs in front of a client
127/// that is waiting for its reply, and a server a long way over its limit would
128/// otherwise hold that client for as long as it took to walk all the way back
129/// under. Sixty four is a batch's worth of commands, so a server that went over
130/// by what one batch allocated comes back under in one command, and a server
131/// whose limit was just cut in half works through it over the next few thousand
132/// rather than in one long stall. Redis bounds the same loop by a time slice
133/// instead of a count and hands the rest to a timer; there is no timer here, so
134/// the rest goes to the next command that runs.
135const EVICT_BUDGET: usize = 64;
136
137/// The `maxstore` a server with no storage limit carries.
138///
139/// Sixteen exabytes, which is every disk there is and then some, so a server
140/// that set a limit this high and a server that set none behave the same way and
141/// the only difference is what `CONFIG GET maxstore` says. Zero cannot be the
142/// sentinel because zero is a limit with a meaning: nothing may live on the
143/// file.
144const NO_MAXSTORE: u64 = u64::MAX;
145
146/// What a server says to a command that would allocate when it has no room.
147///
148/// Redis's `shared.oomerr`, word for word including the full stop, because
149/// clients match on the `OOM` prefix and people match on the sentence.
150const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
151
152/// What the connection should do after a command.
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum Flow {
155    /// Read the next command.
156    Continue,
157    /// Write what is buffered and then close, which is what `QUIT` asks for.
158    Close,
159    /// Nothing was written and nothing is owed yet.
160    ///
161    /// The client is on the waiter list and its reply comes when a key it named
162    /// has something in it or when its deadline passes, whichever happens first.
163    /// Until then the connection stops reading commands, because a client that
164    /// is waiting for an answer is not a client that has sent another question.
165    Block,
166}
167
168/// A number one thread adds to and any thread may read.
169///
170/// The add is a load, an add and a store rather than a fetch and add, which on
171/// x86 is three ordinary instructions instead of one locked one. That is sound
172/// because every counter here has exactly one writer, which is what the slots
173/// below are for: two threads never hold the same counter, so nothing can be
174/// lost between the load and the store. A reader can be a command or two behind,
175/// and `INFO` on a running server is behind by the time the reply reaches the
176/// client anyway.
177#[derive(Debug, Default)]
178pub struct Counter(AtomicU64);
179
180impl Counter {
181    /// One more.
182    fn bump(&self) {
183        self.0.store(self.get().wrapping_add(1), Relaxed);
184    }
185
186    /// One fewer, stopping at zero.
187    ///
188    /// The floor is for the gauge, which is the number of open connections: a
189    /// close that arrives without its open, which nothing can do now and a
190    /// misplaced call could, is a number that stays at zero rather than one
191    /// that wraps to eighteen quintillion clients.
192    fn drop_one(&self) {
193        self.0.store(self.get().saturating_sub(1), Relaxed);
194    }
195
196    /// What it says.
197    fn get(&self) -> u64 {
198        self.0.load(Relaxed)
199    }
200
201    /// Back to zero, which is `CONFIG RESETSTAT`.
202    fn zero(&self) {
203        self.0.store(0, Relaxed);
204    }
205}
206
207/// The numbers `INFO` reports that this layer cannot see for itself.
208///
209/// The reactor owns the sockets, so the reactor is what knows how many clients
210/// there are. It counts them here and nothing else does anything with them
211/// except report them.
212#[derive(Debug, Default)]
213pub struct Stats {
214    /// Connections open right now.
215    clients: Counter,
216    /// Connections accepted since the server started.
217    connections: Counter,
218    /// Commands run since the server started, which this layer counts itself.
219    commands: Counter,
220}
221
222impl Stats {
223    /// A connection arrived.
224    pub fn opened(&self) {
225        self.clients.bump();
226        self.connections.bump();
227    }
228
229    /// A connection went away.
230    pub fn closed(&self) {
231        self.clients.drop_one();
232    }
233}
234
235/// Every thread's [`Stats`] added together, which is what `INFO` answers.
236#[derive(Debug, Clone, Copy, Default)]
237pub struct Totals {
238    /// Connections open right now.
239    pub clients: u64,
240    /// Connections accepted since the server started.
241    pub connections: u64,
242    /// Commands run since the server started.
243    pub commands: u64,
244}
245
246thread_local! {
247    /// Which set of counters the running thread writes into.
248    ///
249    /// Claimed the first time a thread counts anything and kept for as long as
250    /// the thread runs. It is a number rather than a pointer, so a thread that
251    /// has counted on one server and then counts on another lands in the same
252    /// place in both, and a process with two servers in it shares the numbering
253    /// between them. That is the tests and it is not `yodb`, which has one.
254    static SLOT: Cell<usize> = const { Cell::new(usize::MAX) };
255}
256
257/// What one thread keeps to itself.
258///
259/// One of these per thread and not one per server, because a number every
260/// thread writes to is a cache line every thread has to own to write to it, and
261/// at a few million commands a second that one line is the server. So each
262/// thread writes into its own and whoever needs the whole picture, which is
263/// `INFO` and the maintenance turn, puts the pieces together when it asks.
264///
265/// A cache line apart for the same reason, so that two threads writing at once
266/// are not two threads passing one line back and forth.
267#[derive(Debug, Default)]
268#[repr(align(64))]
269struct Local {
270    /// What the reactor counts.
271    stats: Stats,
272    /// A counter per command, for `INFO commandstats`.
273    cmdstats: CommandStats,
274    /// Which databases this thread has run a command against since the
275    /// maintenance turn last took the mask.
276    ///
277    /// One bit per database. The thread ors into it and the turn takes the whole
278    /// of it with a swap, which is what keeps a mark that lands during the swap
279    /// from being lost: the worst that can happen is a bit the turn has already
280    /// taken being set again, and that costs one more look at a database with
281    /// nothing to collect.
282    dirty: AtomicU64,
283}
284
285impl Local {
286    /// Note that a command has run against these databases.
287    fn mark(&self, dbs: u64) {
288        self.dirty.store(self.dirty.load(Relaxed) | dbs, Relaxed);
289    }
290}
291
292/// Room for one thread, which is what a server starts with.
293fn one_thread() -> Box<[Local]> {
294    slots(1)
295}
296
297/// Room for `threads` of them.
298fn slots(threads: usize) -> Box<[Local]> {
299    (0..threads.max(1)).map(|_| Local::default()).collect()
300}
301
302/// Where the process was started, which is what `dir` defaults to.
303///
304/// A dot if the working directory cannot be read, which happens when it has
305/// been deleted out from under a running process. That is not a reason to
306/// refuse to start a server, and it leaves `BACKUP` to fail with the real error
307/// from the filesystem if anybody asks for one.
308fn working_dir() -> PathBuf {
309    std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
310}
311
312/// One command's counters, for `INFO commandstats`.
313///
314/// Three of Redis's five. `usec` and `usec_per_call` are not here because
315/// nothing times a command, and timing one means two clock reads around a call
316/// that takes tens of nanoseconds to begin with. Redis pays that because Redis
317/// has room for it; this does not, and a zero under a name that says microseconds
318/// is worse than an absent field, which is the same rule the rest of `INFO`
319/// follows.
320#[derive(Debug, Clone, Copy, Default)]
321pub struct CommandStat {
322    /// Times the command ran, whatever it answered.
323    pub calls: u64,
324    /// Times it was turned away before it ran, which is the wrong number of
325    /// arguments or no room under `maxmemory`.
326    pub rejected: u64,
327    /// Times it ran and answered with an error.
328    pub failed: u64,
329}
330
331impl CommandStat {
332    /// Whether this command has ever been seen.
333    ///
334    /// A row that has not is left out of the reply, which is what Redis does and
335    /// is why the section is a handful of lines on a working server rather than
336    /// one line per command in the table.
337    const fn seen(&self) -> bool {
338        self.calls != 0 || self.rejected != 0 || self.failed != 0
339    }
340}
341
342/// One command's counters as one thread keeps them.
343///
344/// The same three numbers as [`CommandStat`], which is what they add up to when
345/// `INFO` asks. This is the written form and that is the read one.
346#[derive(Debug, Default)]
347struct Row {
348    /// Times the command ran.
349    calls: Counter,
350    /// Times it was turned away before it ran.
351    rejected: Counter,
352    /// Times it ran and answered with an error.
353    failed: Counter,
354}
355
356/// A counter per command, indexed the way [`table::index_of`] says.
357///
358/// A flat array and not a map, because the dispatcher is already holding the
359/// spec and the spec's position in the table is two addresses subtracted. That
360/// makes the counting a load, an add and a store on a row the previous command
361/// of the same name has already pulled into cache.
362#[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    /// The row for one command.
373    fn at(&self, spec: &'static Spec) -> &Row {
374        &self.0[table::index_of(spec)]
375    }
376}
377
378/// Where a database gets its store from, asked by database number.
379///
380/// `None` means that database cannot have one. The caller owns whatever the
381/// stores are cut out of, which for `yodb` is one `.yo` file with a log per
382/// database, and this crate never learns what any of that is.
383pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
384
385/// Everything a server holds.
386///
387/// One of these per shard thread, not one per process: the databases inside are
388/// not `Sync` and are reached by sending their thread a command. What makes
389/// this a server rather than a shard is that it is the whole of what a
390/// connection can address.
391pub struct Server {
392    dbs: Vec<Db>,
393    /// How many stripes each database is cut into, the same for all of them.
394    ///
395    /// Kept here as well as in each database so that the flat slot arithmetic
396    /// below is a multiply and a divide against a field on the server rather
397    /// than a walk asking each database how wide it is.
398    width: usize,
399    clock: Clock,
400    started_ms: u64,
401    /// Where the next maintenance turn starts looking, so that a database
402    /// under constant write load cannot hold the other fifteen's space.
403    next_db: usize,
404    /// One bit per database, set when a command ran against it.
405    ///
406    /// The maintenance turn after every batch used to ask all sixteen
407    /// databases whether they had anything to collect, and asking costs a load
408    /// and a store in each one. Fifteen of those are cold lines on a server
409    /// where every client is on database zero, which is every server, and the
410    /// answer is no every time. This is the cheap half of the question: a
411    /// database nobody has touched since it last said no cannot have started
412    /// saying yes.
413    ///
414    /// The maintenance turn's own mask and not a shared one. Threads mark what
415    /// they have touched in [`Local::dirty`] and the turn takes those with
416    /// [`Server::collect_marks`] before it reads this, so nothing on a command
417    /// path writes here.
418    dirty: u64,
419    /// What the connections are holding, kept by the engine.
420    conn_bytes: usize,
421    /// The `maxmemory` limit in bytes, zero when there is not one.
422    ///
423    /// Zero is the default and it is the whole reason the check in front of
424    /// every write is one comparison against a field that is already warm. It
425    /// is read by every command on every thread and written by a client that
426    /// sends `CONFIG SET`, so it is a number the threads can share rather than
427    /// a field one of them owns.
428    maxmemory: AtomicU64,
429    /// Where a database gets a store from the first time it needs one.
430    ///
431    /// A closure and not a store, because there are sixteen databases and a
432    /// server that fills memory on database zero should not have opened
433    /// anything for the other fifteen. Nothing is asked of this until a memory
434    /// limit is actually reached, so a server that never fills memory never
435    /// opens a file, and a server that has no file never has one of these.
436    ///
437    /// `None` from the closure means that database cannot have one, which is
438    /// how the caller says the file it opened has no more room for logs.
439    store: Option<Box<StoreSource>>,
440    /// The `maxstore` limit in bytes, `None` when there is not one.
441    ///
442    /// The storage limit, and the other half of the inversion `14` section 4.1
443    /// describes. `maxmemory` is a limit on memory and the right answer to a
444    /// memory limit on a system with a file under it is to move data to the
445    /// file, not to delete it. Deleting is the right answer to a limit on the
446    /// file, and this is that limit.
447    ///
448    /// Zero is not "no limit" here, which is the one place this reads
449    /// differently from `maxmemory` and is the difference that makes a drop in
450    /// cache possible. A storage budget of zero bytes means nothing may live on
451    /// the file, so migration cannot make room and eviction is the only thing
452    /// left, which is Redis exactly. `None` is no limit and is the default,
453    /// which with `noeviction` means the database grows until the disk is full
454    /// and then writes fail, which is what a database does.
455    ///
456    /// Shared between the threads the same way `maxmemory` is, and no limit is
457    /// [`NO_MAXSTORE`] rather than a second field saying whether the first one
458    /// counts. Two fields cannot be read as one, and a limit that was on when
459    /// the bytes were read and off by the time the number was is a limit that
460    /// answers from a server that never existed.
461    maxstore: AtomicU64,
462    /// What [`Server::memory_bytes`] said at the last maintenance turn.
463    ///
464    /// The reading is a walk over every collection in every database and cannot
465    /// go on a command path, so the command path reads this instead and is at
466    /// most one batch behind. What that costs is overshoot: a server can end a
467    /// batch holding one batch's worth of allocation more than its limit before
468    /// anything notices. A batch is 64 commands, so that is bounded by what 64
469    /// commands can allocate and not by how long the server runs.
470    ///
471    /// Only kept up to date when there is a limit to judge it against. A server
472    /// with no `maxmemory` never reads it and never pays for it.
473    used: usize,
474    /// Which database the next eviction draws from.
475    ///
476    /// Its own cursor and not [`Server::next_db`], because eviction and
477    /// compaction move at different rates and sharing one would make the
478    /// database that gets compacted depend on how many keys were evicted.
479    evict_db: usize,
480    /// Which database the next active expiry sweep starts at.
481    ///
482    /// A third cursor for the same reason there is a second one. A sweep runs on
483    /// every turn of the loop and compaction runs when there is dead space, so
484    /// sharing a cursor would make which database gets swept depend on which one
485    /// was last collected.
486    expire_db: usize,
487    /// The millisecond the last active expiry sweep ran on, so the next one on
488    /// the same millisecond does not bother.
489    expire_ms: u64,
490    /// Clients parked on a blocking command.
491    waiters: Waiters,
492    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
493    ///
494    /// Empty on a server nobody has migrated a key out of, which is nearly all
495    /// of them, and it costs a vector's three words to be empty.
496    peers: migrate::Peers,
497    /// What each thread that runs commands here keeps to itself.
498    ///
499    /// A fixed list, because a thread reading its own entry must not have the
500    /// list move under it, and how many threads there will be is known before
501    /// any of them starts. A server nobody told otherwise has one.
502    locals: Box<[Local]>,
503    /// How many entries have been handed out.
504    claimed: AtomicUsize,
505    /// Where `BACKUP` puts its files, and where `CONFIG GET dir` points.
506    ///
507    /// Absolute, and resolved once when the server is built rather than every
508    /// time somebody asks. `BACKUP LIST` answers absolute paths and a client is
509    /// entitled to hand one of them to a copy tool, so a relative path that
510    /// meant something different after a `chdir` would be a path that stops
511    /// working for reasons nobody could see.
512    dir: PathBuf,
513    /// What backup is running, if one is.
514    ///
515    /// On the server and not on a session, because a backup outlives the
516    /// connection that asked for it and any other connection can seal it.
517    backup: backup::State,
518    /// The search indexes and the names pointing at them.
519    ///
520    /// On the server and not on a database, which is the one collection in this
521    /// build that is. A real server keeps its indexes in the search module, the
522    /// module has one table, and `SELECT 1` followed by `FT._LIST` lists the
523    /// indexes made on database zero. `search.rs` has the rest of why.
524    ///
525    /// A server nobody has made an index on holds two empty vectors here, which
526    /// is six words and no allocation.
527    search: Registry,
528    /// Set by `SHUTDOWN`, and read by whatever is turning the loop.
529    ///
530    /// A flag rather than an exit, because the command layer is not what owns
531    /// the process. It runs inside a batch that has other commands behind it
532    /// and inside a driver that has a socket file to take away and a file to
533    /// close, and a server that calls `exit` from a command handler skips all
534    /// of that. So the command says stop and the driver stops, on the same turn
535    /// and through the same door a signal uses.
536    stopping: AtomicBool,
537}
538
539impl Server {
540    /// A server with [`DATABASES`] empty databases on the system clock.
541    #[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    /// A server whose databases are cut into `width` stripes each.
571    ///
572    /// Not reachable from the command line yet. Every command group answers on
573    /// a server of any width now and so does everything that walks a whole
574    /// database, and the tests run each group at a width of one and a width of
575    /// eight and check the two agree.
576    ///
577    /// What is left before this is what `--threads` sets is the engine. A
578    /// database being several objects is what makes more than one thread
579    /// possible, and it is not what makes more than one thread happen.
580    #[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    /// A server on a clock the caller moves by hand, for tests.
592    #[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    /// One database, by index.
621    ///
622    /// A caller that knows which key it wants names the one stripe the key is
623    /// on rather than working over the whole thing, which is what `at` and its
624    /// neighbours on [`Db`] are for. A caller that is about a database rather
625    /// than about a key, which is the snapshot walk and a setting, works over
626    /// all of them.
627    ///
628    /// The borrow is mutable, so the database is marked as having had something
629    /// run against it. Anything that only reads has [`Server::striped_ref`] and
630    /// does not come through here.
631    ///
632    /// # Panics
633    ///
634    /// If `i` is not a database. `SELECT` is the only way a client changes the
635    /// index and it checks, so an index that is out of range here is a bug in
636    /// the caller and not something a client can ask for.
637    pub fn striped(&mut self, i: usize) -> &mut Db {
638        self.dirty |= 1u64 << i;
639        &mut self.dbs[i]
640    }
641
642    /// Every keyspace on the server, which is every stripe of every database.
643    ///
644    /// What the aggregates walk. A total over the whole server is a total over
645    /// all of these and the stripe boundaries do not appear in it, which is
646    /// what makes the numbers `INFO` reports the same numbers whatever the
647    /// server was cut into.
648    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    /// The same, mutably.
655    fn keyspaces_mut(&mut self) -> impl Iterator<Item = &mut Keyspace> {
656        self.dbs.iter_mut().flat_map(Db::stripes_mut)
657    }
658
659    /// How many keyspaces there are, counting every stripe of every database.
660    ///
661    /// The maintenance turns walk these rather than the databases, because a
662    /// stripe is the thing that holds an arena and a deadline heap and so it is
663    /// the thing that has anything to collect.
664    const fn slots(&self) -> usize {
665        DATABASES * self.width
666    }
667
668    /// Which database slot `i` belongs to.
669    const fn slot_db(&self, i: usize) -> usize {
670        i / self.width
671    }
672
673    /// Keyspace `i` of [`Server::slots`].
674    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    /// The same, without taking it mutably.
680    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    /// Where `BACKUP` writes and what `CONFIG GET dir` answers.
686    #[must_use]
687    pub fn dir(&self) -> &Path {
688        &self.dir
689    }
690
691    /// Point the server at a different directory, which `yodb serve --dir` does.
692    ///
693    /// Only before it is serving. There is no `CONFIG SET dir` here and there
694    /// is none on a real server either without turning protected configs on,
695    /// for the good reason that moving it out from under a running backup would
696    /// leave files nothing can find again.
697    pub fn set_dir(&mut self, dir: PathBuf) {
698        self.dir = dir;
699    }
700
701    /// Drop a sealed backup that has outlived `backup-sealed-ttl`.
702    ///
703    /// Once per batch, from the same maintenance turn that collects the arena.
704    /// It reads two fields and returns on a server that has never taken a
705    /// backup, which is nearly all of them.
706    pub fn backup_expire(&mut self) {
707        backup::expire(self);
708    }
709
710    /// Ask for the server to stop, which is what `SHUTDOWN` does.
711    ///
712    /// It sets a flag and returns. Nothing here closes a socket, flushes a file
713    /// or ends the process, because none of those belong to this layer, and a
714    /// batch that is halfway through still has to finish and be written out.
715    pub fn stop(&self) {
716        self.stopping.store(true, Release);
717    }
718
719    /// Whether somebody has asked the server to stop.
720    ///
721    /// Read once per turn by the loop, next to the flag a signal sets. The two
722    /// mean the same thing and are separate only because one arrives from the
723    /// operating system and the other from a client.
724    #[must_use]
725    pub fn stopping(&self) -> bool {
726        self.stopping.load(Acquire)
727    }
728
729    /// One database, by index, without taking it mutably.
730    ///
731    /// What the prefetch stage needs. It runs for all 64 commands in a batch
732    /// before any of them executes, so it cannot hold the mutable borrow `run`
733    /// is about to want, and it does not need one: warming a cache line reads
734    /// nothing and changes nothing.
735    #[must_use]
736    pub fn striped_ref(&self, i: usize) -> &Db {
737        &self.dbs[i]
738    }
739
740    /// The stripe that answers for a database when a setting is read back.
741    ///
742    /// A ladder setting and an eviction policy are one number on a real server,
743    /// and the fact that every stripe of every database carries a copy of it is
744    /// ours rather than the client's problem. A write puts the same value on
745    /// every one of them, so any stripe answers for all of them and this is the
746    /// first one.
747    fn settings(&self) -> Held<'_, Keyspace> {
748        self.dbs[0].hold_stripe(0)
749    }
750
751    /// Take a new clock reading and give it to every database.
752    ///
753    /// Once per turn of the event loop, which is the only place time moves. A
754    /// command asking what the time is gets the answer the whole batch got, so
755    /// two keys written by the same batch expire together (`04` section 3).
756    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    /// Move every clock here on by `ms`, for tests about expiry.
765    ///
766    /// The same thing [`Server::set_clock_ms`] does and by the same argument,
767    /// except that it moves from wherever the clock is rather than to a stated
768    /// moment, which is what a test that wants a key to have expired asks for.
769    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    /// Move every clock here to `ms` by hand, for tests about expiry.
775    ///
776    /// A test cannot wait a hundred seconds and a test that waits a hundred
777    /// milliseconds is a test that fails on a loaded machine, so time moves on
778    /// request. The system clock underneath will overwrite this on the next
779    /// [`Server::refresh_clock`], which is why this is only useful in a test
780    /// that drives commands directly rather than through the event loop.
781    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    /// Seconds since this server was built.
789    #[must_use]
790    pub fn uptime_secs(&self) -> u64 {
791        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
792    }
793
794    /// Bytes held by every database's index and arena, plus the read and reply
795    /// buffers of every connection.
796    ///
797    /// The buffers are in here because they are real and because Redis counts
798    /// its own, so leaving them out would make the one number people compare
799    /// flattering rather than true. They are not a database, so nothing in the
800    /// keyspace can change them and the engine has to say when they move.
801    #[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    /// What the keyspace itself is holding, live records only.
807    ///
808    /// `used_memory` minus this is what the store costs to run: the index, the
809    /// space dead records are sitting in until compaction gets to them, and the
810    /// connections' buffers.
811    #[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    /// Bytes the arenas are holding, live and dead together.
819    #[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    /// Bytes the indexes are holding.
827    #[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    /// What arena compaction has cost, across every database.
835    ///
836    /// The write amplification of value separation, which is invisible from the
837    /// outside otherwise: a client that writes a megabyte can leave the store
838    /// copying several more, and the only sign of it without these is that the
839    /// writes got slower.
840    #[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    /// Arena segments whose pages are real, across every database.
853    #[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    /// What the connections' read and reply buffers are holding.
861    #[must_use]
862    pub const fn conn_bytes(&self) -> usize {
863        self.conn_bytes
864    }
865
866    /// Note that the connections are holding `delta` bytes more than they were,
867    /// or fewer when it is negative.
868    ///
869    /// A delta and not a total because the alternative is a walk over every
870    /// connection, and the walk would have to happen on a turn of the loop
871    /// rather than when `INFO` asks, which puts the cost of a report on the
872    /// command path of a server nobody is asking.
873    pub fn note_conn_bytes(&mut self, delta: isize) {
874        self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
875    }
876
877    /// Keys reclaimed by running into them after their deadline.
878    #[must_use]
879    pub fn expired_keys(&self) -> u64 {
880        self.keyspaces().map(|db| db.expired_keys()).sum()
881    }
882
883    /// Keys thrown away to make room, which is the other number entirely.
884    #[must_use]
885    pub fn evicted_keys(&self) -> u64 {
886        self.keyspaces().map(|db| db.evicted_keys()).sum()
887    }
888
889    /// Every command that has been seen, with its counters.
890    ///
891    /// Only the ones that have. A server reports a handful of lines rather than
892    /// one per command in the table, which is what Redis does and is the
893    /// difference between a section a person can read and one they cannot.
894    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    /// One command's counters, added up over every thread.
901    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    /// The counters the calling thread writes into.
913    ///
914    /// The first call on a thread claims a set and every call after it is a
915    /// thread local read and an index. A server asked to count from more threads
916    /// than it was built for wraps round and shares a set, which loses the odd
917    /// count between two threads and cannot happen to a server `yodb serve`
918    /// built, because that one is told how many threads it will have before it
919    /// starts any of them.
920    pub fn counted(&self) -> &Stats {
921        &self.mine().stats
922    }
923
924    /// Everything the calling thread keeps to itself.
925    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    /// Every thread's numbers added together, which is what `INFO` reports.
935    #[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    /// Put the totals back to zero, which is `CONFIG RESETSTAT`.
947    ///
948    /// Every thread's set and not only the one asking, since the number the
949    /// client is resetting is the sum it was just shown. The open connections
950    /// are left alone because that is a gauge and not a total: the connections
951    /// are still open.
952    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    /// Say how many threads will run commands here, before any of them does.
960    ///
961    /// What it changes is how many sets of counters there are. Called once at
962    /// startup by whoever is about to start the threads, and calling it on a
963    /// running server throws away what has been counted so far, which is why it
964    /// wants the server to itself.
965    pub fn set_threads(&mut self, threads: usize) {
966        self.locals = slots(threads);
967        self.claimed = AtomicUsize::new(0);
968    }
969
970    /// The `maxmemory` limit in bytes, zero when there is not one.
971    #[must_use]
972    pub fn maxmemory(&self) -> u64 {
973        self.maxmemory.load(Relaxed)
974    }
975
976    /// Set the limit, and take a reading straight away.
977    ///
978    /// The reading is here rather than left to the next maintenance turn because
979    /// a client that sets the limit and sends a write in the same batch expects
980    /// the write to be judged against the limit it just set, and because the
981    /// cached number is meaningless until the first time there is a limit to
982    /// compare it with.
983    ///
984    /// Turning the limit on also turns on the running total every slab keeps of
985    /// what its collections hold, and turning it off turns that back off, so a
986    /// server with no limit is not paying to count something nobody reads. The
987    /// first reading after switching it on is the walk that the total starts
988    /// from, and it is the only walk.
989    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    /// Say where a database should get its store from when it needs one.
998    ///
999    /// This is what turns the eviction inversion on. Until it is called every
1000    /// database answers a memory limit by evicting, which is Redis, and after it
1001    /// is called a database under memory pressure moves values to whatever the
1002    /// closure hands back instead of throwing keys away.
1003    ///
1004    /// Called at most once per database and only under pressure, so a server
1005    /// that is given a file and never fills memory never touches it.
1006    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    /// Whether this server has been given somewhere to put cold values.
1014    #[must_use]
1015    pub const fn has_store_source(&self) -> bool {
1016        self.store.is_some()
1017    }
1018
1019    /// Open database `at`'s store, if it has not got one and there is one to be
1020    /// had.
1021    ///
1022    /// A store that will not open leaves the database where it was, which is
1023    /// evicting, because a memory limit that cannot be answered by moving data
1024    /// still has to be answered.
1025    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    /// The `maxstore` limit in bytes, `None` when there is not one.
1038    #[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    /// Set the storage limit, or clear it with `None`.
1047    ///
1048    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
1049    /// total, because this limit is compared against a number the store keeps
1050    /// and answers on demand, not against a walk.
1051    pub fn set_maxstore(&self, bytes: Option<u64>) {
1052        self.maxstore.store(bytes.unwrap_or(NO_MAXSTORE), Relaxed);
1053    }
1054
1055    /// What every attached store is holding, for `INFO memory`.
1056    ///
1057    /// Zero on a server with nothing attached, which is not the same as a server
1058    /// whose file is empty, and [`Server::regime`] is the field that tells those
1059    /// two apart.
1060    #[must_use]
1061    pub fn store_bytes(&self) -> u64 {
1062        self.keyspaces().filter_map(|db| db.store_bytes()).sum()
1063    }
1064
1065    /// What the file has been asked to do, added up over every database.
1066    ///
1067    /// Counters and not levels, so they only ever go up and a run is the
1068    /// difference between two readings. G9 is a ratio over these: the faults a
1069    /// run took, divided by the point reads it issued, has to come out at 1.05
1070    /// or less with a working set ten times memory. There is no way to work that
1071    /// out from outside the server, so it is reported rather than inferred.
1072    ///
1073    /// A fault is a read that went to the store. Whether it also went to the
1074    /// device depends on the store: a log serves a read out of a resident page
1075    /// without touching anything. At ten times memory almost every fault is a
1076    /// real read, which is why the gate is written against this number, but the
1077    /// two are not the same thing and a run tight against the bar should be
1078    /// checked against what the operating system says.
1079    #[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    /// Which way this server answers a memory limit, in one word for `INFO`.
1096    ///
1097    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
1098    /// inversion: a memory limit moves values to the file and nothing stored is
1099    /// lost. A server reports one word rather than leaving an operator to work
1100    /// it out from a limit, a setting and whether a file happens to be open.
1101    #[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    /// Whether database `at` answers a memory limit by moving values to the
1111    /// file rather than by throwing keys away.
1112    ///
1113    /// Three things have to hold. There has to be somewhere to move them, which
1114    /// is a store attached to that database or a source that can open one, and
1115    /// on a server that was never given a file this is false everywhere and
1116    /// every database behaves exactly as it did.
1117    /// The storage budget has to be more than nothing, which is what
1118    /// `maxstore 0` says it is not. And the file has to be under that budget,
1119    /// because a full file is a storage limit reached and eviction is the right
1120    /// answer to a storage limit.
1121    fn migrates(&self, at: usize) -> bool {
1122        let cap = self.maxstore();
1123        if cap == Some(0) {
1124            return false;
1125        }
1126        // Out of the stripe first. A match keeps whatever it is looking at
1127        // alive for the whole of itself, and that would be this stripe held
1128        // across the arms for no reason.
1129        let bytes = self.slot(at).store_bytes();
1130        match bytes {
1131            Some(held) => cap.is_none_or(|cap| held < cap),
1132            // Nothing attached, but somewhere to get one from the moment this
1133            // database needs it, which is what makes the answer yes rather than
1134            // no. Opening it here would mean `INFO` opened files.
1135            None => self.store.is_some(),
1136        }
1137    }
1138
1139    /// Take a fresh memory reading, which the maintenance turn does once a batch.
1140    ///
1141    /// Nothing at all when there is no limit, which is the default and is every
1142    /// server that has not asked for one.
1143    pub fn refresh_memory(&mut self) {
1144        if self.maxmemory() != 0 {
1145            self.used = self.settled_memory();
1146        }
1147    }
1148
1149    /// [`Server::memory_bytes`], asked the cheap way.
1150    ///
1151    /// The same number. The difference is that this asks each database only
1152    /// about the collections that could have moved since the last time, which is
1153    /// what a batch touched rather than what the server holds, so it can be
1154    /// asked once a batch and again on every command that is over the limit.
1155    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    /// Make room under the `maxmemory` limit, throwing keys away if that is what
1163    /// it takes. Answers whether there is anything left it could throw away.
1164    ///
1165    /// Redis runs the same thing from `processCommand` before every command and
1166    /// so does this: a client that writes has to be judged at the moment it
1167    /// writes, not a batch later, or the limit is a suggestion.
1168    ///
1169    /// Three things happen in the loop and all three are needed. Eviction picks
1170    /// a key and drops it. Compaction gives the pages back, because dropping a
1171    /// key marks its record dead and returns nothing on its own, so a loop that
1172    /// only evicted would throw the whole keyspace away and watch the number
1173    /// stay where it was. The reading is taken again each time round, because
1174    /// the two of them together are the only thing that moves it.
1175    ///
1176    /// # Why running out of budget is not a no
1177    ///
1178    /// `false` means there was nothing left to evict, which is `noeviction`, or
1179    /// a `volatile` policy on a database where nothing has a deadline, or a
1180    /// keyspace that is already empty. It does not mean the server is still over
1181    /// its limit, and that difference is Redis's: `performEvictions` answers
1182    /// `EVICT_FAIL` only when it has run out of things to delete, and
1183    /// `processCommand` refuses the client on that and on nothing else. Running
1184    /// out of time part way through a job it is doing well comes back as
1185    /// `EVICT_RUNNING` and the command goes through, because a server that is
1186    /// evicting steadily and refusing every write while it does it is worse for
1187    /// the client than a little overshoot.
1188    ///
1189    /// # What the limit is worth
1190    ///
1191    /// Space comes back a segment at a time and a segment is two megabytes, so
1192    /// this holds a server to its limit give or take a segment. A `maxmemory` of
1193    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
1194    /// megabytes is asking for a precision this store does not have.
1195    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        // The cached reading is a batch old and the batch may have compacted
1201        // since, so take a fresh one before throwing anything away. It is the
1202        // settled reading and not the walk, so what this costs is the handful of
1203        // collections the last batch touched and not the whole database.
1204        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    /// Give back `over` bytes from whichever database can, by moving values to
1222    /// the file where there is one and by throwing keys away where there is not.
1223    ///
1224    /// The two answers are the eviction inversion and which one a database gets
1225    /// is [`Server::migrates`]. Answers whether anything was given back at all,
1226    /// and `false` is what refuses the client's write.
1227    ///
1228    /// A store that will not take the bytes counts as nothing given back, so the
1229    /// write is refused rather than turned into a deletion. A disk that is
1230    /// misbehaving is a reason to stop accepting writes and it is not a reason
1231    /// to start losing data that was accepted already.
1232    ///
1233    /// Round robin from a cursor rather than always starting at database zero,
1234    /// so a server using more than one of them does not empty the first before
1235    /// touching the second. Almost every server is on database zero only, where
1236    /// this is one call that answers and fifteen that say the map is empty.
1237    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            // An empty keyspace has nothing to move and opening a log for one
1241            // would cost a resident page window to find that out.
1242            let used = !self.slot(i).is_empty();
1243            let gave = if used && self.migrates(i) {
1244                self.attach_store(i);
1245                // Whether it made room and not whether it moved a key. A round
1246                // that demoted nothing and handed back a segment is a round
1247                // that made room, and reading only the count refuses the write
1248                // that provoked it.
1249                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    /// The sweep the shard loop calls, at most once a millisecond.
1265    ///
1266    /// The gate is the whole difference between this and [`Server::expire_step`].
1267    /// A maintenance slice runs on every turn of the loop and a turn is a
1268    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
1269    /// thousand times per millisecond and spend a real share of the shard on
1270    /// looking for keys that cannot have died since the last look. Nothing in a
1271    /// database changes fast enough to be worth asking about more often than the
1272    /// clock can tell the difference, and the clock here is milliseconds.
1273    ///
1274    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
1275    /// hertz, so this is not the thing that decides how promptly memory comes
1276    /// back. What it decides is that an idle server sweeps a thousand times a
1277    /// second rather than a million.
1278    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    /// Sweep dead keys out of the databases, spending at most `budget` looks.
1288    ///
1289    /// Answers what it spent, so the caller can charge its maintenance slice for
1290    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
1291    ///
1292    /// Round robin from its own cursor, and every database gets offered whatever
1293    /// is left of the budget rather than a sixteenth of it each, so a server on
1294    /// database zero only, which is nearly every server, spends the whole slice
1295    /// where the keys are. The fifteen empty ones cost a comparison apiece
1296    /// because a database with no key carrying a deadline says so without
1297    /// drawing anything.
1298    ///
1299    /// The cursor moves to the database after whichever one did the work, so two
1300    /// busy databases take turns instead of the lower numbered one starving the
1301    /// other.
1302    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    /// One slice of compaction for a server that is over its limit.
1320    ///
1321    /// Takes the databases in the same order [`Server::compact_step`] does and
1322    /// stops at the first one that had something to move, and it asks with the
1323    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
1324    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    /// Take what every thread has marked and add it to the turn's own mask.
1336    ///
1337    /// The mask the turn works from is its own and not a shared one, because a
1338    /// mask it read in place and then cleared a bit of would be a mask that lost
1339    /// whatever another thread marked in between. A swap cannot lose a mark: a
1340    /// thread that ors while the swap happens either gets its bit in before the
1341    /// swap or leaves it there afterwards, and the second one costs one look at
1342    /// a database the turn has already been through.
1343    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    /// Give one database's dead space back, if any database has enough of it to
1352    /// be worth the move. `None` when no database had a candidate.
1353    ///
1354    /// Once per batch, next to the clock. Overwriting a key writes a new record
1355    /// and counts the old one dead, so without this a server holds everything
1356    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
1357    /// a key against Redis at 144 for the same load, and the whole difference
1358    /// was dead records nothing ever came back for.
1359    ///
1360    /// At most one segment moves per call and the search starts one database
1361    /// further along each time, so the cost of asking is a comparison per
1362    /// database and the cost of acting is bounded by a segment.
1363    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            // Nothing has run against this database since it last said it had
1368            // nothing to collect, so it still has nothing to collect and the
1369            // line it lives on stays where it is.
1370            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            // Only once every stripe of the database has said it has nothing,
1379            // since the bit is per database and one stripe answering for all of
1380            // them would stop the others being asked at all.
1381            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
1395/// What one connection has chosen.
1396pub struct Session {
1397    db: usize,
1398    id: u64,
1399    name: Vec<u8>,
1400    /// The `HIMPORT` fieldsets this connection has prepared.
1401    ///
1402    /// Connection state and not keyspace state, which is the reference's design
1403    /// and not a shortcut: a fieldset is invisible to every other connection and
1404    /// the keys built from one outlive it.
1405    sets: himport::Fieldsets,
1406}
1407
1408impl Session {
1409    /// A new connection, on database zero with no name.
1410    #[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    /// The connection id, which `HELLO` reports and `CLIENT` will.
1421    #[must_use]
1422    pub const fn id(&self) -> u64 {
1423        self.id
1424    }
1425
1426    /// Which database this connection is working in.
1427    #[must_use]
1428    pub const fn db(&self) -> usize {
1429        self.db
1430    }
1431
1432    /// The name the client gave itself, empty if it gave none.
1433    #[must_use]
1434    pub fn name(&self) -> &[u8] {
1435        &self.name
1436    }
1437
1438    /// Put everything back the way it was when the connection was opened.
1439    ///
1440    /// The protocol is not here because it is not here: it lives in the reply
1441    /// buffer, and `RESET` sets it back there.
1442    pub fn reset(&mut self) {
1443        self.db = 0;
1444        self.name.clear();
1445        // `SELECT` leaves these alone and `RESET` does not, both checked
1446        // against 8.10.1, which is the one pair of answers you could not guess
1447        // from what the command is for.
1448        self.sets.clear();
1449    }
1450
1451    /// Record the name from `HELLO ... SETNAME`.
1452    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
1460/// Run one command and write its reply.
1461///
1462/// The name is looked up and the arity is checked here, once, so that no body
1463/// has to. Everything after that is the command's own.
1464pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1465    // The decoder never produces a command with no name. If one ever arrives,
1466    // it is not something to answer.
1467    if args.is_empty() {
1468        return Flow::Continue;
1469    }
1470    resolved(server, session, lookup(args.name()), args, out)
1471}
1472
1473/// The same, for a caller that has already found the command.
1474///
1475/// The engine frames a command before it runs it, and between those two it also
1476/// asks which key the command touches so the record can be prefetched. That is
1477/// two more chances to look the name up, and looking it up three times to run it
1478/// once is three times the cost of the cheapest thing in the path. So the engine
1479/// resolves the name where it frames the command, carries the answer on the
1480/// framed command, and both the other two take it from there.
1481///
1482/// `spec` is `None` for a name that is not a command, which is the same thing
1483/// [`lookup`] says and lands in the same reply.
1484pub 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    // The limit first, so a server with no `maxmemory`, which is the default and
1507    // is nearly all of them, pays one comparison against a field that is already
1508    // warm. Every command and not only the writes, because that is where Redis
1509    // puts it: making room is the server's job whatever the client asked for,
1510    // and the flag only decides who gets told no when there is no room to make.
1511    //
1512    // The flag is Redis's own `denyoom` and the list of commands carrying it is
1513    // Redis's list, so a command that only frees is let through with nothing
1514    // left, which is what lets a client dig itself out with `DEL`.
1515    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    // Which databases the maintenance turn after this batch has to ask. Marked
1522    // for every command and not only for the writes, because a read can make
1523    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
1524    // record it dropped is exactly the kind of thing the collector is for.
1525    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
1526    // two groups that hold them mark all of them rather than the session's.
1527    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    // Before the group, because the five that block are list commands and would
1537    // otherwise land in `lists`, which is handed one database and nothing that
1538    // could park a client. The flag is the right thing to branch on rather than
1539    // a list of names: it is what `COMMAND INFO` reports about exactly these
1540    // commands, and the sorted set and stream ones that arrive later carry it
1541    // too.
1542    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            // Its own group and its own file, and the same values underneath:
1551            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
1552            // something a `SET` left behind works.
1553            "bitmap" => {
1554                let db = session.db;
1555                bits::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1556            }
1557            // The same again: a sketch is a string with a documented layout, so
1558            // `GET` hands one to a client and `SET` takes it back.
1559            "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            // The one hash command whose state is not in the keyspace. A
1568            // fieldset belongs to the connection, so this is handed the session
1569            // as well as the database, the same exception `MIGRATE` gets in the
1570            // keyspace group for the socket it keeps.
1571            "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            // A geo key is a sorted set and these are sorted set commands with
1589            // arithmetic on the way in and on the way out, so a client can ZREM
1590            // a place out of one and ZCARD it to count them.
1591            "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            // A document under a key, reached by a path. The group is Redis's
1604            // module surface and the storage is ours, the same trade the vector
1605            // set group makes.
1606            "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            // The clock is read before the database is borrowed, because every
1639            // stream command needs the time and it lives on the server. An
1640            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
1641            // `XINFO` reporting it all have to agree about what moment this is.
1642            "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            // The one keyspace command that needs more than the databases,
1648            // because the socket it talks down is held on the server between
1649            // commands and not opened again for each one.
1650            "keyspace" if spec.name == "migrate" => {
1651                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1652            }
1653            // Every database and not the one the session is on, because `COPY` takes
1654            // a `DB n` and writes into a database nobody selected.
1655            "keyspace" => {
1656                keyspace::execute(&server.dbs, session.db, spec, args, out).map(|()| Flow::Continue)
1657            }
1658            // No database at all, because an index is not a key. The registry
1659            // is the whole of what these sixteen commands touch.
1660            "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    // Counted here and not before the call, which is where Redis counts it, so
1677    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
1678    // same way theirs does.
1679    //
1680    // Failure is read off the reply rather than off the `Result`, because the
1681    // two are not the same set. A command that ran out of arguments comes back
1682    // as an `Err` and a command that was sent the wrong password writes its own
1683    // error line and comes back `Ok`, and both of those are a call that failed.
1684    // The first byte at the mark is what a client would branch on, and it is `-`
1685    // for an error on either protocol and `!` for RESP3's long form.
1686    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
1694/// The error line for an error value.
1695///
1696/// The prefix is what a client branches on, and there are three of them:
1697/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
1698/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
1699/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
1700/// than routed through here. `OOM` is not a [`Code`] of its own because
1701/// [`Code::Full`] already covers the string that is too long for
1702/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
1703fn write_error(out: &mut Out, e: &Error) {
1704    let prefix: &[u8] = match e.code() {
1705        Code::WrongType => b"WRONGTYPE ",
1706        // Only the HyperLogLog commands answer this one, and the prefix is the
1707        // sentence a client branches on to tell a sketch it cannot read from a
1708        // sketch it sent wrong.
1709        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    /// Build the wire bytes for a command.
1722    ///
1723    /// Tests go through the codec rather than around it, so an argument in a
1724    /// test is the same borrowed slice a connection produces.
1725    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    /// A server, a connection and a buffer, driven the way the reactor will.
1736    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        /// The same, on a server whose databases are cut into `width` stripes.
1749        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        /// Run one command and answer with the bytes it wrote.
1763        fn run(&mut self, parts: &[&[u8]]) -> String {
1764            self.flow(parts).1
1765        }
1766
1767        /// Run one command and answer with the bytes exactly as written.
1768        ///
1769        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
1770        /// every reply that is text and destroys a `DUMP` payload, since a
1771        /// payload is arbitrary bytes and a checksum on the end of them.
1772        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        /// Move every clock in the server on by `ms`.
1786        fn advance(&mut self, ms: u64) {
1787            self.server.advance_clock_ms(ms);
1788        }
1789
1790        /// The same, with what the connection should do next.
1791        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    /// What a client does all day: write the same keys again and again. Every
1809    /// one of those writes leaves the previous record behind, so a server that
1810    /// never compacts holds every version of every key it has ever been sent.
1811    #[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        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
1824        // of it. Thirty two megabytes written to hold sixty four kilobytes,
1825        // which is the shape of a real workload and is enough churn to fill
1826        // sixteen segments if nothing ever comes back.
1827        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    /// The same churn on a database nobody starts on, either side of a quiet
1844    /// spell long enough for the maintenance turn to stop asking about it.
1845    ///
1846    /// The turn after each batch skips a database that has already said it has
1847    /// nothing to collect and has not been touched since, which is what keeps a
1848    /// server whose clients are all on database zero from loading and storing
1849    /// in the other fifteen every batch to be told no. Two things could go
1850    /// wrong with that. A database might never be marked at all, so this uses
1851    /// database nine, which nothing marks by accident. And a database whose
1852    /// mark was cleared might never get it back, so this drains the collector
1853    /// until it says there is nothing left, checks the mark really is gone, and
1854    /// then writes another thirty two megabytes through the same sixty four
1855    /// keys. If either went wrong the server would hold all of it.
1856    #[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        // And nothing landed anywhere else on the way.
1889        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        // The name is matched whatever case it came in, and so are the options.
1901        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        // A key named twice exists twice and can only be deleted once, and both
1910        // of those are Redis's answers rather than tidier ones.
1911        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        // UNLINK is the same body and reports the same way.
1915        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        // A simple string on both protocols, which is unusual: most replies
1924        // that carry a word are bulk strings.
1925        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        // The source is checked before the destination, so this is the error
1964        // and not the zero RENAMENX would otherwise answer for a taken name.
1965        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        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
1976        // one call the two disagree about and neither does any work for.
1977        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    /// Every type a key can hold, copied, because two of them used to panic.
2013    ///
2014    /// `COPY` reads the value out of the source through one match on the type
2015    /// tag, and that match had a catch all at the bottom from back when a set
2016    /// and a hash were the only bodies. The list and the sorted set landed after
2017    /// it and nobody came back, so `COPY mylist other` took the shard down. It
2018    /// is an ordinary command against a type the server supports everywhere
2019    /// else, so this walks all five rather than the two that were broken: the
2020    /// point is that the next type cannot land the same way.
2021    #[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        // And the copy is its own value, not a second name for the source.
2055        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        // Same key, different database, so this is not the same object and is
2080        // an ordinary copy. Same key in the same database is the error below.
2081        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        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
2104        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        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
2120        // misses, which is a nil in the middle of the array and not a short one.
2121        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        // An empty result takes the destination with it rather than leaving a
2138        // list that holds nothing.
2139        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        // And back, which proves the body survived the trip rather than being
2188        // rebuilt from a copy that happened to look the same.
2189        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        // The destination is taken, so nothing moves and the source is still
2202        // there with what it had.
2203        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        // Still on database zero, and database zero is a different database.
2240        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        // A database swapped with itself is fine and changes nothing.
2244        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        // A number too big to be an index on a server that keeps one in an int
2260        // is the same complaint, and a plausible one that is not ours is the
2261        // range complaint instead. The split is Redis's.
2262        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        // A replica that is never going to arrive, and a timeout that would be
2282        // a real wait on a server that had one.
2283        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2284        // Negative replicas is not an error, because zero is already more than
2285        // it asked for.
2286        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        // The arguments are all read before the server looks at itself, so a
2318        // bad timeout beats the append only complaint even with numlocal set.
2319        assert_eq!(
2320            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2321            "-ERR timeout is negative\r\n"
2322        );
2323    }
2324
2325    /// The bytes inside a bulk reply, with the header and the trailing break
2326    /// taken off. Every `DUMP` test needs this and none of them care how the
2327    /// length was written.
2328    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", &copy, b"0", &bytes]), "+OK\r\n");
2348            assert_eq!(f.run(&[b"TYPE", &copy]), 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        // The encoding survives too, since the payload names the plainest legal
2361        // type and the loader puts the value back on the rung it belongs on.
2362        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        // An absolute deadline that has already gone is not an error. The key is
2394        // not created and the reply is the same OK a live one gets.
2395        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    /// The busy key comes before the payload, which is not the order the
2430    /// arguments read in. Whether a key is taken should not depend on whether
2431    /// the bytes behind it happened to be good.
2432    #[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        // And the options come before even that, so a bad FREQ beats the busy
2441        // key the same way a bad DB beats a missing source in COPY.
2442        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        // A footer that is right over a body that is not. The type byte says
2465        // string and there is nothing behind it, so the checksum agrees and the
2466        // value does not exist.
2467        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        // Both are accepted and both are then dropped, which is D-26.
2496        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    /// Neither word is refused for being the wrong one. Each is only accepted
2507    /// while the other is unset, so the second of the two falls through to the
2508    /// plain syntax error rather than getting a message of its own.
2509    #[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        // No key exists at all, and every one of these is still the option
2554        // complaint rather than a zero, which is the order a real server uses.
2555        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        // Repeated, reordered and lowercased, and the last DB wins.
2576        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(&micros), "got {micros}");
2592        // The coarse clock the keyspace uses is a cached millisecond that a
2593        // background tick refreshes, so a TIME built on it would answer the
2594        // same microsecond twice in a row here.
2595        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        // And more than one call to get them, or the COUNT is being ignored and
2623        // the loop above proved nothing about resuming.
2624        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        // Case insensitive, the same as Redis's own comparison.
2655        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2656        // A type nothing can hold is not an error, it just matches nothing.
2657        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2658        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2659        // Both filters at once, and they are an and rather than an or.
2660        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        // A cursor the client made up is a cursor. It resumes somewhere
2682        // arbitrary and answers whatever is there, which is what Redis does and
2683        // is the only behaviour that does not need the server to remember every
2684        // cursor it has handed out.
2685        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        // The walk collected it on the way past, which is what makes DBSIZE
2729        // here answer what Redis answers once its own cycle has been round.
2730        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        // The absolute pair, derived from the same one number the store kept.
2746        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        // The body is not touched by any of that, which is the whole reason the
2778        // deadline lives in the record and the body lives somewhere else.
2779        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        // Four ways of naming a moment that has passed, and all four are a
2791        // delete answering 1 rather than an error. Zero is a moment, minus one
2792        // is a moment, and the hash field commands refuse the negative one.
2793        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        // The condition is answered before the past check, so this is a 0 and
2827        // the key survives. The other order would delete it.
2828        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        // XX with LT is the one pair that is not either of them on its own: LT
2847        // alone would accept a key with no deadline and this does not.
2848        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            // Seconds that overflow when multiplied into milliseconds. Every
2902            // message names the command it came from.
2903            (
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        // The one of the four that has no arithmetic to overflow. Redis takes
2925        // it and holds the number as given, and a record here holds forty six
2926        // bits, so it lands in the year 4199 instead. D-17.
2927        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        // ASYNC and SYNC are both taken and neither changes anything, since the
2943        // keyspace is empty before the OK goes out either way.
2944        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2945        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2946        // Only database one was emptied.
2947        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        // Anything else after the name is a syntax error, and so is a third
2954        // argument even when the second one is a word we take.
2955        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        // Nothing is cached, so nothing is there, one answer per hash asked
2969        // about.
2970        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        // Redis's two messages here are its own, one per container, and one of
2985        // them reads like a typo.
2986        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        // A second argument after the mode is the generic one instead, because
2995        // the count is checked before the word is looked at.
2996        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        // The ones that need an interpreter are not here, and say so rather
3010        // than answering OK to a load that loaded nothing.
3011        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        // Read back as a string it is still an integer, written out as digits
3028        // only because somebody asked for them.
3029        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        // A counter that is not a number is the error the store raises and this
3032        // layer only spells, which is the whole point of the split.
3033        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    /// Every one of these was read off a running 8.8. They are the answers a
3045    /// client library's own test suite checks, and the shapes are not
3046    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
3047    /// integer, `INCREX` is a pair.
3048    #[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        // The same digest a real 8.8 answers for the same five bytes, which is
3053        // what makes `IFDEQ` usable against a mixed deployment.
3054        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        // A float counter is a double on RESP3 and the digits in a bulk string
3087        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
3088        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        // `RESET` puts the protocol back, which is the part that is easy to
3094        // miss and leaves a pooled connection speaking the wrong one.
3095        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        // A name with a line ending in it cannot write its own frame into the
3109        // stream, which is the reason the error writer maps them to spaces.
3110        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        // The table says `PING` takes one or more and a real server then
3126        // refuses three, which is the sort of thing that only shows up against
3127        // the real thing.
3128        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        // `DELEX` takes two or four and nothing between.
3135        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    /// The option rules, all of them measured against 8.8 rather than read off
3142    /// the documentation. The surprising one is that `SET` accepts the same
3143    /// keyword twice and `INCREX` does not.
3144    #[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        // Twice is fine, and the last one wins.
3160        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        // `INCREX` refuses what `SET` allows.
3167        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    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
3190    /// key that is not there, which answers null without ever looking at the
3191    /// expiration it was given.
3192    #[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        // A deadline in the past is accepted and the key goes with it.
3219        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        // Without `IDX` the two options that only mean something with it are
3259        // accepted and ignored, which is what a real server does.
3260        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        // `RESET` brings it back to zero.
3288        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        // A null in the list, and the plain one: `$-1` and not `*-1`.
3337        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    /// A cluster aware client asks this question and then routes on the
3349    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
3350    /// that matters.
3351    #[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        // A pattern matches more than one, and a setting two patterns both ask
3386        // for is still sent once.
3387        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        // Too few arguments and an odd number of them are different
3404        // complaints, which is the sort of thing only the real server tells
3405        // you.
3406        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        // And INFO agrees with CONFIG, which it did not when it was a literal.
3438        assert!(
3439            f.run(&[b"INFO", b"memory"])
3440                .contains("maxmemory_policy:allkeys-lfu"),
3441            "INFO and CONFIG disagree about the policy"
3442        );
3443        // The refusal names every legal value in the order the real server's
3444        // enum table lists them, because a client comparing the message compares
3445        // the whole string.
3446        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        // A bad pair leaves the good one in the same command alone, and the
3451        // policy is checked by the same pass that checks the numbers.
3452        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            // A number that is not a number is refused with the same sentence
3499            // every other number gets, which names the setting the client typed.
3500            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        // The pairing is Redis's and it is a trap: the bare letter is a power of
3518        // ten and the one with the b is a power of two.
3519        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        // A unit nobody has heard of, and a negative number, which is not a very
3542        // large one however it is spelled.
3543        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        // A byte, which is under what an empty server holds, so nothing this
3562        // command could do would get it under. The default policy is
3563        // `noeviction`, so nothing is what it does.
3564        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        // Reading is allowed, and so is the one thing that would help.
3574        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        // Taking the limit away lets the write through again.
3579        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        // Two megabytes under what it is holding, which is one segment's worth,
3598        // so getting there means giving a whole segment back and not just
3599        // dropping a few records.
3600        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        // Writes keep working the whole way down. The budget means one command
3610        // does not do it all, so this runs until the server has settled and
3611        // checks that nothing was refused on the way.
3612        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        // The limit is judged against a number kept as the collections move,
3640        // rather than found by asking all of them, and the two have to be the
3641        // same number or the limit is enforced against a fiction. This does the
3642        // things that move it, which is growing a collection, shrinking one,
3643        // changing its representation, deleting it and reusing its slot, across
3644        // all five types, and checks the two against each other as it goes.
3645        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                // Every type deleted and made again, so a slot goes on the free
3667                // list and comes back holding something else.
3668                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        // The run has to have built something, or the two numbers agreeing is
3678        // two zeroes agreeing.
3679        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        // And it survives the collections going away entirely.
3687        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        // A server with no limit does not keep the running total, so setting a
3694        // limit on a database that is already full has to start it from a walk.
3695        // If it did not, the first reading would be zero and the server would
3696        // think it had all the room in the world.
3697        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        // Nothing has been evicted and nothing can be under the default policy,
3723        // so this stays at zero while the other one moves.
3724        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        // Under the default the clock is kept and the counter is not, and under
3737        // an LFU policy it is the other way round. Each subcommand refuses on
3738        // the side where its reading of the three bytes means nothing.
3739        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        // The key was written under a clock policy, so what comes back is that
3751        // clock read as a counter. It is a number and not an error, which is the
3752        // point: switching at runtime does not invalidate anything, it only makes
3753        // the old field mean something else until the key is used again.
3754        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        // A field deadline widens the blob rather than promoting it, and this
3780        // is the only place a client can see that happen.
3781        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        // And the key is looked up before FREQ has its complaint, so the
3803        // complaint only reaches a key that exists.
3804        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        // The old spelling is the same number under a different name, and a
3836        // glob that catches both sends both.
3837        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        // The set has three of these and all three move.
3875        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        // The name in the complaint is the one that was typed, so the old
3906        // spelling comes back as the old spelling.
3907        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        // A number past what an i64 holds is the parse complaint and not the
3916        // range one, which is upstream reading it before it checks it.
3917        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        // One section is one section.
3963        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    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
3970    ///
3971    /// This is Redis's `unit/info-command` written against the fixture. Every
3972    /// assertion in it is one of theirs, in their order, and the two fields it
3973    /// turns on are the two that suite was failing on: `master_repl_offset`,
3974    /// which is in the default set, and `rejected_calls`, which is not.
3975    #[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        // Their case, to make the point that a section name is not case
4002        // sensitive any more than a command name is.
4003        let stats = f.run(&[b"INFO", b"commandSTATS"]);
4004        assert!(!stats.contains("used_memory"), "{stats}");
4005        assert!(stats.contains("rejected_calls"), "{stats}");
4006
4007        // Two sections named, and neither of them pulls in a third.
4008        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        // A section named twice is still written once.
4017        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    /// The memory section says what this process may use, not what the machine
4038    /// has.
4039    ///
4040    /// The distinction is the whole point of it. A server inside a container
4041    /// that reports the host's memory is a server whose operator sizes it for
4042    /// memory it will be killed for touching, so all three numbers are there:
4043    /// what the machine has, what the cgroup allows, and the quarter of the
4044    /// tighter one that pools are sized from.
4045    #[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        // Zero means there is no limit to report, which is a real answer on a
4069        // machine with no cgroups and no way to ask how big it is.
4070        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    /// The three counters, each on the path that raises it.
4081    ///
4082    /// `calls` on a command that worked, `failed_calls` on one that ran and
4083    /// answered with an error, and `rejected_calls` on one that never ran at
4084    /// all. The last two are the pair that is easy to collapse into one number
4085    /// and that Redis keeps apart, because a client sending the wrong number of
4086    /// arguments and a client asking for a list element that is not there are
4087    /// not the same problem.
4088    #[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        // Ran, and answered with an error, because `k` is not a list.
4094        f.run(&[b"LPUSH", b"k", b"x"]);
4095        // Never ran: `LPUSH` takes at least three arguments.
4096        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    /// A cache that writes with a deadline and never reads back used to hold
4114    /// every key it had ever written, because lazy expiry needs somebody to walk
4115    /// past a key before it can reclaim it and nobody ever did.
4116    #[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        // What the shard loop does, one slice at a time.
4134        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        // And one database having them does not make the other fifteen pay.
4160        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    /// The gate, which is what stops a maintenance slice that runs every hundred
4173    /// nanoseconds from drawing a sample every hundred nanoseconds.
4174    #[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        // A small budget, so that one slice cannot finish the job and a second
4184        // one having nothing to do would mean the gate and not an empty
4185        // database.
4186        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    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
4203    /// how much of a cache is volatile was reading a constant.
4204    #[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        // Each database answers for itself, the way Redis reports it.
4223        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        // Redis's unit/info-command asks for this one by name in three tests.
4237        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        // It is a measurement and not a constant, so it goes up when work
4243        // happens. A tight loop rather than a sleep, because sleeping is the
4244        // one thing that does not move this number.
4245        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            // A bound rather than a spin, so a platform where this number does
4254            // not move fails here instead of hanging. Even a clock with whole
4255            // millisecond granularity gets there in the first round or two.
4256            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
4257        }
4258    }
4259
4260    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
4261    #[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    /// The safety net under the rule that a body checks its arguments before
4272    /// it writes anything. `MGET` writes its array header first and then reads
4273    /// each key, so if a later argument could fail the header would already be
4274    /// out. Nothing in the string group does that today and this is what would
4275    /// catch the first one that did.
4276    #[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    /// A server that has not been asked to stop is not stopping, and one that
4293    /// has says so without writing anything back.
4294    ///
4295    /// The empty reply is the point. Redis answers nothing at all here and the
4296    /// client sees the socket close, and an `OK` would be a promise from a
4297    /// process that is about to not exist.
4298    #[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    /// Every flag combination 8.10.1 takes, and every one it refuses.
4310    ///
4311    /// The refusals are the half worth pinning down. `SAVE` and `NOSAVE`
4312    /// contradict each other, `ABORT` says to do nothing so it cannot be
4313    /// combined with a word about how to do it, and repeating any one of them
4314    /// is fine. All of it was read off a running 8.10.1 rather than worked out
4315    /// from the documentation, which does not say.
4316    #[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    /// `ABORT` has nothing to call off, ever.
4358    ///
4359    /// A shutdown here is decided and done inside one turn of the loop, so
4360    /// there is no window in which one is in progress. That makes Redis's
4361    /// message for a cancel with nothing to cancel the right answer every time
4362    /// rather than only when nothing happens to be pending. Two `ABORT`s is
4363    /// still one `ABORT`, which is what 8.10.1 does.
4364    #[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    /// A fixture whose server writes into a directory of its own.
4377    ///
4378    /// Every test here really writes files, because the whole point of the
4379    /// command is the files and a backup that is only a state machine would
4380    /// pass a test suite and fail the first person who tried to restore one.
4381    /// The directory carries the test's name so that the suite can run its
4382    /// tests in parallel the way it always does.
4383    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        /// The names in `backupdir`, sorted, so a test can say what is on disk.
4403        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    /// The four states and the moves between them, in the order a client walks
4427    /// them, with the files checked at every step.
4428    #[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    /// Every move that is refused, in the reference's words.
4457    #[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        // Cleanup from idle is not an error, it is a way of saying there was
4470        // nothing to clean up.
4471        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    /// An abort takes the base file away and leaves a state saying who did it.
4499    ///
4500    /// The next backup takes the next sequence number rather than reusing the
4501    /// one whose files were just thrown away, so a directory somebody copied a
4502    /// half finished backup out of cannot end up with two different files under
4503    /// one name.
4504    #[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        // A start from failed works, and is the second backup.
4517        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    /// `LIST` names nothing, then one file, then three, and they are absolute.
4525    #[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        // The order is the manifest's order, base then incremental then the
4542        // manifest itself, which is the order a restore needs them in.
4543        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    /// The base file is the dataset as it was at `START` and not at `SEAL`.
4554    ///
4555    /// That is D-46 and it is the one thing about this a client can notice, so
4556    /// it is pinned here rather than left to be discovered by whoever restores
4557    /// one. The incremental file is empty for the same reason: there is no
4558    /// append only log underneath this server to copy the writes in between out
4559    /// of.
4560    #[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        // The aux field a loader acts on, and the one that says this file is
4576        // the base of an append only file rather than a standalone dump. Its
4577        // value is the one byte string 1, which the encoder writes as an
4578        // integer the way a real server writes it.
4579        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    /// `STATUS` is a map of four pairs on RESP3 and the same pairs flat on
4594    /// RESP2, which is what every other map shaped reply in this server does.
4595    #[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    /// A sealed backup that nobody cleans up goes away on its own once
4620    /// `backup-sealed-ttl` seconds have passed since the seal.
4621    #[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        // A minute short of the deadline, nothing happens.
4633        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        // Zero is the default and means a sealed backup is kept for ever.
4645        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    /// The three settings around the command, read and written the way 8.10.1
4654    /// reads and writes them.
4655    #[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        // `dir` is a protected config, so it is refused even for the value it
4674        // already holds, and `backupdirname` is immutable.
4675        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    /// The help text, which has `HELP` in it twice because the reference's does.
4694    #[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    /// What a mistyped `BACKUP` gets told.
4708    ///
4709    /// The arity error names `backup` where the reference names `backup|start`,
4710    /// which is D-46: the table reports one arity for the container the way the
4711    /// reference does, and the per subcommand table that would carry the better
4712    /// name is not built yet. Every subcommand is exactly two words, so nothing
4713    /// legal is refused by it.
4714    #[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        // A fresh server has every database marked, so start from nothing to
4745        // see the one mark arrive.
4746        server.dirty = 0;
4747        server.locals[1].mark(1 << 9);
4748        server.collect_marks();
4749        assert_ne!(server.dirty & (1 << 9), 0);
4750        // And taken once rather than left to be taken again next turn.
4751        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        // Written into the two sets by hand, because what is under test is the
4759        // adding up and not the claiming, and one test thread can only ever
4760        // claim one set.
4761        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        // A reset takes the totals and leaves the open connections, which are
4778        // still open.
4779        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        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
4819        // and one that gets a `*` hands it a list, without either of them being
4820        // told which command was sent.
4821        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        // An intset holds the number, so these digits exist for the first time
4832        // in the reply buffer.
4833        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        // MGET is the one that does not, because Redis gives nil for the odd
4861        // key out rather than failing the good keys next to it.
4862        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        // And plain SET overwrites any type, which takes the body with it.
4867        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        // SMISMEMBER writes an array header and then one reply per member, so
4874        // it is the first command in the server that could get a header out in
4875        // front of an error if it checked its key in the wrong order.
4876        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    /// Pull the cursor and the members out of one `SSCAN` reply.
4895    ///
4896    /// Crude on purpose. A test that walked a set through a real client would
4897    /// be testing the client, and what these tests are about is the shape of
4898    /// the bytes and the fact that a walk sees every member once.
4899    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        // A count takes that many, and the last one takes the key with it.
4927        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        // And a pop at a key that is not there is a nil, not an empty bulk.
4931        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        // The one place in the server where the reply type carries something
4938        // the command name does not. SPOP's members are distinct so a RESP3
4939        // client can build a set out of them. SRANDMEMBER with a negative count
4940        // can hand back the same member three times, and a set would lose two.
4941        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        // And a positive count is an array too, since Redis makes it one.
4947        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
4948
4949        // A negative count against a set of one is where the difference bites:
4950        // the same member three times, which is a three element reply and would
4951        // have been a one element reply if it had gone out as a set.
4952        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        // The count form answers an empty array rather than a nil, which is the
4969        // pair of answers Redis gives and is not the pair it looks like.
4970        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        // Asking for more than is there answers all of it once and not padding.
4973        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        // Zero is allowed and is a real answer rather than an error.
4985        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        // A set small enough to be a listpack answers in one call whatever
5015        // cursor it was handed, which is what Redis does for that encoding.
5016        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        // And a key that is not there is a finished scan of nothing.
5021        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        // An integer member has no digits stored anywhere, so MATCH is the one
5035        // place a scan pays to write some.
5036        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        // A count under one is a syntax error and not a range error, which is
5048        // the odder of Redis's two answers and the reason it is copied exactly.
5049        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        // A member that is not in the source is a zero and moves nothing.
5065        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        // A destination that does not exist gets made, and a source that runs
5069        // out goes away.
5070        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        // Not the order it looks like it should be. A source that is not there
5078        // answers zero without ever looking at the destination, so this is a
5079        // zero and not a WRONGTYPE even though the destination is a string.
5080        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        // SSCAN writes an outer array header before it walks, so it is the
5101        // command most likely to get bytes out in front of an error.
5102        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        // The value the client sent is `9`, so HGET h b must not find the `2`
5130        // that is a value. A search with a step of one would have.
5131        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        // The same deadline in the other three units, all of them derived from
5363        // the one number the store kept.
5364        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        // Zero is a delete and not an error, where minus one is an error. That
5410        // is Redis's split and it is easy to get backwards.
5411        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        // Time moves once per turn of the event loop and nowhere else, so a
5429        // test moves it by hand rather than by sleeping. There is nothing to
5430        // sleep for: the deadline is a number and so is the clock.
5431        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        // Any order, because Redis reads these in a loop and not in a fixed
5763        // sequence.
5764        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            // HGETDEL has three sentences of its own for these three mistakes.
5793            (
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            // And HGETEX and HSETEX have three different ones between them.
5806            (
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    /// The two orders `HIMPORT` juggles, which are not the same order.
5959    ///
5960    /// Values arrive in the order the fields were declared in and the hash is
5961    /// built in sorted order, so the first value is not generally the first
5962    /// field. And the sort is by length before bytes, which nothing else here
5963    /// sorts names with: `b` comes before `aa` where a plain byte comparison
5964    /// would put `aa` first. Both read off 8.10.1.
5965    #[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    /// It replaces the key rather than writing over it, so a field the fieldset
5984    /// does not name is gone afterwards and so is the deadline.
5985    #[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    /// A fieldset is connection state. `SELECT` leaves them alone and `RESET`
6000    /// throws them away, and a key built from one outlives it.
6001    #[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    /// Which complaint wins when a line is wrong in more than one place.
6019    ///
6020    /// The type of the key beats both of the others, so a `HIMPORT SET` against
6021    /// a string is a WRONGTYPE even when the fieldset is missing too, which is
6022    /// the ordering a real server has and not the one the argument order
6023    /// suggests.
6024    #[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        // One sentence for too few and for too many alike.
6045        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    /// The arity of each subcommand, and the unknown one.
6059    #[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    /// A `PREPARE` that fails leaves the name pointing where it pointed, which
6092    /// is the answer of the two that could not be guessed from outside.
6093    #[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    /// Preparing the same name twice replaces it, and the two discards count
6109    /// what they took rather than answering OK.
6110    #[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    /// The one integer of a single element array reply.
6130    /// The number out of a plain integer reply.
6131    ///
6132    /// [`int_reply`] is the same thing wrapped in a one element array, which is
6133    /// the shape every hash field command answers in.
6134    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    /// The cursor and the flat items of a scan reply.
6151    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    /// The members of a set reply, sorted, since none of these promise an
6167    /// order and a test that asserted one would be asserting an accident.
6168    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        // A key that is not there is an empty set, which empties an
6201        // intersection and does nothing at all to a union.
6202        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        // An empty answer deletes the destination rather than leaving an empty
6237        // set behind, and the destination may be one of the sources.
6238        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        // And a destination holding something else is overwritten, the same way
6244        // SET overwrites, rather than refused.
6245        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        // The counted keys are what make its three error messages its own.
6270        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        // A key really can be called LIMIT, which is why the count exists.
6291        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    /// The two Redis 8.10 added, which are SINTERCARD's shape over a union and
6296    /// over a difference. Every number here was read off 8.10.1 first.
6297    #[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        // The same three messages SINTERCARD has, because the line is the same
6339        // line and is parsed once for all three.
6340        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        // And a key called LIMIT is a key, here as much as on SINTERCARD.
6381        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    /// The leak a set can spring that nothing on the wire would ever show: the
6414    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
6415    #[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    // --------------------------------------------------------------- bitmaps
6443
6444    /// The two single bit commands, and the encoding rule underneath them.
6445    ///
6446    /// A write always leaves the value `raw` and a read never re-encodes, which
6447    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
6448    /// with its first digit changed after a `SETBIT`.
6449    #[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        // Writing a nought past the end still creates the key and still pads.
6460        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    /// Counting, in bytes and in bits.
6473    ///
6474    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
6475    /// says 22 for it. The server is the thing being copied here.
6476    #[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        // A start past the end is left where it is and the end is pulled back,
6498        // so the range comes out backwards and counts nothing.
6499        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6500
6501        // A lone start is a syntax error here, where BITPOS allows it.
6502        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    /// Searching, and the one place a miss is not minus one.
6513    ///
6514    /// A search for a nought that runs to the end of the string answers the
6515    /// length in bits, because the string is treated as if it had noughts after
6516    /// it forever. Give it an explicit end and it answers minus one instead.
6517    #[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        // A missing key is all noughts, so a one is never found and a nought is
6536        // at position zero.
6537        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    /// The eight operations, with the answers a real server gives for them.
6542    #[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        // The one whose answer is not text, so it is compared as bytes.
6565        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        // A missing source is a string of noughts as long as it needs to be, so
6569        // an AND against one writes three zero bytes rather than nothing.
6570        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        // Every source missing is an empty result, and an empty result takes
6574        // the destination with it.
6575        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    /// What `BITOP` says when it is asked for something it cannot do.
6581    #[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    /// Packed fields, the three overflow policies and the `#` offset.
6610    #[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        // The field at bit 100 is two bits wide, so it ends in the thirteenth
6631        // byte and the value grew to thirteen bytes to hold it.
6632        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6633
6634        // A `#` offset counts in fields rather than in bits.
6635        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    /// A bad subcommand anywhere in the line stops all of it.
6700    ///
6701    /// Redis checks the whole argument list before it runs any of it, so the
6702    /// `SET` in front of the bad type here never happens and the key it would
6703    /// have created is not there afterwards.
6704    #[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    /// The read only twin reads, refuses to write, and creates nothing.
6764    #[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        // A read does not unpack an int the way a write does.
6773        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6774
6775        // An OVERFLOW word is allowed even though nothing here can overflow.
6776        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    /// The offsets a bitmap command will not take.
6803    #[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    /// Every one of the seven refuses a key that is not a string.
6831    #[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    // --------------------------------------------------------- hyperloglogs
6851
6852    #[test]
6853    fn a_sketch_is_added_to_and_counted() {
6854        let mut f = Fixture::new();
6855        // Creating the key counts as a change, even with nothing to add.
6856        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        // And it is a string, which is not an implementation detail: a client
6861        // can `GET` a sketch out of one server and `SET` it into another.
6862        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        // Not text, so it is compared as bytes.
6875        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        // A key that is not there is an empty sketch, not an error and not
6890        // something that gets created by being counted.
6891        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        // The destination is one of the sources, so a second merge adds to it.
6904        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        // And with no sources it is a no-op that still answers OK and still
6908        // creates a destination that was not there.
6909        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        // A dense sketch has no opcodes left to print.
6928        assert_eq!(
6929            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6930            "-ERR HLL encoding is not sparse\r\n"
6931        );
6932
6933        // All 16384 registers, of which three are not nought.
6934        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        // A key that is not a string at all gets the ordinary sentence, and a
6954        // destination that would have been written is not created.
6955        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        // The word is quoted exactly as the client spelled it, and this is not
6969        // the "Try X HELP." sentence every other container command uses.
6970        assert_eq!(
6971            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
6972            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
6973        );
6974        // Where all three of the real commands take a missing key as empty.
6975        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        // The sketch with its last byte cut off, which is still a header and a
6995        // magic and is a run length encoding that stops short of register 16384.
6996        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        // One that stays sparse and one that has gone dense, since the payload
7009        // carries the bytes and the two encodings are different lengths.
7010        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", &copy, b"0", &bytes]), "+OK\r\n");
7023            // The bytes, the encoding and the estimate all come back, which is
7024            // the whole of what byte compatibility across a round trip means.
7025            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
7026            assert_eq!(
7027                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
7028                f.run(&[b"PFDEBUG", b"ENCODING", key])
7029            );
7030            assert_eq!(f.run(&[b"PFCOUNT", &copy]), 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    /// One RESP2 bulk string. The JSON replies are almost all one of these and
7037    /// the text inside them has quotes in it, so writing the frame out by hand
7038    /// buries the part of the assertion that matters.
7039    fn bulk(s: &str) -> String {
7040        format!("${}\r\n{s}\r\n", s.len())
7041    }
7042
7043    /// A RESP2 array of bulk strings, which is what most of the list replies
7044    /// are and what writing them out by hand in every assertion looks like.
7045    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        // Each element in turn goes at the head, so the last one sent is at the
7057        // front when it is over. That reads like a bug in the client and it is
7058        // what every Redis has always done.
7059        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    /// The four ways a pop can come back with nothing, which are three
7088    /// different replies and a RESP2 client can tell all of them apart.
7089    #[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        // A count of zero against a list that is there is an empty array and
7098        // not a null array, which is the fourth answer.
7099        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        // More than there is takes what there is and the key goes with it.
7102        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        // Redis calls this an arity error and not a syntax error, which is a
7115        // distinction it does not always make.
7116        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        // A key that is not there is an empty range and not a nil, which is the
7140        // one place a list disagrees with a set.
7141        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        // Both ways of missing are errors here rather than a nil, because a
7162        // list is never empty and there is nothing else the reply could be.
7163        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        // Zero for a key that is not there, which is not the same as minus one
7177        // for a pivot that is not in a list that is.
7178        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        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
7223        // leave `EXISTS` answering zero rather than leaving an empty one.
7224        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        // MAXLEN counts elements looked at and not matches found, so three
7245        // stops after `a b c` and finds the one match in it.
7246        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        // Nothing found is three different replies depending on how it was
7251        // asked and whether the key is there at all.
7252        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        // The whole sentence and not a prefix, because the older wording of it
7263        // is still all over the internet and clients match on the text.
7264        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        // The same key twice is the documented way to rotate a list and falls
7299        // out of taking the element before deciding where to put it.
7300        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        // The element is still where it was, rather than having gone nowhere.
7327        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        // OBO is what you get from sending LMOVE that many times, BULK keeps
7333        // the source order. The two only differ when both ends are the same,
7334        // which is the whole reason the word exists.
7335        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        // Six and seven arguments are neither of the two forms, so the
7377        // reference calls both of them a syntax error rather than guessing.
7378        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        // A null array and not a null bulk string, which `redis-cli` prints as
7393        // `(nil)` either way and only the raw wire tells apart. What it would
7394        // have sent is an array, so its nothing is an array's nothing.
7395        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        // COUNT takes what there is, and an emptied source goes away.
7406        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        // Every argument is read before the keys are looked at, so a bad count
7468        // beats a wrong type even when the type is wrong on the source.
7469        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        // The name of the key that answered comes back with the elements,
7489        // because the client cannot work out which one it was.
7490        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        // A null array and not a null, even though what it stands in for is an
7500        // array holding a key name and then another array.
7501        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        // A key count that eats the direction is a syntax error and not a
7521        // sentence about key counts, because the direction is simply not there.
7522        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    /// A timeout is not an integer and it is not an ordinary float either: the
7574    /// three sentences it can answer with are its own, and which one a given
7575    /// argument gets is not what reading the code would suggest.
7576    #[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            // Whitespace on either side, which `strtold` would take and Redis
7586            // does not.
7587            (&[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            // These three parse, so they are not the not-a-float error, and all
7592            // three are further off than an i64 of milliseconds reaches.
7593            (&[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    /// A timeout of exactly zero means no timeout, and there are two ways of
7612    /// writing exactly zero.
7613    #[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        // Positive, so it is a real deadline, and the deadline is this
7622        // millisecond. Nothing is written here either: the reply comes from the
7623        // sweep, which is the engine's and not this layer's.
7624        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        // The one difference from LPOP: the reply names the key that answered,
7635        // which is what makes BLPOP over several keys usable.
7636        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        // Onto itself, which is how a list is rotated and is a real thing to ask
7661        // a blocking move for.
7662        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            // Two keys named and one given, so the word that should have been
7684            // the direction is a key and there is no direction left.
7685            (
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            // A count that is not a number at all gets the same sentence a zero
7702            // or a negative one gets, rather than the usual one about integers.
7703            (
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        // Both are wrong. Redis checks the directions first, so this is the
7721        // syntax error and not a complaint about the timeout.
7722        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    /// `BLMOVEM` answers exactly what `LMOVEM` answers when it does not have to
7733    /// wait, which is the same relationship every other command in this file has
7734    /// with the one it wraps.
7735    #[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        // `EXACTLY` with enough there does not wait either.
7754        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    /// The one thing `BLMOVEM` decides differently from the other five: `COUNT`
7764    /// is ready as soon as there is anything and `EXACTLY` is not ready until the
7765    /// whole block has arrived.
7766    #[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        // Two there and three asked for. `COUNT` takes the two.
7771        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        // The same line with `EXACTLY` parks instead, and takes nothing on the
7780        // way past.
7781        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        // All three are wrong and the directions are read first.
7796        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        // Directions fine, timeout and count both wrong, so the timeout wins.
7803        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        // And with the timeout fine, the count before the ordering word.
7816        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        // Seven and eight arguments are neither of the two forms, the same way
7829        // six and seven are for `LMOVEM`.
7830        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    /// The four ways a blocking command sees a key of another type, and the one
7843    /// way it does not.
7844    #[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        // Every key is checked even when an earlier one would have blocked, so
7853        // an empty key in front of a string does not hide it.
7854        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        // The destination, which is only reached because the source has
7859        // something in it.
7860        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        // And the one that does not: an empty source means the destination is
7872        // never looked at, so this waits rather than erroring, and on a real
7873        // server it times out.
7874        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        // `BLMOVEM` has a second way of not being ready, and it hides the
7880        // destination just as well: the source is a list with two elements in it
7881        // and `EXACTLY` wants three, so the string never gets looked at.
7882        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    /// The same churn the set and the string get, because a list that leaks a
7898    /// chunk per push looks exactly like one that does not until it has run for
7899    /// an afternoon.
7900    #[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    // ------------------------------------------------------------ sorted set
7928
7929    #[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        // The key goes when the last member does.
7945        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        // NX leaves a member that is there alone, XX will not create one.
7969        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        // GT and LT only move a score one way.
7974        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        // CH counts a moved score and plain ZADD does not.
7978        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        // A gate that refuses is the string nil, because the reply it stands in
7991        // for is a score.
7992        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        // And a key made for an increment that then fails does not stay behind.
8021        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        // The pairs are counted before the options are looked at, so this is a
8028        // syntax error about having none and not a complaint about NX and XX.
8029        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        // An odd number of arguments after the options.
8045        assert_eq!(
8046            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
8047            "-ERR syntax error\r\n"
8048        );
8049        // Every score is read before the first is stored.
8050        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        // WITHSCORE changes both shapes: the answer and the nothing.
8066        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        // A bad option is a syntax error and one argument too many is an arity
8074        // error, which is Redis's split.
8075        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        // A bare member is not a bound, because a member can start with any
8104        // byte and there would be no way to say the bracket if it were optional.
8105        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    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
8112    ///
8113    /// Every byte in here was read off a real 8.10.1 rather than worked out,
8114    /// because the interesting part of this command is not what it selects, it
8115    /// is which of the two ends the client is expected to name first.
8116    #[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        // REV over ranks reverses the walk and leaves the two arguments alone,
8131        // because a rank counts from the end the walk starts at.
8132        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        // And REV over scores does swap them, since a bound does not count from
8141        // anywhere. This is the one line of the parse that tells the two apart.
8142        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    /// The older spellings, which are the same six windows with the mode in the
8157    /// name and the high end named first on the three that go backwards.
8158    #[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        // The two arguments the wrong way round is an empty answer and not an
8179        // error, which is what the swap being in the parse rather than in the
8180        // window buys.
8181        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        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
8191        // way of spelling the mode, they are a syntax error.
8192        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    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
8202    /// only some of them accept.
8203    #[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        // A negative offset skips past everything, a negative count is no bound.
8214        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        // The two options in either order, which falls out of the parse loop.
8227        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        // LIMIT on a range by rank is refused after the whole option list has
8255        // been read, so this complains about LIMIT and not about WITHSCORES.
8256        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        // Two modes at once, an option nobody knows, a LIMIT missing its count,
8284        // and the three number errors, which are three different sentences.
8285        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    /// `WITHSCORES` is the one place in this group where the two protocols
8314    /// disagree about the shape of the reply and not just the type of a value.
8315    #[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    /// The store form, which is the same parse with the destination in front.
8335    #[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        // A window that selects nothing deletes the destination rather than
8341        // leaving an empty sorted set, because an empty one does not exist.
8342        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        // The destination is allowed to be the source, because the result is
8353        // built whole before anything is written over.
8354        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        // It takes every option ZRANGE takes except WITHSCORES, which is a
8360        // plain syntax error here and not the sentence about BYLEX.
8361        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    /// The three removals, which are the read side's window with the walk
8368    /// turned into a removal and no options at all.
8369    #[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        // The last member going takes the key with it.
8384        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    /// The algebra, which is one gather and three names for it.
8397    #[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        // The scores are added where a member is in both, and the answer comes
8407        // out in the order those combined scores put it in.
8408        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        // A plain set is an input, and it behaves as a sorted set in which
8459        // every member scores one.
8460        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        // A difference never combines two scores, so it has nothing for either
8466        // of the two options to do and refuses both.
8467        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    /// The count of keys, which is what lets a key be named `WEIGHTS`.
8484    #[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        // Redis names the command in this one, so each spelling says its own.
8490        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        // A count bigger than the line is a plain syntax error, which reads
8503        // oddly and is what Redis says.
8504        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        // A WEIGHTS list that is not one per key is a syntax error, and a
8513        // weight that is not a number gets a sentence of its own.
8514        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    /// The three store forms, which answer a count and take no WITHSCORES.
8529    #[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        // An empty result deletes the destination rather than leaving an empty
8542        // sorted set, because an empty one does not exist.
8543        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        // The destination is allowed to name its own source.
8549        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    /// `ZINTERCARD`, which counts without building anything.
8576    #[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        // A limit of zero is no limit, which is Redis's reading of it.
8583        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        // A negative limit and a limit that is not a number at all get the same
8592        // sentence, which looks like a mistake in Redis and is copied as one.
8593        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    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
8612    #[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        // No count is one member or a nil, a count is an array that may be
8617        // empty, and those are two reply types the client has to tell apart.
8618        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        // A positive count draws without replacement, so a count over the size
8623        // answers the whole set and never a member twice.
8624        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        // A negative one draws with replacement and answers exactly as many as
8630        // it was asked for, whatever the size of the set.
8631        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    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
8656    #[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        // A score stays a bulk string on RESP3, which is the one place the two
8672        // protocols agree about a score and everywhere else they do not.
8673        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    /// The count is what decides the shape, and its value is not.
8688    #[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        // No count, so one flat pair, and the score is a bulk string on RESP2.
8693        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        // A count, so pairs, and on RESP2 they are flattened into one run.
8697        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        // An empty array rather than a null, which is where a sorted set pop and
8702        // a list pop part company, and the same answer a count of zero gives.
8703        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        // The last member takes the key with it.
8707        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        // Both of these are the range error rather than the usual sentence about
8722        // integers, which is the odd answer and so the one worth copying.
8723        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    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
8733    #[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        // Nested on RESP2 as well, because the key name is already in front of
8742        // the pairs and there is nothing left to flatten into.
8743        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        // A null array and not a null, the same as LMPOP.
8748        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            // Two keys named and one given, so the word that should have been
8771            // the direction is a key and there is no direction left.
8772            &[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    /// The three that wait, when there is something there and they do not have
8782    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
8783    #[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        // Nothing to take, so the client is parked and nothing was written.
8812        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        // The timeout is read before the key count, so this complains about the
8821        // timeout and not about the count.
8822        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    /// A parked sorted set client is served by whatever puts a member under one
8837    /// of its keys, and is not served by something of another type landing
8838    /// there.
8839    #[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        // A string under the key is not what it asked for, so it stays parked
8845        // rather than being handed a WRONGTYPE on a command that was accepted.
8846        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        // And the member is gone, which is what makes a queue of workers on a
8858        // sorted set work at all.
8859        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    /// The same churn the set, the string and the list get, because a sorted
8910    /// set that leaks a tree node per add looks exactly like one that does not
8911    /// until it has run for an afternoon.
8912    #[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    // ------------------------------------------------------------------- geo
8942
8943    /// The three places every Redis geo example uses, and one more.
8944    ///
8945    /// Every reply this section asserts on came off a running 8.10.1 with these
8946    /// three loaded, byte for byte, including the number of digits in a
8947    /// coordinate and the four places on a distance.
8948    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        // A geo key is a sorted set and says so, which is not an implementation
8985        // detail either: a client removes a place with ZREM and counts them
8986        // with ZCARD, and the score is the number a real server stores.
8987        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        // A key that is not there is an empty one, and the two nulls are not
9007        // the same null: GEOPOS answers the array one and GEOHASH the string
9008        // one, which a RESP2 client can tell apart.
9009        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        // A member that is not there and a key that is not there are the same
9030        // nil, and the unit is read before the key is looked up, so a bad unit
9031        // on a missing key is still an error.
9032        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        // The older spelling of the same search, which is the same nine boxes
9067        // and the same order.
9068        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        // A count with no ordering means the nearest ones, so DESC has to be
9085        // asked for to get the far end.
9086        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        // Nothing inside a kilometre of that point, and nothing in a key that
9114        // is not there, and both are the empty array rather than an error.
9115        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        // The member itself is nothing away from itself, which is where the
9157        // fixed point writer's zero shows up on the wire.
9158        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        // Three options asked for, so each result is a four element array of
9195        // the member, the distance, the hash and a pair. The order of the three
9196        // is Redis's and not the order they were written in the command.
9197        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        // The same again through the older spelling, which stores the same
9249        // scores, so a key written by either is a geo key.
9250        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        // STOREDIST stores the distance in the search unit instead, and those
9268        // are full doubles rather than the four places WITHDIST writes. The
9269        // numbers on the right are what 8.10.1 stored for this search, and they
9270        // are compared with a tolerance rather than byte for byte because the
9271        // last bit of a haversine is the platform's sin, cos and asin: this
9272        // machine and that one disagree in the sixteenth digit, and so do two
9273        // Redis builds. Everything a client actually reads back is four places
9274        // and is asserted exactly above.
9275        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        // The order they went in is the order the scores put them in, which is
9308        // the point of storing the distance rather than the hash.
9309        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        // A search that finds nothing takes the destination with it rather than
9314        // leaving what was there, and a source key that is not there is a
9315        // search that finds nothing.
9316        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        // XX on a member that is already where it is changes nothing, and NX on
9338        // one that is there refuses to move it.
9339        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        // Out of range, and nothing is stored: the whole call is refused rather
9374        // than the good pairs going in and the bad one stopping it.
9375        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        // The count of triples is checked before the two gates are, and a call
9394        // with no triples at all reaches the same sentence.
9395        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    /// The sentences a search answers, which are its contract as much as the
9414    /// results are.
9415    #[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            // The two options a GEOSEARCH cannot leave out, each with its own
9537            // sentence, and the command quoted the way the client spelled it.
9538            (
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            // A store cannot also be asked for the distance, and the two
9563            // families name themselves differently in the same sentence.
9564            (
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            // The read only forms have no store at all, so the word is a stray
9594            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
9595            (
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    /// A wrong type wins over a bad argument, because the key is looked up
9629    /// first, and every one of the ten says the same thing about it.
9630    #[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        // And it wins over an argument that will not parse, which is the whole
9670        // reason the lookup comes first.
9671        assert_eq!(
9672            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9673            wrong
9674        );
9675    }
9676
9677    // ----------------------------------------------------------------- array
9678
9679    #[test]
9680    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9681        let mut f = Fixture::new();
9682        // Three consecutive positions from a high index, and the reply is how
9683        // many of them were empty before rather than how many were written.
9684        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        // A hole and a key that is not there are the same answer.
9692        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        // Scattered pairs in one command, last write wins within it.
9699        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    /// The two numbers an array reports are not the same number, and one of
9704    /// them does not fit a signed integer.
9705    #[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        // Deleting in the middle leaves the high water mark where it was.
9714        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        // The top of the space is addressable, and its length is a number with
9719        // bit sixty three set, so the reply has to be unsigned or it comes back
9720        // negative.
9721        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        // And one past it does not exist, so a write that would reach it fails
9725        // before any of it lands.
9726        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    /// One reply per position and not one per element, which is the whole
9734    /// reason the range is capped.
9735    #[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        // The two ends may come in either order, and the answer is reversed
9744        // rather than empty.
9745        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        // A key that is not there reads like an array of nothing but holes.
9750        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        // A range wider than a million positions is refused and not trimmed,
9755        // because against a missing key it is a request for as many nulls as
9756        // the range is wide.
9757        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    /// Every index in the argument list is read before the key is touched, so
9764    /// a bad one at the end leaves nothing half written.
9765    #[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        // An index is unsigned here, so the numbers a list would take are not
9780        // the last element, they are errors.
9781        assert_eq!(
9782            f.run(&[b"ARGET", b"a", b"-1"]),
9783            "-ERR invalid array index\r\n"
9784        );
9785        // And a pair list with an odd tail is an arity error rather than a
9786        // syntax one.
9787        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        // Two ranges in one command, and the second one covers the whole space
9804        // without walking it.
9805        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    /// A value goes out as the bytes it came in as, whichever of the three ways
9822    /// the array found to store it.
9823    #[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        // 42 is an integer, 007 is not one because it does not print back the
9832        // same, 3.5 survives a double and 3.14 does not, and the last two are a
9833        // word packed string and a blob.
9834        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        // And it is a body like any other, so the key commands work on it.
9853        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    /// Two of the array commands look the key up before they read the index and
9891    /// the rest read the index first, so the same broken argument gets two
9892    /// different errors depending on which command it went to.
9893    #[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        // And on a key that is an array the index is just an index.
9906        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        // A seek says where the next one goes, and a missing key has no cursor
9921        // to move and is not created by the asking.
9922        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        // The top of the space is the one index only ARSEEK will take, and it
9931        // leaves the cursor with nowhere to go.
9932        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        // Growing it after it has wrapped puts the survivors back in the order
9955        // they arrived, which is the whole point of paying for the rebuild.
9956        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        // The size is read before the key, so a bad one is a bad size wherever
9962        // it is sent.
9963        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        // Nothing asked for is an empty reply, and Redis answers that before it
9992        // has read the option or looked at the key.
9993        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        // With no cursor the tail of the array is the anchor, and a hole inside
10004        // the window is reported as one.
10005        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        // The whole index space, which ARGETRANGE refuses and this one answers
10018        // in three visits because holes cost nothing.
10019        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        // The two bounds take the ends of the array as well as an index, and a
10059        // reversed range is walked backwards the way ARSCAN walks one.
10060        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        // One test each. NOCASE reaches all four of them and it may be written
10074        // after the pattern it applies to.
10075        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        // OR is the default and AND has to be asked for, and either way the
10093        // last of a repeated option wins.
10094        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        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
10112        // not the positions it had to look at.
10113        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    /// Everything ARGREP refuses, in the order it refuses it.
10136    #[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        // The bounds are read before the plan, so a bad index beats a bad
10143        // predicate whichever way round the two are written.
10144        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        // A keyword with nothing after it, and a command that asks for nothing.
10150        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        // The arity is minus six, so a predicate keyword with no pattern after
10186        // it is short by one and never reaches the parser.
10187        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        // An aggregate is written with seventeen significant digits, which is
10211        // Redis's own choice and not what a score comes back as.
10212        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        // Nothing to work with is a null, and a missing key is a null for the
10221        // aggregates and a zero for the two that count.
10222        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        // Two values one apart are held sparsely, so the dense count is zero and
10260        // the two dense averages have nothing to average.
10261        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        // On RESP3 the same reply is a map and the averages are doubles.
10270        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        // Whole numbers up to two to the sixty second come back as integers,
10283        // and past that the digit generator takes over and uses an exponent.
10284        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        // The same bytes on RESP3, where the reply is a double rather than a
10304        // bulk string.
10305        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        // The two float increments are not this printer. They go through
10310        // ld2string in its human mode, which is a fixed point conversion with
10311        // the trailing zeros taken off, so they never write an exponent, and
10312        // they reply with a bulk string on both protocols.
10313        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    // ----------------------------------------------------------------- graph
10325
10326    #[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        // The year comes back as the four bytes that were sent and not as a
10336        // number, because every property is text and there is nothing on the
10337        // wire that says which of `1815` and `"1815"` the client meant. The
10338        // fields are in the document's order, which is sorted by name, because
10339        // that is what makes a field lookup a binary search.
10340        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        // A second write to the same id replaces the document and says so with
10345        // a zero, so an ingest can count what it created.
10346        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        // A node with no properties is an empty map and not a null, which is
10355        // how a client tells an isolated node from one that is not there.
10356        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        // A field with no value creates nothing, because the pairs are checked
10362        // before the key is touched.
10363        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        // On RESP3 the same reply is a map.
10370        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        // Neither end was written first and both are there, as empty nodes.
10389        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        // The same pair under the same label again updates the edge rather than
10400        // making a second one.
10401        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        // A different label between the same pair is a different edge.
10409        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        // A label nothing has used, an end that is not there, and a key that is
10427        // not there are all a zero rather than an error.
10428        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    /// A run is paged the way `SCAN` is paged, so a client that can walk one
10443    /// can walk the other.
10444    #[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        // Ten without being asked, and the cursor is where to carry on from.
10452        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        // A cursor past the end is an empty page and not an error, and so is a
10487        // key or a label that is not there.
10488        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    /// A walk answers which nodes it can reach and not by how many routes, so a
10530    /// node two ways out is in the frontier once.
10531    #[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        // Two hops without being asked, the start left out, and edsger once
10544        // even though both of the first hop's nodes point at it.
10545        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        // COUNT stops the walk rather than trimming what it found.
10557        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        // A node nothing leaves is an empty array and not an error.
10562        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    /// The two sided search, which is the whole reason `G.PATH` is a command
10576    /// and not something a client builds out of `G.OUT`.
10577    #[test]
10578    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10579        let mut f = Fixture::new();
10580        // A chain of six, and a shortcut that makes a shorter way round under a
10581        // second label so the search has to take either kind of hop.
10582        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        // A node to itself is a path of one, and a depth too short to reach is
10597        // no path at all.
10598        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        // Direction counts: the chain only goes one way.
10607        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10608        // An unreachable node, a node that is not there, and a key that is not
10609        // there are the same empty answer.
10610        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    /// The point of the escape in the record tag: the keyspace owns a graph key
10621    /// the way it owns every other key, and none of these commands know a graph
10622    /// exists.
10623    #[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        // A graph is counted against the server the way every other body is,
10636        // which is what `maxmemory` will read when this key is a million nodes.
10637        // There is no `MEMORY USAGE` command yet, so this asks the server.
10638        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        // An expiry, then a rename, then a move to another database, all of
10651        // which are the keyspace moving a record it cannot look inside.
10652        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    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
10668    /// rather than answering the way they answer for a key that is not there.
10669    #[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        // A refused copy leaves both keys exactly as they were.
10686        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10687    }
10688
10689    /// A graph key is a key, so the commands for the other types refuse it and
10690    /// the graph commands refuse theirs.
10691    #[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    /// Every other collection here takes its key with it when its last member
10718    /// goes, and a graph is no different.
10719    #[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        // The node and the edges that hung off it are both gone.
10727        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        // The id the removed node had is not handed out again, so a client
10741        // holding an id from an earlier reply cannot have it mean another node.
10742        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    // ------------------------------------------------------------------ json
10753
10754    /// The two path syntaxes answer different shapes, which is the thing a
10755    /// client is most likely to be broken by and so the thing to pin first.
10756    #[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        // No path at all is the legacy root and not `$`, so the document comes
10762        // back as itself rather than wrapped.
10763        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        // A path that matched nothing is an empty set on one syntax and an
10771        // error on the other, and the error does not quote the path.
10772        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        // The key is a document to the rest of the keyspace, under the name
10779        // RedisJSON registers, and every generic command works on it.
10780        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    /// The two error lines RedisJSON sends without a prefix in front of them.
10788    ///
10789    /// Every other error this server writes starts `ERR` or `WRONGTYPE`. These
10790    /// two do not, on a real server, and a differential harness compares the
10791    /// whole line.
10792    #[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        // A wildcard that matched something writes to all of it. A wildcard
10805        // that matched nothing would have to invent a place, and that is the
10806        // other unprefixed line.
10807        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    /// What `JSON.SET` does with a path that named nowhere.
10819    #[test]
10820    fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10821        let mut f = Fixture::new();
10822        // A key that is not there can only be written whole.
10823        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        // The root check comes before NX and XX, which is the order a real
10829        // server checks them in.
10830        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        // One step past a container that is there is a place to write.
10844        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10845        // One step past something that is not, or past something that is not an
10846        // object, is not an error and is not a write either.
10847        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        // An index past the end does not append. JSON.ARRAPPEND appends.
10853        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        // NX on a path that is there and XX on a path that is not are both a
10863        // nil and neither changes anything.
10864        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        // Text that is not JSON is refused before the key is touched. The
10877        // line has no `ERR` in front of it, which is this command's and not
10878        // every command's, and is in D-37.
10879        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    /// `JSON.DEL`, `JSON.TYPE`, `JSON.TOGGLE` and `JSON.CLEAR`, each of which
10887    /// answers a count or a word rather than text.
10888    #[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        // The one place a legacy path that matched nothing is a nil rather than
10902        // an error, which lines up with a key that is not there.
10903        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        // A boolean flips and answers the value it now has, as an integer on
10907        // one syntax and as the word on the other.
10908        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        // Something that is not a boolean is a hole on one syntax and one
10911        // sentence covering both cases on the other.
10912        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        // Clearing empties containers and zeroes numbers and leaves everything
10927        // else alone, and counts only what it changed.
10928        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        // Deleting counts what it removed, and deleting the root is deleting
10937        // the key.
10938        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        // Deleting the last member of the root container deletes the key, the
10941        // same way popping the last element off a list does. It is a rule about
10942        // deleting and not about shape: a document written as an empty object
10943        // by JSON.SET stays, because nothing was removed from it.
10944        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    /// `JSON.GET` with more than one path, and with a layout.
10955    ///
10956    /// The wrapper the reply is built in is laid out too, so what a path
10957    /// matched starts one level in for a single JSONPath and two for one of
10958    /// several, and getting that wrong is the kind of thing only a byte for
10959    /// byte comparison catches.
10960    #[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        // Legacy paths are not wrapped, even when there are several of them.
10970        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        // The options are read before the paths and in any order, and a
10991        // document with nothing to lay out is the same either way.
10992        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    /// `JSON.MGET`, which is the only command here that reads more than one key
10998    /// and so the only one whose answer has holes in it.
10999    #[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        // A key that is not there and a key holding something else are both a
11010        // hole rather than an error, the way MGET treats a hash.
11011        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        // A legacy path that matched nothing is a hole too, because one bad
11016        // answer should not lose the others.
11017        assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
11018    }
11019
11020    /// The four commands that ask how big something is, and the four different
11021    /// sets of answers they give for the same three failures.
11022    ///
11023    /// There is no pattern in this and there is no reading it off the
11024    /// documentation either. It was read off a running RedisJSON one line at a
11025    /// time, and it is written down here because the error text is what a client
11026    /// library branches on.
11027    #[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        // A JSONPath answers one entry per match and a hole for a match of the
11042        // wrong kind, which is the one shape all four agree on.
11043        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        // A legacy path that matched nothing. Two of them are an error and two
11049        // of them are a nil, and the two errors do not use the same sentence.
11050        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        // A nil bulk and not an empty array, even though the answer would have
11060        // been an array, which is what RedisJSON sends here too.
11061        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
11062        // The JSONPath spelling of the same question is an empty array, since
11063        // no match is not a failure on that syntax.
11064        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
11065
11066        // A legacy path that matched the wrong kind of value. Now two of them
11067        // are an ERR and two of them are a WRONGTYPE, and it is not the same
11068        // two.
11069        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        // A key that is not there, where the two syntaxes swap over: the legacy
11087        // path is the quiet answer and the JSONPath is the error.
11088        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        // Except this one, which answers about the path instead.
11097        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    /// `JSON.ARRAPPEND`, `JSON.ARRINSERT`, `JSON.ARRTRIM` and `JSON.ARRPOP`.
11104    ///
11105    /// The four of them share one error line for a path that named something
11106    /// that is not an array, and they disagree about what an index outside the
11107    /// array means: insert refuses it and the other two clamp.
11108    #[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        // A negative index counts back from the end, and the end itself is a
11121        // place to insert at, so an insert at the length is an append.
11122        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        // One past the end is not, and neither is one before the front.
11135        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        // Trim takes both ends inclusive and clamps both of them, so a start
11145        // past the end leaves an empty array rather than an error.
11146        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        // Pop clamps as well, its default is the last element, and an empty
11164        // array pops a nil rather than failing.
11165        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        // One sentence covers a path that matched nothing and a path that
11172        // matched the wrong kind of value, for all four of them.
11173        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        // A key that is not there is the same sentence for all four, on either
11195        // syntax, and it is about the key and not about the path.
11196        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        // The values are parsed before the key is touched, so text that is not
11206        // JSON leaves the document alone.
11207        // Text that is not JSON is refused before the key is touched, and
11208        // the line has no `ERR` in front of it, which is D-37.
11209        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    /// `JSON.ARRINSERT` refuses the whole command when any one of the arrays a
11217    /// path matched cannot take the index, which is D-36.
11218    ///
11219    /// RedisJSON walks the matches, inserts into each one it can, and returns
11220    /// the error on the first one it cannot, leaving the earlier inserts in the
11221    /// document. A write here is one list of edits applied together, so either
11222    /// all of them happen or none of them do.
11223    #[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        // Every match can take the index, so every match gets it.
11237        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    /// `JSON.ARRINDEX`, whose stop is exclusive and whose start clamps to the
11248    /// last element rather than to one past it.
11249    ///
11250    /// Both of those read like mistakes and both are what RedisJSON does. The
11251    /// start is the one that bites: a start of five into an array of four still
11252    /// looks at the fourth, so a search that should have run out of array comes
11253    /// back with an answer.
11254    #[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        // Zero as the stop means the end rather than the front, so leaving it
11267        // off and passing it are the same thing.
11268        assert_eq!(
11269            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
11270            ":3\r\n"
11271        );
11272        // The stop is exclusive, so a stop of three does not look at index
11273        // three.
11274        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        // The start clamps to the last element in both directions, which is why
11280        // a start of four, five or minus one all find the 1 at index three.
11281        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        // An empty array is the one case that comes back with nothing, since
11294        // the stop is zero and the loop never starts.
11295        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        // The comparison is structural rather than one of the encoded bytes,
11302        // because an object in a stored document holds its keys as intern table
11303        // ids where one parsed off the wire holds them as bytes.
11304        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        // Its errors are a third set again: a missing legacy path is the short
11319        // sentence, the wrong kind of value is a WRONGTYPE, and a key that is
11320        // not there is about the path on either syntax.
11321        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    /// The number family answers text and keeps an integer an integer until
11340    /// something in the sum is not one.
11341    #[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        // A legacy path answers the new value as JSON text in a bulk string,
11348        // not as a number, which is the shape all three of them use.
11349        assert_eq!(
11350            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11351            bulk("9").as_str()
11352        );
11353        // A JSONPath answers a bulk string holding a JSON array.
11354        assert_eq!(
11355            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11356            bulk("[11]").as_str()
11357        );
11358        // Two integers stay an integer and a double anywhere in it makes the
11359        // answer a double, which the document then holds.
11360        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        // A power of a half is a square root, and the square root of a negative
11377        // number is the error that says the answer is not a number.
11378        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        // An integer answer that does not fit is refused rather than promoted,
11388        // and a negative exponent lands in the same error because there is no
11389        // integer answer to two to the minus one.
11390        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        // A double that leaves the finite numbers is the other error.
11401        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        // A match that is not a number is a null inside the array on a
11408        // JSONPath, and a legacy path that found no number at all is the error
11409        // with the module's own typo in it.
11410        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        // The operand is JSON and has to be a number. Valid JSON that is not
11427        // one is a line of its own, and it goes out without a prefix.
11428        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    /// `JSON.STRAPPEND` puts its path in the middle and makes it optional,
11443    /// which nothing else in the group does.
11444    #[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        // The length is in bytes and not in characters, so one two byte letter
11458        // takes it up by two.
11459        assert_eq!(
11460            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11461            ":6\r\n"
11462        );
11463        // Three arguments means the value is the last one and the path is the
11464        // root, so this appends to a document that is a string on its own.
11465        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        // The value is JSON and has to be a JSON string. A number is a
11470        // WRONGTYPE about a path value even though it was the value that was
11471        // wrong, which is the module's wording and not a slip here.
11472        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    /// A legacy path can match more than one value, and which of them the one
11495    /// answer comes from is not the same choice twice.
11496    #[test]
11497    fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11498        let mut f = Fixture::new();
11499        // Three arrays of one, two and three elements, which tells the first
11500        // match and the last match apart in a single command.
11501        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        // Every one of them wrote to all three matches, whichever one it chose
11539        // to answer about.
11540        assert_eq!(
11541            f.run(&[b"JSON.GET", b"doc", b".a"]),
11542            bulk("[false,true,false]").as_str()
11543        );
11544
11545        // A match of the wrong kind is skipped rather than being the answer, so
11546        // a path that found a string and then two arrays still answers.
11547        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        // Nothing of the right kind anywhere is the error, and that is the only
11557        // case that is.
11558        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        // The one array that was there and had nothing in it is an answer and
11568        // not a skip, so the pop answers about it rather than about the array
11569        // after it.
11570        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    /// A path that matched a value and something inside that value writes to
11579    /// both, which is what `$..` and a nested wildcard are for.
11580    #[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        // The same for a trim, where the outer array keeps the two elements the
11596        // inner writes landed in.
11597        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        // And for a number, where the first match is the object the outer array
11608        // holds and only the two inside it are numbers.
11609        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    /// The value a write is given is looked at only once the path has found
11617    /// something of the right kind to use it on.
11618    #[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        // A string is not a number, so the path answers first and the `"x"` is
11624        // never looked at. Same for the value that is not JSON at all.
11625        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        // A number match anywhere and the value is looked at after all.
11642        assert_eq!(
11643            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11644            "-bad input number\r\n"
11645        );
11646
11647        // JSON.STRAPPEND follows the same order with its own two answers.
11648        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        // A key that is not there still comes before either of them.
11662        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    /// RFC 7386 in one test: a null deletes, everything else merges, and a
11673    /// patch that is not an object replaces what it lands on.
11674    #[test]
11675    fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11676        let mut f = Fixture::new();
11677
11678        // A key that is not there is created at the root, nulls and all,
11679        // because a deletion with nothing to delete is still what the client
11680        // sent.
11681        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        // Onto something that is there, a null deletes the member of that name
11691        // and the rest is merged one level at a time.
11692        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        // A patch that is not an object replaces what it is merged onto.
11703        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        // A patch object onto a value that is not an object starts from an
11710        // empty object, so this time the null has nothing to delete and is
11711        // dropped rather than stored.
11712        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        // A member one level past the end of the document is created and keeps
11722        // its nulls, two levels past it is a write that did not happen, and a
11723        // path that would have to invent where it goes is the unprefixed line.
11724        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        // A wildcard merges every match.
11742        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        // The three ways to get it wrong.
11753        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    /// A descent is the one path that matches a value and something inside that
11769    /// same value, and the inner merge has to survive the outer one.
11770    #[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        // `a`, `a.b`, `c` and `c[0]` all match. `a.b` is merged first and `a` is
11779        // merged onto the result, so the `{"m":1}` written into `a.b` is still
11780        // there. Doing it the other way round would leave `{"a":{"b":1,"m":1}}`.
11781        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        // A deletion down the same path, which is the case where the inner
11787        // merge empties the object the outer one then copies.
11788        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    /// A filter is a selector like any other, so every command that takes a path
11800    /// takes one, reads and writes alike.
11801    #[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        // `$` inside the expression is the document, so a member can be measured
11812        // against something that is not inside it.
11813        assert_eq!(
11814            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11815            bulk(r#"["a","c"]"#).as_str()
11816        );
11817        // The legacy syntax takes one too, and answers the first match.
11818        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        // A write goes through it as far as a value that is already there. A
11828        // field that is not there yet has nowhere definite to go, which is the
11829        // same refusal a wildcard gets.
11830        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        // A path that does not parse is refused before the document is read, so
11852        // a key that is not there answers the same way.
11853        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    /// The operators past the comparisons, over the wire rather than in the
11864    /// parser's own tests, so that a client can reach all of them.
11865    #[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        // A write goes through one of these the same way it goes through a
11892        // comparison.
11893        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    /// D-41. RedisJSON refuses this one, and which document it refuses is
11904    /// decided by how it happens to hold an array of numbers.
11905    #[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        // The same document with one element that is not an integer is the one
11918        // RedisJSON is happy with, and it goes the same way here.
11919        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    /// `JSON.MSET` checks what it can before it writes anything and skips the
11931    /// one thing it cannot, which is a path with nowhere to put its value.
11932    #[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        // A repeated key takes the last write.
11943        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        // A triple whose path names nowhere is skipped, the others are still
11950        // written and the reply turns into a nil. Both ways round, because a
11951        // loop that gave up at the first skip would agree with this on one
11952        // order and not on the other.
11953        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        // A value that is not JSON, a key holding something else and a path
11966        // that would have to create a document below its own root are all
11967        // checked before anything is written, so the good triple next to them
11968        // does not happen either.
11969        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        // The two errors a path can be are checked up front as well, so the
11985        // triple before them is not written either. A wildcard that matched
11986        // nothing has nowhere to invent, and an index that is not in the array
11987        // is out of range, and both of them stop the whole command.
11988        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        // Every triple is worked out against the keyspace as the command found
11999        // it, so a second triple on the same key does not see the first one and
12000        // the last write is the one that stays.
12001        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        // An argument count that is not a run of key, path and value is the
12012        // arity error rather than a syntax one.
12013        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    /// `JSON.RESP` hands back RESP types, and the marker element is what tells
12020    /// an empty array and an empty object apart.
12021    #[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        // A JSONPath wraps the same answer in one more array.
12030        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        // A double goes out as its text, so a client reads the same digits
12044        // `JSON.GET` would have given it.
12045        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        // A missing legacy path is an error, a missing JSONPath is an empty
12050        // array, and a key that is not there is a nil on either.
12051        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    /// `JSON.DEBUG` answers a byte count that is this encoding's, so the test
12061    /// pins the shapes and that the two syntaxes agree rather than a number
12062    /// read off another server. That is D-42.
12063    #[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        // A key that is not there is a zero on a legacy path and an empty set
12077        // on a JSONPath, which is the one reader here that does not answer nil
12078        // for it.
12079        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    // ---------------------------------------------------------------- vector
12106
12107    /// The first `VADD` fixes the dimension and every one after it has to
12108    /// agree, because there is no create command to say it earlier.
12109    #[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        // A second vector under the same name replaces it and says so with a
12119        // zero, so an ingest can count what it created.
12120        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        // Three dimensions into a two dimensional set names both numbers, since
12126        // a client that gets this wrong needs to know which end is which.
12127        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        // A vector of zeros has no direction, and it is taken anyway and comes
12132        // back as the origin, because that is what a real server does with it.
12133        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        // A set is made with one quantisation and keeps it, and a `VADD` that
12142        // names another is refused. Naming none names `Q8`, which is why this
12143        // set is a `Q8` one.
12144        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        // Nothing above created a key, and a set that never took a vector has
12149        // no dimension to report.
12150        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    /// What a client sent comes back out, and what a client asked for is a
12156    /// similarity and not the distance underneath it.
12157    #[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        // The set stored the direction and the length is multiplied back on the
12162        // way out, so this is `3 4` and not `0.6 0.8`. It is not quite `3 4`
12163        // either, because nobody named a quantisation and that means `Q8`: the
12164        // wider coordinate lands on a code exactly and the other one does not.
12165        // Both numbers are a real server's answers for the same input.
12166        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        // NOQUANT is the way to ask for what went in to come back out.
12171        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        // BIN keeps the signs and nothing else, and does not multiply the
12177        // length back on, since a sign has no length in it to scale.
12178        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        // On the axes, where the unit vector is exact and so is the dot
12187        // product, both ends of the scale come out exact: the same direction is
12188        // 1 and the opposite one is 0, with a right angle at a half.
12189        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        // A search from an element leaves that element out, since it is always
12199        // its own nearest neighbour.
12200        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        // An element that is not there is an empty answer and not an error,
12205        // which is what a missing key gives too.
12206        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        // COUNT bounds it and TRUTH reads every vector rather than the codes,
12209        // which has to agree with the index on a set this small.
12210        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        // EF widens how much of the index is read and does not change how many
12219        // answers come back, so a wide search still returns what COUNT asked
12220        // for.
12221        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        // On RESP3 a scored search is a map, which is what the vector set
12227        // module replies and is not what ZRANGE does here.
12228        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    /// The attribute pair, and the one reply that means two things.
12238    #[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        // Not parsed as JSON, because nothing reads into it yet and refusing a
12246        // write for a rule nothing enforces would be the wrong trade.
12247        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        // An empty string clears it, which is Redis's spelling of the removal.
12250        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        // An element that is not there answers zero rather than being created,
12253        // since an attribute with no vector under it is not a thing this holds.
12254        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        // A null for an element with no attribute and a null for one that is
12258        // not there. VISMEMBER is how a client tells the two apart.
12259        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        // WITHATTRIBS carries it alongside the answers.
12265        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    /// The slot a removed element had is reused, and nothing that was beside it
12274    /// comes back with the next element to get it.
12275    #[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        // The key went with the last element, the way every other collection
12284        // here works.
12285        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12286
12287        // The next element is given the slot the removed one had, and it comes
12288        // with no attribute on it.
12289        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    /// `VINFO` says what the index is before it says anything a client could
12298    /// mistake for a graph.
12299    #[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        // What the client asked for and not what happened to the tuning, which
12309        // is `10` section 7: M is recorded and changes nothing.
12310        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        // Nobody named a quantisation, so this set is a `Q8` one and every
12314        // element in it is stored that way.
12315        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    /// A set to read ranges of names out of.
12329    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    /// `VRANGE` reads the names in the order bytes come in and pays no
12350    /// attention to where the vectors point.
12351    #[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        // Bytes and not letters, so an upper case name sorts before every lower
12373        // case one rather than beside its own spelling.
12374        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    /// The count cuts the answer after the range is decided, and zero is not
12383    /// the same as leaving it out.
12384    #[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    /// Both ends are read before either is placed, and the count is read before
12400    /// either end.
12401    #[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        // A bracket with nothing after it is not the empty name here, though an
12419        // element really can be called that.
12420        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    /// The option that asks for something this index does not have says so
12440    /// rather than doing something else quietly.
12441    #[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    /// A filtered search answers with the nearest elements that match, and an
12455    /// expression that is not one is an error before the key is looked at.
12456    #[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        // `b` is the nearest to the query and is the one the filter drops, so
12478        // this is the answer a filter applied afterwards would have got wrong.
12479        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        // A number is compared as a number, and the two halves of an `and` both
12495        // have to hold.
12496        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        // A list, and a field an element does not have.
12510        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        // TRUTH measures every vector, and the filter still decides which ones
12537        // are measured.
12538        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        // VSETATTR moves an element in and out of a filter, which means the tag
12553        // beside its code was rewritten and not just the string.
12554        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        // And a VADD that replaces the vector keeps the attribute and the tag,
12571        // which is the same rewrite from the other end.
12572        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        // The expression is parsed before the key is read, so a bad one is an
12590        // error whether or not the key is there.
12591        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        // FILTER-EF raises the effort rather than capping it, and zero is
12598        // Redis's word for no limit, so neither is an error.
12599        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    /// A vector set key is a key, so the keyspace owns it the way it owns every
12647    /// other one and none of those commands know what is inside it.
12648    #[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        // And the wrong type is the wrong type in both directions.
12664        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        // A graph and a vector set share the escape in the record tag and are
12679        // still two different types, which is the case the tag alone cannot
12680        // decide.
12681        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    /// `VRANDMEMBER` is `SRANDMEMBER` over the element names, in both of its
12693    /// shapes, off the database's own generator.
12694    #[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        // One element is a bulk string and not an array of one.
12702        let one = f.run(&[b"VRANDMEMBER", b"v"]);
12703        assert!(one.starts_with("$1\r\n"), "{one}");
12704        // A positive count is distinct and stops at the size of the set.
12705        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        // A negative one draws that many and allows repeats.
12713        let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12714        assert!(many.starts_with("*5\r\n"), "{many}");
12715        // A key that is not there answers the shape that was asked for.
12716        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    /// `VLINKS` answers about the index that is here rather than the graph that
12721    /// is not, which is D-2.
12722    #[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        // One layer deep, because the index is one layer deep, so a client
12728        // walking layers gets a short list and not a shape it cannot parse.
12729        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    /// A vector arrives either as digits or as bytes, and the two have to mean
12742    /// the same thing.
12743    #[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        // RAW is the stored bytes and the numbers that turn them back into the
12757        // client's vector, which for `Q8` is a code a coordinate, the length the
12758        // vector arrived with and the scale the codes are measured against. The
12759        // name of the form is a simple string, which is a real server's shape,
12760        // and all four of these are a real server's answers.
12761        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        // A blob that is not a whole number of floats is not a vector.
12766        assert_eq!(
12767            f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12768            "-ERR invalid vector specification\r\n"
12769        );
12770        // Neither is a count that promises more than arrived.
12771        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    // ----------------------------------------------------------------- bloom
12779
12780    /// The filter a client gets when it does not describe one, and the two
12781    /// answers an add can give.
12782    #[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        // The defaults are the module's configs and not anything the command
12791        // said, which is 100 entries at a hundredth and a growth of 2.
12792        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        // A key that is not there has no filter to report on, and answers two
12801        // different ways about it depending on which command asked.
12802        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    /// `BF.EXISTS` on a key holding something else answers a miss, and
12807    /// everything else in the family answers `WRONGTYPE`.
12808    ///
12809    /// The two halves of a check and set disagree about what that key is, which
12810    /// is the module's behaviour and not a decision taken here.
12811    #[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        // The arguments are read before the key is, so a reserve with a bad
12829        // error rate complains about the rate and never learns about the string.
12830        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    /// A chain grows by its expansion factor and each link is half as wrong as
12841    /// the one before, which is what makes the whole filter hold its rate.
12842    #[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        // Capacity is the sum of every link and not the number that was asked
12856        // for, so it is 10 and then 10 plus 20.
12857        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        // The same filter told not to grow fills instead.
12866        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        // And an item that is already in it still answers, because membership
12877        // is checked before fullness.
12878        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12879        // A filter that will not grow has no expansion rate to report, in
12880        // either of the two spellings that make one.
12881        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        // Asking for both at once is refused, which is one of the module's
12885        // errors that carries no prefix at all.
12886        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    /// A multi add stops where the filter did, so the reply can be shorter than
12901    /// the argument list.
12902    #[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    /// `BF.INSERT` describes a filter and fills it in one command, with its own
12917    /// spelling of every complaint.
12918    #[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        // NOCREATE is the only way to add without making the key.
12937        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        // The same mistakes as BF.RESERVE, in the sentences this command uses
12943        // for them, and one sentence where BF.RESERVE has two.
12944        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        // An option is matched on its first letter and not on the word, so a
12957        // token nobody meant as an option is one anyway if it starts with the
12958        // right letter. NOSUCHTHING is NONSCALING here, and the filter it
12959        // builds says so.
12960        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        // Only E and N need a second look, one for ERROR against EXPANSION and
12966        // the other for NOCREATE against NONSCALING, and both stop as soon as
12967        // they can tell the two apart.
12968        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        // A letter that starts nothing is the one case that is refused.
12982        assert_eq!(
12983            f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12984            "-Unknown argument received\r\n"
12985        );
12986        // Everything after ITEMS is an item, even when it spells an option.
12987        assert_eq!(
12988            f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12989            "*1\r\n:1\r\n"
12990        );
12991        // And ITEMS with nothing after it is the same as leaving it out.
12992        assert!(
12993            f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
12994                .contains("wrong number of arguments")
12995        );
12996    }
12997
12998    /// A filter dumped a chunk at a time and put back into another key is the
12999    /// same filter.
13000    #[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        // Iterator zero asks for the header and every one after it is a running
13010        // byte offset, and a chunk never spans two links.
13011        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        // A header on top of a filter is refused rather than merged, and so is
13049        // one that no filter wrote.
13050        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        // An offset past the end of the filter names itself.
13059        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        // The same complaint without the prefix on the way out, which is the
13068        // module's inconsistency and not a slip here.
13069        assert_eq!(
13070            f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
13071            "-Second argument must be numeric\r\n"
13072        );
13073    }
13074
13075    /// The argument checks, which have a sentence each and read numbers the way
13076    /// Redis reads them everywhere else.
13077    #[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        // Trailing rubbish after the capacity is ignored rather than refused.
13124        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    /// The RESP3 shapes, which are where this family differs most from RESP2.
13143    #[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        // The count stays an integer, because it counts rather than answers.
13156        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        // One field is a map of one here and a bare array of one on RESP2, so
13164        // this is the reply where the two protocols carry different facts.
13165        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    // ---------------------------------------------------------------- cuckoo
13172
13173    /// A dump header, which is the four counts and the three widths a filter
13174    /// writes in front of its fingerprints.
13175    ///
13176    /// Written by hand rather than taken from a `CF.SCANDUMP`, because what the
13177    /// tests below want out of it is the states a filter cannot be put into
13178    /// from the wire.
13179    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    /// The filter a client gets when it does not describe one, and the thing a
13197    /// cuckoo filter does that a Bloom filter cannot, which is count copies and
13198    /// take them out again.
13199    #[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        // The NX form is the one that looks first, which is why it is a command
13206        // of its own rather than an option.
13207        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        // The defaults are the module's configs: 1024 entries over buckets of
13216        // two, twenty kicks and a chain that grows by one.
13217        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        // A delete takes one copy, so the same item goes twice and then stops.
13233        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        // A key with no filter under it gets three different sentences and one
13240        // plain miss, depending on which command asked.
13241        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        // And `CF.COMPACT` is declared as taking any number of keys and takes
13249        // exactly one, which is the module's own arity being wrong rather than
13250        // this table's.
13251        assert!(
13252            f.run(&[b"CF.COMPACT", b"a", b"b"])
13253                .contains("wrong number of arguments")
13254        );
13255    }
13256
13257    /// The four that only read fingerprints treat a key holding something else
13258    /// as a key with no filter, and everything else answers `WRONGTYPE`.
13259    #[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        // `CF.DEL` writes and is still in that group, and `CF.COMPACT` writes
13267        // and is declared read only, so neither of the two halves of the family
13268        // is the same set as the flags say.
13269        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    /// `CF.RESERVE` reads its options by name in an order of its own, and the
13291    /// first pair with a given name is the only one it looks at.
13292    #[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        // The range is the bucket size's and not a constant, so a capacity that
13322        // was fine at two slots a bucket is not at four.
13323        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        // The capacity is checked last, so a command that is wrong twice
13333        // answers about the option. Which option it answers about is the order
13334        // the module looks for them in and not the order they were written, so
13335        // a bad kick budget wins over a bad bucket size wherever the two sit.
13336        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        // A second pair with a name that has already been read is not looked at
13365        // at all, so this one is a filter with buckets of one rather than an
13366        // error about a bucket size of zero.
13367        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        // A pair nobody knows is dropped, which is the opposite of what
13380        // `CF.INSERT` does with the same mistake.
13381        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        // And an option with nothing after it leaves an odd number of them,
13391        // which is an arity error rather than a complaint about the option.
13392        assert!(
13393            f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13394                .contains("wrong number of arguments")
13395        );
13396    }
13397
13398    /// `CF.INSERT` is a reserve and a multi add, with a grammar that agrees
13399    /// with `CF.RESERVE` about nothing.
13400    #[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        // The NX form has three answers rather than two, which is why it stays
13413        // integers on both protocols.
13414        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        // The bucket size cannot be given here, so the range names the config
13429        // that holds it instead of the option `CF.RESERVE` names.
13430        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        // Every occurrence is checked, which is where this differs from
13435        // `CF.RESERVE`: the second `CAPACITY` is an error even though the first
13436        // one is the one that would have been used.
13437        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        // An option is one letter and not a word, so `NOSUCH` is `NOCREATE` and
13451        // `ITEMSXYZ` is `ITEMS`, and only a letter that starts nothing is
13452        // refused.
13453        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        // Everything after ITEMS is an item, even when it spells an option.
13466        assert_eq!(
13467            f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13468            "*1\r\n:1\r\n"
13469        );
13470        // And the two ways of sending no items at all are the same complaint.
13471        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    /// The two walls a filter can hit, which say different things and are not
13482    /// the same wall.
13483    #[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        // The add commands say it in a sentence and the insert commands say it
13504        // in the array, one value per item, and the array is never short.
13505        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        // A chain that is allowed to grow stops for a different reason, and the
13515        // count it stops at is the filter limit rather than the room: this one
13516        // gives up with three slots free. Loading a chain that already has
13517        // every filter it is allowed shows why, since it refuses an item
13518        // straight into an empty one.
13519        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    /// A filter dumped a chunk at a time and put back under another key is the
13535    /// same filter, and the headers that describe one nobody could build are
13536    /// refused on the way in.
13537    #[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        // Position zero asks for the header and every one after it is a byte
13553        // offset across every filter laid end to end, and the walk ends on a
13554        // zero and a nil rather than an empty chunk.
13555        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        // A filter with nothing in it hands out no header at all, so a client
13597        // that dumps one has nothing to load back.
13598        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        // The positions this end will not take, which are not the same set at
13605        // both ends: a dump refuses a negative one and a load takes it as an
13606        // offset and fails to find anything there.
13607        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        // A header on top of a filter is refused rather than merged.
13624        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        // A chunk that is not the size of a header where a header should have
13630        // been is one sentence, and one that is the size of a header and
13631        // describes a filter nobody could build is another.
13632        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            // The count is written in eight bytes and read into two, so a
13654            // number that is a multiple of the second arrives as none.
13655            (
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    /// The RESP3 shapes, which are where this family differs most from RESP2
13669    /// and where one of its answers stops being readable.
13670    #[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        // The count stays an integer, because it counts rather than answers.
13685        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        // `CF.INSERT` writes a boolean per item here and an integer per item on
13695        // RESP2, and minus one has nowhere to go in a boolean, so a RESP3
13696        // client cannot tell an item that did not fit from one that is already
13697        // there. `CF.INSERTNX` keeps its integers for exactly that reason.
13698        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        // The end of a dump is a nil and not an empty chunk, which is one
13727        // underscore here and a negative length on RESP2.
13728        assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13729    }
13730
13731    // ------------------------------------------------------------------- cms
13732
13733    /// A sketch is made from either end, and both constructors look at the key
13734    /// before they look at their arguments.
13735    #[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        // Two over the error rounded up, and the log of the probability over the
13746        // log of a half rounded up, which for these two is 200 by 6.
13747        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        // The key is checked first, so a width of zero at a key that is already
13756        // there is about the key and not about the width.
13757        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        // A probability whose float conversion is zero has no depth, and a width
13778        // past a signed sixty four bit integer has no width, and both are the
13779        // same sentence.
13780        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        // And a sketch bigger than a gibibyte of counters is refused here where
13785        // the reference reserves address space nobody has touched, which is
13786        // D-47.
13787        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    /// Every pair is parsed before any of them lands, the counters saturate,
13795    /// and the count is a signed total of what was asked for.
13796    #[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        // An item that is incremented twice in one call sees its own first
13805        // increment in the reply to the second.
13806        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        // A bad number anywhere means nothing at all is applied.
13811        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        // The counters stop at four billion and the item that stopped says so in
13824        // its own slot while the one beside it answers a number.
13825        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        // The count is what was asked for rather than what landed, and it is
13832        // signed, so a big enough total comes back negative.
13833        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        // An odd number of arguments after the key is an arity error and not a
13841        // syntax one.
13842        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    /// A merge overwrites its destination, and it is worked out in full before
13857    /// any of it is written.
13858    #[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        // Overwritten and not added to, so the same merge twice is the same
13872        // answer twice.
13873        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        // A cell times a weight is checked wide rather than wrapped, so this is
13893        // a refusal and the destination is left exactly as it was.
13894        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        // The destination comes first, then the count, then the layout, then the
13907        // weights, then the sources one at a time.
13908        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    /// A key holding anything else is `WRONGTYPE` to all six, and a key holding
13936    /// a sketch is refused by the two commands that would have to serialise it.
13937    #[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            // The two constructors see the key before anything else and say so
13951            // in the module's own words, and the rest are `WRONGTYPE`.
13952            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        // Redis refuses to copy a module key that has no copy callback, and
13959        // these are its words rather than ours. `DUMP` is the other half of
13960        // D-48: the reference has a payload for one of these and we do not.
13961        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        // A graph is nobody's module and keeps its own sentence.
13970        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        // Everything that does not need a byte shape works on a sketch key the
13980        // way it works on any other.
13981        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    // ------------------------------------------------------------------ topk
13989
13990    /// `TOPK.RESERVE` takes three arguments or six, and looks at the key before
13991    /// it looks at any of them.
13992    #[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        // Four arguments and five are an arity error rather than a defaulted
14001        // depth or decay.
14002        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        // The key is checked first, so a reserve with nothing else right at a
14013        // key that is taken still says the key is taken.
14014        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        // Zero is out and one is in, which is the module's `> 0` and `<= 1`.
14031        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        // Past the cap, with the one sentence in the family that has a prefix.
14040        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    /// What the sketch keeps, and the three ways of asking about it.
14054    #[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        // A null an item while there is room, then the name of whatever was
14059        // pushed out.
14060        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        // Two slots are full and `c` arrives with a count of one, which is not
14066        // under the smallest kept count, so it takes that slot straight away.
14067        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        // The table still counts what the kept set let go of.
14074        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        // Any prefix of the keyword turns the counts on, the empty string
14084        // included, and only a longer word or a different one is refused.
14085        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        // And the keyword is looked at before the key, so a missing key with a
14098        // bad keyword complains about the keyword.
14099        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        // An item counted zero times is kept and not listed.
14108        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    /// `TOPK.INCRBY` applies as it goes, so a bad increment leaves everything
14118    /// before it counted, and the reply counts what it wrote.
14119    #[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        // Three pairs, the middle one bad: two elements come back, one of them
14124        // the error, and the array header says two rather than three. That last
14125        // part is D-51 and it is why a client here stays in step.
14126        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        // A hundred thousand is in and one more is out.
14138        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        // Pairs have to be pairs.
14147        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    /// The RESP3 shapes, which are the two the protocols disagree about.
14155    #[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        // The count stays an integer on both protocols.
14166        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    /// A top k key answers the module sentences the other sketch families
14175    /// answer, and its own word for its type.
14176    #[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        // Every one of the six that is not the constructor says the same thing
14212        // about a key that is not there.
14213        assert_eq!(
14214            f.run(&[b"TOPK.INFO", b"t3"]),
14215            "-TopK: key does not exist\r\n"
14216        );
14217    }
14218
14219    // --------------------------------------------------------------- tdigest
14220
14221    /// `TDIGEST.CREATE` takes two arguments or four, and the keyword search is a
14222    /// search rather than a lookup.
14223    #[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        // A hundred is the default and the capacity is six times it plus ten.
14228        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        // Three arguments is an arity error and not a missing keyword.
14239        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        // The word is looked for across both trailing arguments and the number
14252        // is then read out of the last one whatever was found, so this looks for
14253        // a number inside the word `COMPRESSION` and does not find one.
14254        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        // The reference's own ceiling, which is where the capacity stops fitting
14271        // in an int, and one past it.
14272        assert_eq!(
14273            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
14274            "-ERR T-Digest: allocation failed\r\n"
14275        );
14276        // And ours, which is a gibibyte of centroids and is D-52.
14277        assert_eq!(
14278            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
14279            "-ERR T-Digest: allocation failed\r\n"
14280        );
14281        // The key is checked before the arguments, so a bad compression at a key
14282        // that is already a digest still says the key is taken.
14283        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    /// The four samples every note about this family is written against, and the
14290    /// answers a real 8.10.1 gives for them.
14291    #[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        // The cdf of a sample is the weight below it plus half its own.
14302        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        // Out of order, the walk restarts, and 0.5 answers 3 either way while
14311        // the two after it are read from the front again.
14312        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        // The ranges, which are separate sentences from the parse failures.
14341        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        // Both cuts have their own parse sentence and share the range one, and
14366        // equal cuts are refused rather than answering nothing.
14367        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    /// An empty digest answers every question, and answers most of them with
14386    /// something that is not a number.
14387    #[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        // Minus two, which is a number no rank on a digest with samples in it
14403        // can ever be.
14404        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        // A reset puts a digest with samples back into exactly this state.
14417        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        // Down to the compression count, so a reset digest and a fresh one of
14421        // the same compression report the same nine numbers.
14422        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    /// The double parser is Redis's and not this engine's, and the two disagree
14430    /// at both ends of the range.
14431    #[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        // Overflow and underflow are parse failures rather than an infinity and
14436        // a zero, which is where this parts company with the rest of the engine.
14437        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        // An infinity spelled out parses and is then refused for being one, with
14457        // a different sentence.
14458        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        // These all parse: hex, a bare point either side, and the smallest
14467        // subnormal the reference will take.
14468        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        // Nothing landed from the failures, so six samples is what there is.
14477        assert!(
14478            f.run(&[b"TDIGEST.INFO", b"a"])
14479                .contains("Observations\r\n:6\r\n")
14480        );
14481        // Every value is parsed before any is added, so this whole command is a
14482        // no op.
14483        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    /// What a merge does to its destination, to its inputs and to the buffer
14494    /// split `TDIGEST.INFO` reports.
14495    #[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        // The destination did not exist, so the compression is the largest of
14507        // the inputs. The three from the first input were swept in before the
14508        // three from the second arrived, which is the one visible effect of the
14509        // reference folding one input at a time.
14510        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        // Reading a source sweeps it too, so a merge writes to keys it only
14518        // reads from.
14519        assert!(
14520            f.run(&[b"TDIGEST.INFO", b"m1"])
14521                .contains("Merged nodes\r\n:3\r\n")
14522        );
14523        // Without OVERRIDE the destination joins its own inputs, so this takes
14524        // it to nine observations and keeps its own compression.
14525        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        // With OVERRIDE the old destination is dropped and the compression goes
14530        // back to the largest of the inputs.
14531        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        // And COMPRESSION beats both.
14536        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        // Naming the destination as a source folds it in twice.
14542        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        // The arguments, in the order the reference checks them.
14548        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        // A source that is not there stops the whole thing, and the destination
14569        // is left as it was.
14570        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        // A destination that is not there and is also named as a source is the
14579        // same sentence rather than an empty merge.
14580        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    /// The RESP3 shapes, which are the two the protocols disagree about.
14587    #[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        // The two infinities and the NaN go out as the bare words.
14600        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        // The ranks stay integers on both protocols.
14608        assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14609        // Every question above swept the buffer in, so the four samples are all
14610        // merged by now and the compression count says it happened once.
14611        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    /// A t digest key answers the module sentences the other sketch families
14620    /// answer, and its own word for its type.
14621    #[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        // The merge checks its destination the same way, and its sources too.
14645        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        // An empty digest is still a key, so the twelve that are not the
14669        // constructor all say the same thing once it is gone.
14670        assert_eq!(
14671            f.run(&[b"TDIGEST.INFO", b"t3"]),
14672            "-ERR T-Digest: key does not exist\r\n"
14673        );
14674        // The key is looked at before the arguments, so a bad argument at a key
14675        // that is not there still says the key is not there.
14676        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    // -------------------------------------------------------------------- ts
14683
14684    /// A `TS.INFO` reply with the memory usage taken out of it.
14685    ///
14686    /// That number is what a series costs here rather than what one costs in the
14687    /// module, which is D-53, and it moves whenever the layout of a chunk does.
14688    /// Everything either side of it is the wire contract and is worth pinning
14689    /// down exactly, so the tests below check the whole reply with the one
14690    /// number lifted out.
14691    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    /// A series is made empty and still says it has a chunk, and the options are
14700    /// read before the key is looked at.
14701    #[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        // Fourteen fields, so twenty eight elements. An empty series reports one
14708        // chunk and zero at both ends, and neither the chunk type nor the
14709        // duplicate policy is ever a nil.
14710        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        // A key that is already there is about the key whatever it holds, and
14729        // the existence is what is checked rather than the type.
14730        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        // But the arguments are read first, so a bad one at a key that is there
14740        // answers about the argument.
14741        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        // The seven that will not make a series say WRONGTYPE about a key
14746        // holding something else, where the two that would say a sentence.
14747        // The word is inside the sentence and not in front of it, because the
14748        // module writes its own error text and Redis puts ERR on the front of
14749        // anything a module writes.
14750        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        // And the ones that will not make one say so about a key that is gone.
14761        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    /// Every option word, including the ones that are wrong, and the scan that
14780    /// finds them.
14781    #[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        // A plain double here, where a sample value out of TS.GET is the
14812        // shortest digits that read back as the same number.
14813        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        // A word that is not an option is read past rather than refused.
14823        assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
14824        // LABELS eats everything after it in pairs, and the later scans still
14825        // look inside what it ate, so this sets a retention and stores a label
14826        // called RETENTION at the same time.
14827        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        // Every way an option can be wrong, in the order the module reads them.
14847        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        // A retention below zero is one of the two the module writes with no
14860        // ERR in front of it, where one that is not a number gets one.
14861        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        // And an ENCODING with nothing behind it is an arity error where every
14878        // other keyword in the same spot is a sentence.
14879        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        // An alter changes what was named and leaves the rest alone, and reads
14902        // an encoding only far enough to refuse a bad one.
14903        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        // An encoding it does take is still not applied.
14913        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    /// Samples go in, come back out and are refused for the reasons the module
14924    /// refuses them.
14925    #[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        // The series was made on the way in.
14930        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        // A sample value goes out as a simple string of the shortest digits
14933        // that read back as the same number.
14934        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        // An empty series has no newest sample and answers an empty array
14938        // rather than a nil.
14939        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        // The value is read before the key, so a bad one against a key holding
14943        // a string is about the value.
14944        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        // The grammar is tighter than the one a number argument usually gets:
14950        // no leading plus, no bare fraction, no infinity and nothing that does
14951        // not fit.
14952        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        // And a reading that is not a number is one of three words.
14971        assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
14972
14973        // A timestamp that is not a number, and one that is and is below zero,
14974        // are two different sentences.
14975        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        // A repeated timestamp is blocked by default, and ON_DUPLICATE on the
14985        // command beats what the series was told.
14986        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        // ON_DUPLICATE is only read when the key was already there, which is
14996        // why a policy word that is not a policy passes on a fresh key.
14997        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        // Retention is exact and it is checked before anything else happens, so
15007        // a sample landing behind the window is refused rather than trimmed.
15008        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        // And the window trims as it moves.
15016        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        // An ignore window drops a sample close enough to the newest one to be
15023        // uninteresting, and answers the newest timestamp so a client can tell.
15024        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    /// Every triple in a `TS.MADD` is answered on its own, and none of them
15042    /// makes a series.
15043    #[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        // A key that is not a series is an error in its own slot and the ones
15055        // after it still land.
15056        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        // A bad value and a bad timestamp are answered in their slots too.
15068        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        // And a list that is not made of triples is an arity error.
15073        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    /// The two increments, which only ever write forwards.
15080    #[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        // Two on one timestamp add up rather than collide, because the sample
15092        // goes in under the last policy whatever the series says.
15093        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        // A timestamp behind the newest sample is the other of the two errors
15100        // the module writes with no ERR in front of it.
15101        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        // The increment goes through the ordinary number reader, so it takes
15106        // what a sample value will not and refuses a NaN that a sample value
15107        // takes.
15108        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        // A key holding something else is WRONGTYPE and is answered before the
15125        // number is looked at.
15126        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        // A TIMESTAMP keyword with nothing behind it is about the timestamp.
15132        // The reference reads one past the end of its own arguments here and
15133        // answers whatever was in that memory, so there is nothing to copy and
15134        // this answers the same thing every time.
15135        assert_eq!(
15136            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
15137            "-ERR TSDB: invalid timestamp\r\n"
15138        );
15139        // And one behind a LABELS is a label name rather than the keyword, so
15140        // this lands at the clock rather than at 5.
15141        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        // Adding to a series whose newest value is not a number has no answer.
15146        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    /// Deleting a span, both ends included.
15154    #[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        // Ends the wrong way round take nothing out rather than being an error.
15166        assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
15167        // The two open ends.
15168        assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
15169        // A series everything has been deleted from keeps its chunk and reports
15170        // zero at both ends again.
15171        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        // The two ends have their own sentences.
15178        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    /// What RESP3 changes, which is the two places a number is written and the
15193    /// shape of `TS.INFO`.
15194    #[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        // A double rather than the simple string RESP2 gets.
15204        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    /// Reading a span back, both ways round, with the two ends and the three
15226    /// things that trim what comes out.
15227    #[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        // Both ends are included.
15244        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        // Backwards, and the count takes from the front of what comes out, so
15249        // backwards it takes the newest.
15250        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        // Ends the wrong way round are empty rather than an error.
15255        assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
15256        // The two filters.
15257        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        // A word that is not an option is ignored wherever it sits.
15282        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        // `LATEST` means nothing until there is a compaction rule to follow.
15295        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    /// The bucketing, which is one column a reduction and a flat row.
15302    #[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        // Three reductions is a row of four and not a row of two with a nested
15326        // three in it.
15327        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        // The timestamp a bucket is reported under.
15343        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        // An alignment moves where the bucket edges land.
15358        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        // A `COUNT` sitting where the reduction name belongs is that name, and
15373        // the scan for a real one starts again two words later.
15374        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    /// `EMPTY` fills the gaps between readings and nothing else, and `last`
15403    /// carries two different things depending on which kind of empty it is.
15404    #[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        // Without `EMPTY` the buckets with nothing in them are not there at all,
15416        // and neither is the one holding only a reading that is not a number.
15417        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        // The sum of nothing is zero rather than not a number.
15430        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        // Buckets 200 through 400 have no readings at all and carry the reading
15446        // before the gap either way round. Bucket 500 has a reading that is not
15447        // a number, so it carries whatever the bucket before it in the reading
15448        // direction answered, which is 2 forwards and 3 backwards.
15449        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        // And a window that opens on that bucket has nothing in range before it
15480        // to carry, so it answers not a number.
15481        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    /// The sentences a read answers when its options do not add up, which are
15497    /// the module's own word for word.
15498    #[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            // The key is resolved before a single option is read.
15509            (
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            // The list of names is read before the width is looked at.
15570            (
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        // The one sentence here that is yo's own rather than the module's, which
15693        // is D-54. A read that would build more rows than yo will build is
15694        // refused instead of attempted.
15695        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    /// What RESP3 changes on a read, which is only how a number is written.
15713    #[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    /// Two series with an overlap and a gap each, plus a third holding nothing,
15753    /// which is what the joined reads are measured against.
15754    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    /// The joined read lines its keys up on the timestamp and writes a row as
15777    /// the timestamp and then a nested array of the columns, which is the one
15778    /// shape in the family that is not the flat pair.
15779    #[test]
15780    fn an_nrange_joins_its_keys_on_the_timestamp() {
15781        let mut f = joined();
15782        // One key still nests, so the shape does not depend on the count.
15783        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        // A key with no reading where another key has one writes NaN there.
15789        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        // A series holding nothing is a column of NaN and never a row of its
15799        // own, and the same key twice answers twice.
15800        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        // COUNT is applied to the joined rows and not to each key, so backwards
15809        // it gives the newest joined row rather than the newest of each.
15810        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        // The two sample filters are settled a key at a time, before the join.
15828        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    /// The aggregation on a joined read names one reduction a key and then the
15848    /// one bucket width, and each name may be a comma list, so a row can be
15849    /// wider than the key count.
15850    #[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        // A comma list on the first key widens the row to three columns.
15872        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        // Everything behind the width moves along with it, so BUCKETTIMESTAMP
15891        // sits one or two past the width whatever the key count is.
15892        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        // A COUNT landing in one of the name slots is a reduction name and not
15911        // the keyword, and the read then has no count at all.
15912        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    /// The sentences a joined read answers when it does not add up, which are
15930    /// the module's own and come out in the module's own order.
15931    #[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            // Not enough words behind the count for the keys and both ends of
15943            // the span, which is an arity error however many keys were named.
15944            (
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            // The reduction names are read before the two ends of the span,
15953            // which no other option is.
15954            (
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            // A name slot that is missing or holds a number is the count
15978            // sentence, and a width slot that is itself a reduction name is
15979            // that sentence as well.
15980            (
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            // With one key none of that applies and the plain parser runs, so a
16055            // lone width is a missing width rather than a count mismatch.
16056            (
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            // The keys come last and in the order they were named.
16074            (
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    /// `TS.READ`, which is a key, one timestamp and everything from there on.
16091    #[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        // A plus is the last sample on its own, and a timestamp between two
16100        // samples starts at the one behind it.
16101        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        // Past the end, a series holding nothing and a key that is not there
16110        // are all the empty array rather than an error.
16111        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        // The timestamp refusal goes out with nothing in front of it, and a key
16116        // holding something else answers the bare WRONGTYPE rather than the
16117        // module's prefixed one, both unlike the rest of the family.
16118        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        // Anything other than exactly three words is an arity error, so there
16132        // is nowhere to put an option even though the table says minus three.
16133        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    /// The keys of a joined read sit behind a count, so `COMMAND GETKEYS` has
16144    /// to read the count to find them.
16145    #[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        // A count of zero, or one too large for the words that follow it, is
16174        // the server's own refusal and not the module's.
16175        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    /// The five series every test of the label surface works against.
16184    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    /// The filter grammar, which is four steps and a `strtok` rather than a
16213    /// grammar, and which every command that searches on labels shares.
16214    #[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            // The plain forms, and the order the answer comes back in, which is
16219            // by key name and not by anything the series remembers.
16220            (
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            // An empty list still counts as something that says which series to
16233            // take, it just never takes any.
16234            (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
16235            // Absent and present, neither of which stands on its own.
16236            (
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            // A run of separators is one separator and everything past the
16249            // second field is dropped, so all three of these ask one question.
16250            (
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            // A bracket is only a list when it sits straight behind the
16260            // separator, and then the label in front of it has to be there.
16261            (&[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            // Nothing here says which series to take.
16280            (
16281                &[b"TS.QUERYINDEX", b"room!=kitchen"],
16282                "-ERR TSDB: please provide at least one matcher\r\n",
16283            ),
16284            // Names and values are both compared byte for byte.
16285            (&[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    /// `TS.QUERYLABELS`, whose filter is the one that is allowed to be missing.
16299    #[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            // The series wearing `r` twice contributes the smaller of the two
16316            // here, which is not the one it was written down as first.
16317            (&[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            // With no filter at all every series is taken, which is why the
16336            // first case here answers about `r` as well. A filter that is there
16337            // still has to say which series to take.
16338            (
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    /// `TS.MGET`, the newest sample of every series a filter takes, and the two
16360    /// ways of asking for the labels back alongside it.
16361    #[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            // A series with no samples writes an empty array where the sample
16366            // goes rather than dropping out of the reply.
16367            (
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            // A selected label the series does not wear is a nil, not a gap.
16379            (
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            // The other half of the duplicated name rule. This one takes the
16392            // first written down where `TS.QUERYLABELS` takes the smallest.
16393            (
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            // A word that is not an option is ignored, but a missing `FILTER`
16403            // is an arity error whatever else was written.
16404            (
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            // Both keyword checks happen before the filter is read, and the two
16417            // sentences spell the second keyword without its `ED`.
16418            (
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    /// What RESP3 changes across the label surface, which is a set where there
16441    /// was an array and a map where there was a pair of them.
16442    #[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            // The key stops being the first of three and becomes the map key,
16460            // and the labels stop being pairs and become a map of their own.
16461            (
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            // A map with a name in it twice, which is what a series wearing one
16485            // label name twice turns into.
16486            (
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    /// The same five series with enough samples in them for a group to have
16499    /// something to fold.
16500    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    /// A span read out of every series a filter takes, with and without a group
16509    /// over the top of it.
16510    #[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            // Newest first is applied to each series before anything else sees
16520            // the rows.
16521            (
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            // A label a series does not wear comes back against a nil rather
16536            // than being left out.
16537            (
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            // The fold: 100 is in both series and adds up, the other two are in
16553            // one each and are still rows.
16554            (
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            // RESP2 has nowhere to put the reducer and the member keys, so a
16570            // group wearing labels writes them as two more labels.
16571            (
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            // A count is applied to each member and then again to the fold.
16590            (
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            // Nothing wears the label, so nothing is in any group.
16607            (
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            // The errors, in the order they are looked for.
16635            (
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            // The group is four words from the end here, so the length is what
16658            // is wrong with it.
16659            (
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            // And here it is not, so its words are filters and answer first.
16675            (
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            // The label list ends at a keyword, so this is a `COUNT` with a
16722            // `FILTER` where its number should be.
16723            (
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    /// The multi key reads on RESP3, where the key becomes a map key and the
16743    /// reducer and the member keys become fields of their own.
16744    #[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            // The reductions a read asked for, which RESP2 has no room for at
16755            // all and which is empty on a read that asked for none.
16756            (
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            // The labels hold only the pair the group was made on, because the
16787            // reducer and the sources have somewhere else to go.
16788            (
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    /// `TS.CREATERULE`, whose refusals come in an order of their own.
16828    #[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            // The width is read before the reduction, the reduction before the
16836            // width being above zero, and all three before either key is looked
16837            // at, so a command that is wrong twice complains about the first.
16838            (
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            // A key holding something else answers the same as a key that is not
16895            // there at all, because the source is looked up first and neither of
16896            // them is a series.
16897            (
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            // A keyword other than AGGREGATION is an arity error rather than a
16920            // syntax one, because the arity is all that is checked.
16921            (
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            // The link is now in place, so the same rule again is refused from
16937            // the destination's end.
16938            (
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            // A source that is already someone's destination, and a destination
16950            // that is already someone's source, are two different sentences.
16951            (
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            // The source is looked up and the destination is not, so a missing
16968            // destination is a missing rule and a missing source is a missing
16969            // key, which is the other way round from `TS.CREATERULE`.
16970            (
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    /// What a rule writes, which is every bucket but the one it is filling.
16986    #[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        // The readings written before the rule was made are not folded, so the
16992        // destination is still empty after the first two.
16993        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        // The bucket the rule is filling holds only what it was given, so it is
17005        // 2 rather than 3, and it is written when a reading lands past it.
17006        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        // A reading into a bucket that has already been written works that
17013        // bucket out again over everything the source now holds.
17014        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        // Deleting from the source works the buckets it touched out again and
17020        // reopens the newest one, so `LATEST` starts from the whole bucket.
17021        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        // The link shows on both ends, and dropping either key takes it down.
17031        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    /// The three shapes an `XADD` id can take, and the one rule behind all of
17040    /// them.
17041    #[test]
17042    fn xadd_ids_only_ever_go_up() {
17043        let mut f = Fixture::new();
17044        // A bare millisecond is that millisecond and sequence zero.
17045        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
17046        // And `5-*` is the next free sequence inside it.
17047        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        // The pairs have to be pairs, and Redis calls an odd one an arity error
17065        // rather than a syntax error even though the table has already passed.
17066        assert!(
17067            f.run(&[b"XADD", b"s", b"*", b"a"])
17068                .contains("wrong number of arguments")
17069        );
17070
17071        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
17072        // producer can tell nobody is consuming this yet from the write landed.
17073        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    /// The trim options, which are three keywords that disagree about how many
17083    /// arguments they take.
17084    #[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        // One argument after the keyword and the `~` is read as the threshold,
17096        // which is what a real server does and is the reason this is a number
17097        // complaint and not a syntax one.
17098        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        // The strategy check runs before the approximation check, so a LIMIT
17107        // with neither is told about the missing strategy.
17108        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        // NOMKSTREAM is XADD's and XTRIM does not take it.
17121        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    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
17129    #[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        // The exclusive bound is stepped after the missing sequence is filled
17145        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
17146        // `6-1` is still in the range.
17147        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        // The two kinds of nothing. A key that is not there is an empty array
17162        // and a key that is there with a count of zero is a null array, because
17163        // the lookup happens first.
17164        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        // The count is read in a loop, so the last one wins.
17178        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    /// `XDEL` and `XACK` check every id before they touch any of them.
17185    #[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    /// `XGROUP`, and the two different complaints it makes about arguments.
17202    #[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        // A second CREATE is BUSYGROUP and not an ordinary error, because a
17214        // client racing another one to make a group branches on the prefix.
17215        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        // Below the subcommand's own arity is an arity error naming the pair.
17233        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        // At or above it in a shape the handler will not take is the other one.
17239        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    /// A group read, an acknowledgement, and what is left in between.
17263    #[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        // A history read names its stream even with nothing to show, which is
17286        // the difference between it and a `>` read that found nothing.
17287        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        // Empty is four nulls and not a zero with three empty things.
17303        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        // A history read of an entry that has since been deleted is the id with
17309        // a null beside it, so the consumer can still acknowledge it.
17310        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        // The group lookup runs before the id parse, so a `+` at a stream with
17318        // no such group is told about the group and not about the id.
17319        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    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
17355    /// answer.
17356    #[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        // Nothing new is a null array and not an empty one, and a stream with
17365        // nothing new is left out rather than sent with an empty list.
17366        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        // `$` is the last id, so nothing that is already there comes back.
17374        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17375        // And `+` is the last entry, whatever COUNT says.
17376        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        // A count of zero means unlimited here, which is the opposite of what it
17381        // means to XRANGE.
17382        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        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
17387        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    /// A blocked reader, and the two ways it stops being blocked.
17402    #[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        // Everybody parked on the stream gets the entry, because a read takes
17411        // nothing away. That is the difference between this and BLPOP.
17412        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        // And a deadline that runs out is a null array, the same as a plain
17425        // XREAD that found nothing.
17426        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    /// A blocked group reader whose group is destroyed under it.
17440    #[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        // The ordinary sentence and not a special one about having been parked,
17462        // which is what a running 8.10 sends.
17463        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    /// `XCLAIM`, whose argument shape is the odd one in the group.
17470    #[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        // Everything after the first argument that is not an id is an option, so
17479        // a `-` is an unrecognised option and not a bad id.
17480        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        // An id that is pending but whose entry has gone is an empty answer, and
17489        // it leaves the pending list on the way past.
17490        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    /// `XAUTOCLAIM`, and the third value nobody expects.
17510    #[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        // The cursor, what was claimed, and what was dropped for no longer being
17520        // in the stream. The third one is what makes a sweep converge.
17521        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    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
17536    #[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        // One means gone and minus one means it was not there to start with.
17556        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        // `KEEPREF` leaves the pending entry behind, so the group still counts
17561        // the one it was handed even though the entry has gone.
17562        assert!(
17563            f.run(&[b"XPENDING", b"s", b"g"])
17564                .starts_with("*4\r\n:2\r\n")
17565        );
17566        // `DELREF` takes it out of every pending list on the way past.
17567        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        // `1-1` is still in the list, because the delete before it said KEEPREF.
17572        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        // Two means somebody still wants it, and the question is wider than the
17578        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
17579        // refused even though no consumer has ever been handed it.
17580        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        // A key that is not there answers minus ones without reading the IDs.
17586        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        // A key that is there validates every ID before deleting any of them.
17591        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        // The condition is one word, so a second one is a syntax error, and so
17606        // is one ID more than the count promised.
17607        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        // The key is looked up first, so the wrong type beats the syntax.
17616        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    /// `XACKDEL`, whose reply is about the pending list and not about the log.
17624    #[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        // Minus one is not about the stream: `2-1` is sitting there unread and
17644        // still answers minus one, because the group was not holding it. It also
17645        // stays, since only an ID that was acknowledged is deleted.
17646        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        // A missing group is minus one an ID and not a NOGROUP.
17653        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        // The acknowledgement happens whatever the condition says, so an ACKED
17663        // that answers two has still emptied the pending list.
17664        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    /// `XNACK`, which hands an entry back to nobody.
17678    #[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        // Twice, so the delivery count is two and the words have something to
17686        // do with it.
17687        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        // No owner, no idle time, and the count left where it was. A released
17694        // entry reads as idle for longer than any min-idle-time, which is what
17695        // puts it at the front of the next claim.
17696        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        // The consumer no longer holds it, so a filtered XPENDING skips it.
17701        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        // The bookmark did not move, so a `>` read will not hand it out again.
17706        assert_eq!(
17707            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17708            "*-1\r\n"
17709        );
17710        // A claim at any min-idle-time takes it.
17711        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        // `SILENT` takes one off the count rather than putting it back to zero,
17725        // which only shows on an entry that has been handed out more than once.
17726        // It was delivered and then claimed, so it is on two and goes to one.
17727        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        // And it stops at zero rather than wrapping.
17733        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        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
17740        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        // Releasing something the group is not holding is zero, and `FORCE`
17762        // makes the pending entry rather than answering zero. A forced entry
17763        // starts at zero, since there was no earlier count to keep.
17764        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        // `FORCE` on an ID the stream does not have is still zero.
17780        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        // The group is looked up before the mode word, and it raises rather
17788        // than answering per ID the way the two delete commands do.
17789        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        // Its own sentences, which are not the ones XDELEX uses.
17798        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        // Everything past the counted IDs is an option, so one too many is an
17807        // option nobody recognises and not a count that does not add up.
17808        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    /// `XINFO`, which is where the shape of the storage shows through.
17815    #[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        // Ten pairs, since the six idempotency fields have nothing behind them
17835        // here and a zero would claim they had. That is D-27.
17836        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        // A consumer that has never been given anything reports minus one for
17852        // inactive rather than the moment it turned up, which is what tells a
17853        // worker that is stuck from one that has nothing to do.
17854        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        // And in name order, which the storage does not hold them in.
17862        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    /// `XPENDING`'s long form, which reads its arguments by counting them.
17892    #[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        // A consumer nobody has heard of holds nothing rather than erroring.
17906        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        // IDLE is only read at position three.
17915        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    /// `XSETID`, which is three counters and two refusals.
17934    #[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    /// RESP3, where the two reads answer a map and the entries stay an array.
17973    #[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        // A map header and then the key and the entries side by side, with no
17979        // two element array wrapping the pair.
17980        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        // The fields are still one flat array and not a map, which is Redis's
17985        // shape and is what every consumer written before RESP3 expects.
17986        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    /// A store to migrate values into, so a test can watch the inversion.
17994    ///
17995    /// A vector rather than a file for the same reason the tier's own tests use
17996    /// one: the file work has not attached a real store yet, and what this is
17997    /// checking is the policy above the store rather than the store.
17998    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    /// A server holding several segments of strings, with somewhere to put them.
18026    ///
18027    /// Answers the fixture and what it was holding when it stopped filling.
18028    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    /// Write until the server is under `limit` or the writes run out.
18047    ///
18048    /// The same shape the eviction test uses. A memory limit is enforced in
18049    /// front of a command, so nothing happens until something is written, and
18050    /// the budget means one command does not do the whole job.
18051    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        // The same memory value parser `maxmemory` uses, and the same trap in
18080        // it, plus the one spelling that means no limit at all.
18081        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        // Nothing is attached, so the answer to a memory limit is still Redis's.
18105        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        // The inversion. The same pressure that makes a Redis server throw keys
18114        // away makes this one move values to the file, and afterwards every key
18115        // is still there and still answers with what was stored in it.
18116        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        // And the values read back, which is the part that makes it a migration
18149        // rather than a loss.
18150        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        // The documented setting for a drop in cache. A file that may hold
18158        // nothing cannot be migrated to, so eviction is all that is left, and
18159        // the server behaves exactly as it did before any of this existed.
18160        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        // A storage limit reached is a storage limit, and eviction is the right
18188        // answer to one. The budget here is a few kilobytes, so the first round
18189        // of migration fills it and everything after that is evicted.
18190        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    // ------------------------------------------------------------- stripes
18214
18215    /// Every string command, run twice: once on a database that is one keyspace
18216    /// and once on a database that is eight, with the same commands in the same
18217    /// order and the replies compared byte for byte.
18218    ///
18219    /// This is the whole claim the striping rests on. A key belongs to one
18220    /// stripe and to no other, so the answer to a command cannot depend on how
18221    /// many stripes there are, and the way to check that is to ask the same
18222    /// question of two servers that differ in nothing else.
18223    ///
18224    /// The keys are chosen to land on different stripes rather than to look
18225    /// tidy. `MSET a 1 b 2 c 3` over eight stripes is only a test of anything if
18226    /// those three keys are not all on the same one, and at eight stripes three
18227    /// keys land together about one time in fifty.
18228    #[test]
18229    fn the_string_group_answers_the_same_however_many_stripes_there_are() {
18230        let script: &[&[&[u8]]] = &[
18231            // The single key commands, which are the ones that get handed one
18232            // stripe at the dispatch site.
18233            &[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            // The five that name more than one key, which are the ones that
18256            // cannot be handed one stripe at all.
18257            &[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            // And the errors, which have to be the same errors.
18274            &[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    /// The keys of an `MSET` really do end up on different stripes.
18289    ///
18290    /// Without this the test above could pass on a server whose stripe number
18291    /// happened to be a constant, which is a striped database in name only.
18292    #[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    /// A wrong type stops an `MGET` no more than it does on one stripe: the key
18303    /// that is not a string comes back nil and the rest of the reply is intact.
18304    #[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            // Planted rather than pushed. `RPUSH` belongs to the list group,
18311            // which has not been taught about stripes yet and would refuse the
18312            // wide server. What is under test is what `MGET` does when it walks
18313            // onto a key that is not a string, and that does not care how the
18314            // key got there.
18315            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    /// The same claim for the keyspace group, and the same way of checking it.
18328    ///
18329    /// `SORT` is not in the script because it is the one command in that file
18330    /// that has not been taught about stripes, and `SCAN`, `KEYS` and
18331    /// `RANDOMKEY` are not in it either, because those three do not promise an
18332    /// order and comparing two replies byte for byte would be asserting one.
18333    /// They get tests of their own below.
18334    #[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        // `RESTORE` needs bytes a client would have got from a `DUMP`, so the
18388        // payload is taken from the store rather than parsed back out of a
18389        // reply that is not text. Both servers dump the same key and the bytes
18390        // are the same bytes, which is the first half of what is being checked
18391        // here.
18392        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    /// A `SCAN` of a database of eight stripes comes back with all of it.
18420    ///
18421    /// The cursor is the thing under test. It has to carry the stripe as well
18422    /// as the place in it, so a client that stops at one stripe and comes back
18423    /// carries on in that stripe and not at the top of the database, and the
18424    /// walk has to end once rather than eight times.
18425    #[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        // And the options still work when the walk is over several stripes,
18452        // since a `MATCH` is applied to keys a stripe handed up and a `TYPE` is
18453        // applied by each stripe on the way.
18454        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    /// `RANDOMKEY` on a striped database answers a key from any of the stripes.
18463    ///
18464    /// The draw picks the stripe first, so the thing that can go wrong is that
18465    /// it always picks the same one, and two hundred draws over eight stripes
18466    /// would make that obvious.
18467    #[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    /// Two keys that are not on the same stripe, which is what `RENAME` and
18486    /// `COPY` have to cope with and what a test has to arrange rather than
18487    /// hope for.
18488    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    /// A rename whose two keys are on two stripes moves the value, the deadline
18500    /// and, for a collection, the body itself.
18501    #[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        // A list, because a string lives in its record and a collection lives
18515        // in a slab, and the second of those is the one that can be left
18516        // behind. Planted through the store, since the list group has not been
18517        // taught about stripes yet.
18518        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        // And `RENAMENX` still refuses a destination that is taken, which is
18532        // the one answer the cross stripe path has to work out for itself.
18533        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    /// And a copy across two stripes leaves both keys behind it.
18540    #[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        // A collection is cloned rather than moved, so both keys have a body of
18559        // their own afterwards and writing to one does not show up in the
18560        // other.
18561        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    /// Every bitmap command, on one stripe and on eight, replies compared byte
18578    /// for byte.
18579    ///
18580    /// `BITOP` is the one that names more than one key and it is where the work
18581    /// went. The rest are single key commands that now find their own stripe,
18582    /// and they are here because the cheapest way to be sure the routing is
18583    /// right is to ask.
18584    #[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            // The multi key one, over sources that are not on one stripe unless
18621            // eight stripes have folded into one.
18622            &[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            // A source that is not there reads as empty, and a result with
18638            // nothing in it deletes the destination rather than writing one.
18639            &[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            // And the errors, which have to be the same errors. The key that
18644            // is not a string is planted below rather than pushed here, since
18645            // the list group has not been taught about stripes yet.
18646            &[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    /// A list under `key`, put there through the store.
18668    ///
18669    /// What a test does when it wants a key of the wrong type on a striped
18670    /// server, because the command that would make one is in a group that has
18671    /// not been taught about stripes yet.
18672    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    /// A `BITOP` whose keys are on two stripes reads both of them.
18681    ///
18682    /// The test above spreads its keys by hashing and would still pass if one
18683    /// stripe were doing all the work, since the answers would be the same. This
18684    /// one puts the destination and the two sources where they are known not to
18685    /// share a stripe.
18686    #[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        // A result of nothing deletes a destination on whatever stripe it is
18714        // on, and a source of the wrong type is refused before anything is
18715        // written.
18716        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    /// Every HyperLogLog command, on one stripe and on eight.
18731    #[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            // The two that name more than one key.
18742            &[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            // The debugging ones, which are single key and change what they
18752            // look at.
18753            &[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            // And the errors.
18761            &[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    /// Every set command, on one stripe and on eight.
18781    ///
18782    /// The commands that answer members answer them in whatever order the set
18783    /// or the table they were built in holds them, so those replies are
18784    /// compared as sets. Everything else is compared byte for byte. Two servers
18785    /// agreeing on the order would be a fact about the tables and not about the
18786    /// answer, and asserting it would make this test fail for a reason nobody
18787    /// cares about.
18788    #[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            // The two draws, on a set of one member, which is the only shape
18806            // whose answer two servers have to agree on.
18807            &[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            // The one that names two keys.
18815            &[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            // The algebra.
18821            &[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            // An empty result deletes the destination rather than storing a
18839            // set with nothing in it.
18840            &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
18841            &[b"EXISTS", b"d4"],
18842            // And a destination that is also a source.
18843            &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
18844            &[b"SCARD", b"s2"],
18845            // The errors, which have to be the same errors.
18846            &[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    /// The algebra over sets that are known to be on different stripes.
18875    #[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        // A destination on a third stripe, and then one that is also a source.
18896        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        // An empty result deletes a destination wherever it is, and a key of
18904        // the wrong type stops the command before the destination is touched.
18905        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    /// An `SMOVE` whose two keys are on two stripes.
18916    #[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        // A destination that is not there is created on its own stripe, and a
18930        // source that loses its last member is deleted from its own.
18931        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        // And a source that is not there answers zero without ever asking what
18937        // the destination holds, which is Redis's order and not the obvious
18938        // one.
18939        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    /// A count and a merge over sketches that are known to be on two stripes.
18949    #[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        // The three numbers a real server gives for these elements, which are
18964        // the numbers the single stripe tests in the keyspace crate check too.
18965        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        // A merge whose destination is on a third stripe, and then one that
18970        // writes into a source.
18971        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    /// Every sorted set command, on one stripe and on eight.
18980    ///
18981    /// Every reply here is compared byte for byte, unlike the set group, because
18982    /// a sorted set answers in rank order and members sharing a score come out
18983    /// in the order of their bytes. There is nothing left for the table the
18984    /// answer was built in to decide.
18985    #[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            // The range commands, which are one parse and one walk.
19010            &[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            // The draw, on a sorted set of one member, which is the only shape
19029            // whose answer two servers have to agree on.
19030            &[b"ZRANDMEMBER", b"one"],
19031            &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
19032            &[b"ZRANDMEMBER", b"gone"],
19033            // The one that copies a window into another key.
19034            &[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            // The algebra, in both its shapes.
19039            &[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            // An empty result deletes the destination rather than storing a
19073            // sorted set with nothing in it.
19074            &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
19075            &[b"EXISTS", b"d4"],
19076            // A plain set is a sorted set where every score is one, so it is a
19077            // legal input to all of these.
19078            &[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            // And a destination that is also a source.
19082            &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
19083            &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
19084            // The three removals and the two pops.
19085            &[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            // The errors, which have to be the same errors.
19095            &[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    /// The algebra over sorted sets that are known to be on different stripes.
19119    #[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        // a is 1, c is 4, b is 2 and 3 added together, which is the order they
19129        // come out in and the answer that says both stripes were read.
19130        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        // A destination on a third stripe, and the weights and the aggregate
19143        // reaching every input.
19144        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        // A pop over keys on several stripes takes from the first one that has
19168        // anything, which is what makes the order of the keys matter.
19169        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        // An empty result deletes a destination wherever it is, and an input of
19177        // the wrong type stops the command before the destination is touched.
19178        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        // And a destination that is also a source works across stripes for the
19188        // reason it works on one: the whole result is built before anything is
19189        // written.
19190        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    /// A `ZRANGESTORE` whose two keys are on two stripes.
19196    #[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        // A window walked backwards takes the other end of the sorted set and
19212        // still stores what it took in score order.
19213        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        // An empty window deletes the destination on its own stripe, and a
19234        // source of the wrong type is refused before the destination is touched.
19235        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    /// Every list command, on one stripe and on eight.
19251    ///
19252    /// The blocking six are in here too, both when they can be answered on the
19253    /// spot and when they cannot, since a command that parks its client writes
19254    /// nothing at all and two servers have to agree about that as much as they
19255    /// agree about a reply.
19256    #[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            // The ones that name two keys, and the one that takes a block of
19289            // elements rather than the one on the end.
19290            &[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            // The blocking ones, first with something there to answer them and
19310            // then with nothing, which parks the client and writes nothing.
19311            &[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            // The errors, which have to be the same errors.
19322            &[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    /// An `LMOVE` and an `LMOVEM` whose two keys are on two stripes.
19345    #[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        // A block of them, which under BULK arrives in the order it left.
19369        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        // An `EXACTLY` the source cannot fill moves nothing, and a source that
19386        // is not there at all is the two kinds of nothing the two commands have.
19387        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        // A destination of the wrong type is refused before anything is taken,
19405        // which is the order that matters most here, since an element already
19406        // out of the source would have nowhere to go back to.
19407        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    /// A parked client served by a push that landed on another stripe.
19425    ///
19426    /// A waiter remembers the database and not the stripe, which is the point:
19427    /// serving it runs the same attempt the command ran, and the attempt finds
19428    /// the stripe each of its keys is on for itself.
19429    #[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        // And a move across two stripes is served the same way, by the push
19449        // that fills its source.
19450        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    /// Every stream command, on one stripe and on eight.
19466    ///
19467    /// Every ID is written out rather than left to the clock, so the two servers
19468    /// are being compared on what they store and not on how long the test took
19469    /// to get from one of them to the other.
19470    #[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            // The groups, which is where most of the state is.
19489            &[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            // Deleting, trimming and moving the ID on.
19531            &[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            // And the errors.
19545            &[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    /// An `XREAD` and an `XREADGROUP` naming two keys on two stripes.
19565    ///
19566    /// Nothing is shared between the two streams, so the only thing this can go
19567    /// wrong at is looking both of them up, which is exactly what a read that
19568    /// held one database and walked it would get wrong.
19569    #[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        // A group read looks its group up on every key before it reads any of
19583        // them, so a group that is missing on the far key stops the near one.
19584        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    /// A client parked on an `XREAD` woken by an entry on another stripe.
19619    #[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    /// Every JSON command, on one stripe and on eight.
19643    #[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            // The two that name more than one key.
19682            &[
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            // And the errors.
19700            &[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    /// A `JSON.MSET` and a `JSON.MGET` whose keys are on several stripes.
19718    ///
19719    /// `JSON.MSET` works every triple out against the keyspace as it was before
19720    /// the command and writes nothing until all of them are known to work, so
19721    /// the thing to check is that a triple that cannot be written stops the
19722    /// ones on other stripes as well as the ones on its own.
19723    #[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        // A value that is not JSON is refused before anything is written, and
19740        // the key on the far stripe keeps what it had.
19741        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        // A path that names nowhere is not an error. That triple is skipped,
19748        // the ones on the other stripes are still written, and the reply is a
19749        // nil rather than OK.
19750        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    /// Every geospatial command, on one stripe and on eight.
19771    #[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            // And the errors.
19872            &[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    /// A `GEOSEARCHSTORE` whose two keys are on two stripes.
19900    #[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        // `STOREDIST` stores the distance in the unit the search was asked in,
19940        // which is the destination stripe's sorted set and not the source's.
19941        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        // A search that found nothing deletes the destination on its own
19962        // stripe, and a source of the wrong type is refused with the
19963        // destination left alone.
19964        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    /// Every time series command, on one stripe and on eight.
20013    ///
20014    /// Every timestamp is written out rather than left to the clock, so the two
20015    /// servers are compared on the samples they hold and not on how long the
20016    /// test took to get from one of them to the other.
20017    #[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            // The filters, which are the ones that have to walk every stripe.
20060            &[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            // And a rule, which is the one thing here that names two keys.
20101            &[
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            // And the errors.
20129            &[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    /// A compaction rule whose two ends are on two stripes.
20148    ///
20149    /// This is the one thing in the family that walks from a key to another key,
20150    /// and it walks it in both directions: a sample on the source closes a
20151    /// bucket on the destination, a `LATEST` read on the destination folds the
20152    /// bucket the source is still filling, and a delete on the source rewrites
20153    /// what the destination already held. The same script is run against a
20154    /// server one stripe wide, where the two keys share a store, and against one
20155    /// eight stripes wide, where they do not.
20156    #[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        // The bucket the source is filling is not written down yet, and asking
20178        // for it works it out off the source.
20179        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        // A sample past the bucket closes it, which is the write that has to
20184        // land on the other stripe.
20185        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        // And a delete on the source takes it away again.
20191        both(&[b"TS.DEL", src, b"1000", b"1999"]);
20192        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20193
20194        // Both ends still know about each other, and the link comes apart from
20195        // the source.
20196        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    /// A label filter takes the series it names wherever they landed.
20208    #[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    /// Every hash command, and the field import beside it, on one stripe and on
20243    /// eight.
20244    ///
20245    /// `HRANDFIELD` with a count draws from the stripe's own generator and two
20246    /// stripes do not draw the same numbers, so the only draw here is off a hash
20247    /// holding one field, where every generator gives the same answer.
20248    #[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            // The field deadlines.
20274            &[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            // The three that came later and word everything their own way.
20283            &[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            // And the import, whose key is the third word.
20288            &[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            // And the errors.
20294            &[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        // The field deadlines are absolute milliseconds worked out from the
20304        // clock, so both servers are put on the same one rather than left to
20305        // read the wall a moment apart.
20306        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    /// Every array command, on one stripe and on eight.
20316    #[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            // And the errors.
20346            &[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    /// Every graph and vector set command, on one stripe and on eight.
20363    ///
20364    /// `VRANDMEMBER` is not in here for the reason `HRANDFIELD` with a count is
20365    /// not: it draws from the stripe's generator, and the stripes do not share
20366    /// one.
20367    #[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            // The vector set, which is one index under one key.
20387            &[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            // And the errors.
20404            &[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    /// Every bloom filter, cuckoo filter, count min sketch, top k and t digest
20421    /// command, on one stripe and on eight.
20422    #[test]
20423    fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20424        let script: &[&[&[u8]]] = &[
20425            // The bloom filter.
20426            &[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            // The cuckoo filter.
20440            &[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            // The count min sketch.
20453            &[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            // The top k sketch.
20459            &[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            // The t digest.
20468            &[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            // And the errors.
20483            &[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    /// The two sketch merges, with their sources on stripes of their own.
20504    ///
20505    /// These are the only two commands in the ten groups that name more than one
20506    /// key, and both read a run of sources and write a destination, so both go
20507    /// wrong in the same way if a merge holds one store and looks every source up
20508    /// in it.
20509    #[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        // The count min sketch. The destination has to be the sources' shape,
20523        // and it is named first, so all three keys are read before anything is
20524        // written.
20525        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        // And with weights, which are read against the sources in order.
20537        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        // A source that is not a sketch is answered before anything is written.
20540        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        // The t digest, which builds its destination and then puts it in place.
20545        // The two source keys are used again here, so what they held goes first.
20546        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    /// Every shape of `SORT`, on one stripe and on eight.
20558    ///
20559    /// The key it sorts, the keys a `BY` names, the keys a `GET` names and the
20560    /// destination are four different names and nothing lines them up, so on
20561    /// eight stripes this script is reading and writing all over the database
20562    /// while on one it is doing what it always did.
20563    #[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            // A weight per element, so the order comes off keys the command
20573            // never named.
20574            &[
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            // And the answer off another set of keys again, with `#` mixed in
20582            // so the rows are not all lookups.
20583            &[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            // A pattern that reaches into a hash, which is another key again.
20586            &[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            // The destination, which is a fourth place to land.
20593            &[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            // An empty result takes the destination away rather than leaving a
20598            // list of nothing behind.
20599            &[b"SORT", b"missing", b"STORE", b"out"],
20600            &[b"EXISTS", b"out"],
20601            // A set and a sorted set sort the same way a list does, and a set
20602            // written to a destination is sorted even when nothing asked.
20603            &[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            // And the two ways it refuses: a key of the wrong type, and an
20611            // element that is not a number under a numeric sort.
20612            &[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    /// One `SORT` whose four kinds of key are on stripes of their own.
20629    ///
20630    /// The script above spreads keys around by writing enough of them, and this
20631    /// one checks the spread rather than trusting it: the list, the weight key
20632    /// for one of its elements and the destination are asserted to be in three
20633    /// places before the command runs.
20634    #[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        // The weights are four keys and they are not all in one place, which is
20649        // the thing that would go unnoticed if the command held a stripe.
20650        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    /// A `CONFIG SET` reaches every stripe, so where a key landed does not
20677    /// decide what shape it is stored in.
20678    ///
20679    /// This is the setting that would go wrong quietly. A stripe that kept the
20680    /// old ladder would hold the same hash in a different encoding from the
20681    /// stripe next to it, and the only thing that would ever say so is
20682    /// `OBJECT ENCODING`, which is why the check is on that.
20683    #[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        // And the policy, which every stripe has to agree about for the same
20714        // reason: an eviction draws from one stripe at a time.
20715        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}