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 acl;
55mod args;
56mod arrays;
57mod auth;
58mod backup;
59mod bits;
60mod blocking;
61mod bloom;
62mod client;
63mod clients;
64mod cluster;
65mod cms;
66mod cpu;
67mod cuckoo;
68mod debug;
69mod failover;
70mod follow;
71mod geo;
72mod graph;
73mod hashes;
74mod himport;
75mod hll;
76mod indexing;
77mod json;
78mod keyspace;
79pub mod keyspec;
80mod lists;
81mod load;
82mod lua;
83mod memory;
84mod migrate;
85mod misses;
86mod monitor;
87mod multi;
88mod notify;
89mod persist;
90mod pubsub;
91mod repl;
92mod scan;
93mod scripting;
94mod search;
95mod server;
96mod sets;
97mod streams;
98mod strings;
99mod suggest;
100pub mod table;
101mod tdigest;
102mod topk;
103mod ts;
104mod vectors;
105mod vfilter;
106mod zsets;
107
108pub use args::Args;
109pub use blocking::{Parked, Waiters};
110pub use clients::Client;
111pub use load::{Loaded, Refused};
112pub(crate) use pubsub::Envelope;
113pub use server::parse_memory;
114pub use table::{COMMANDS, Spec, arity_ok, lookup};
115
116use crate::reply::Out;
117use std::cell::Cell;
118use std::path::{Path, PathBuf};
119use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
120use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicUsize};
121use std::sync::{Arc, Weak};
122use yo_common::lock::{Held, Lock};
123use yo_common::{Code, Error};
124use yo_kv::cold::Store;
125use yo_kv::lookups;
126use yo_kv::{Clock, Db, Keyspace};
127use yo_search::Registry;
128
129use multi::Watches;
130use search::cursor::Cursors;
131
132/// How many databases a server has.
133///
134/// Redis's default is sixteen and its `databases` setting can change it. Ours
135/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
136/// constant. Nothing in the design needs the number to be fixed; nothing yet
137/// needs it not to be.
138pub const DATABASES: usize = 16;
139
140/// Every database's bit in [`Server::dirty`], which is what a fresh server
141/// starts on so that the first maintenance turn asks all of them.
142///
143/// A `u64` holds sixteen bits with room to spare, and the assertion below is
144/// what turns raising [`DATABASES`] past sixty four into a build failure rather
145/// than a shift that silently drops the databases past the end.
146const ALL_DATABASES: u64 = if DATABASES == 64 {
147    u64::MAX
148} else {
149    (1u64 << DATABASES) - 1
150};
151const _: () = assert!(DATABASES <= 64);
152
153/// How many keys one command throws away before it leaves the rest to the next.
154///
155/// A bound and not a loop to the end, because this runs in front of a client
156/// that is waiting for its reply, and a server a long way over its limit would
157/// otherwise hold that client for as long as it took to walk all the way back
158/// under. Sixty four is a batch's worth of commands, so a server that went over
159/// by what one batch allocated comes back under in one command, and a server
160/// whose limit was just cut in half works through it over the next few thousand
161/// rather than in one long stall. Redis bounds the same loop by a time slice
162/// instead of a count and hands the rest to a timer; there is no timer here, so
163/// the rest goes to the next command that runs.
164const EVICT_BUDGET: usize = 64;
165
166/// How many stripes one compaction turn looks at before it leaves the rest to
167/// the next turn.
168///
169/// The walk used to run from its cursor to the end of [`Server::slots`], which
170/// is [`DATABASES`] times the stripe width, and the width is derived from the
171/// thread count. It does not stop early on a server with nothing to collect,
172/// since nothing to collect is exactly the answer that does not end the walk, so
173/// an idle stripe still cost a lock taken and given back. Every worker paid that
174/// on every batch and the locks it took were the same stripe locks the commands
175/// wanted, which put the thread count into the price every thread pays. Measured
176/// on a ten core laptop at pipeline 50, one thread ran at 6417 Kops and four ran
177/// at 3283, and turning this walk off with `DEBUG DICT-RESIZING 0` took four
178/// threads to 4518.
179///
180/// Eight stripes is a bound with no thread count in it. The cursor moves on
181/// every turn rather than only when something was found, so a walk that stops
182/// after eight still comes round to the far end of the databases, and it comes
183/// round after the same number of turns however many threads are turning.
184///
185/// The active expiry walk is not bounded the same way. It is already gated to
186/// once a millisecond for the whole server rather than running once a batch per
187/// worker, and a bound there would mean a key with a deadline waiting several
188/// sweeps to be noticed rather than one.
189const COMPACT_LOOKS: usize = 8;
190
191/// The `maxstore` a server with no storage limit carries.
192///
193/// Sixteen exabytes, which is every disk there is and then some, so a server
194/// that set a limit this high and a server that set none behave the same way and
195/// the only difference is what `CONFIG GET maxstore` says. Zero cannot be the
196/// sentinel because zero is a limit with a meaning: nothing may live on the
197/// file.
198const NO_MAXSTORE: u64 = u64::MAX;
199
200/// What a server says to a command that would allocate when it has no room.
201///
202/// Redis's `shared.oomerr`, word for word including the full stop, because
203/// clients match on the `OOM` prefix and people match on the sentence.
204const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
205
206/// What the connection should do after a command.
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208pub enum Flow {
209    /// Read the next command.
210    Continue,
211    /// Write what is buffered and then close, which is what `QUIT` asks for.
212    Close,
213    /// Nothing was written and nothing is owed yet.
214    ///
215    /// The client is on the waiter list and its reply comes when a key it named
216    /// has something in it or when its deadline passes, whichever happens first.
217    /// Until then the connection stops reading commands, because a client that
218    /// is waiting for an answer is not a client that has sent another question.
219    Block,
220    /// Nothing was written and the command has not run at all.
221    ///
222    /// The server is paused, so the connection keeps the command it was about to
223    /// run and runs it again once the pause is over. Everything the client
224    /// pipelined behind it is kept in the order it arrived, the same way a
225    /// blocking command keeps it.
226    Hold,
227}
228
229/// A number one thread adds to and any thread may read.
230///
231/// The add is a load, an add and a store rather than a fetch and add, which on
232/// x86 is three ordinary instructions instead of one locked one. That is sound
233/// because every counter here has exactly one writer, which is what the slots
234/// below are for: two threads never hold the same counter, so nothing can be
235/// lost between the load and the store. A reader can be a command or two behind,
236/// and `INFO` on a running server is behind by the time the reply reaches the
237/// client anyway.
238#[derive(Debug, Default)]
239pub struct Counter(AtomicU64);
240
241impl Counter {
242    /// One more.
243    fn bump(&self) {
244        self.0.store(self.get().wrapping_add(1), Relaxed);
245    }
246
247    /// One fewer, stopping at zero.
248    ///
249    /// The floor is for the gauge, which is the number of open connections: a
250    /// close that arrives without its open, which nothing can do now and a
251    /// misplaced call could, is a number that stays at zero rather than one
252    /// that wraps to eighteen quintillion clients.
253    fn drop_one(&self) {
254        self.0.store(self.get().saturating_sub(1), Relaxed);
255    }
256
257    /// What it says.
258    fn get(&self) -> u64 {
259        self.0.load(Relaxed)
260    }
261
262    /// Back to zero, which is `CONFIG RESETSTAT`.
263    fn zero(&self) {
264        self.0.store(0, Relaxed);
265    }
266}
267
268/// The numbers `INFO` reports that this layer cannot see for itself.
269///
270/// The reactor owns the sockets, so the reactor is what knows how many clients
271/// there are. It counts them here and nothing else does anything with them
272/// except report them.
273#[derive(Debug, Default)]
274pub struct Stats {
275    /// Connections open right now.
276    clients: Counter,
277    /// Connections accepted since the server started.
278    connections: Counter,
279    /// Commands run since the server started, which this layer counts itself.
280    commands: Counter,
281}
282
283impl Stats {
284    /// A connection arrived.
285    pub fn opened(&self) {
286        self.clients.bump();
287        self.connections.bump();
288    }
289
290    /// A connection went away.
291    pub fn closed(&self) {
292        self.clients.drop_one();
293    }
294}
295
296/// Every thread's [`Stats`] added together, which is what `INFO` answers.
297#[derive(Debug, Clone, Copy, Default)]
298pub struct Totals {
299    /// Connections open right now.
300    pub clients: u64,
301    /// Connections accepted since the server started.
302    pub connections: u64,
303    /// Commands run since the server started.
304    pub commands: u64,
305}
306
307thread_local! {
308    /// Which set of counters the running thread writes into.
309    ///
310    /// Claimed the first time a thread counts anything and kept for as long as
311    /// the thread runs. It is a number rather than a pointer, so a thread that
312    /// has counted on one server and then counts on another lands in the same
313    /// place in both, and a process with two servers in it shares the numbering
314    /// between them. That is the tests and it is not `yodb`, which has one.
315    static SLOT: Cell<usize> = const { Cell::new(usize::MAX) };
316}
317
318/// What one thread keeps to itself.
319///
320/// One of these per thread and not one per server, because a number every
321/// thread writes to is a cache line every thread has to own to write to it, and
322/// at a few million commands a second that one line is the server. So each
323/// thread writes into its own and whoever needs the whole picture, which is
324/// `INFO` and the maintenance turn, puts the pieces together when it asks.
325///
326/// A cache line apart for the same reason, so that two threads writing at once
327/// are not two threads passing one line back and forth.
328#[derive(Debug)]
329#[repr(align(64))]
330struct Local {
331    /// What the reactor counts.
332    stats: Stats,
333    /// A counter per command, for `INFO commandstats`.
334    cmdstats: CommandStats,
335    /// Which databases this thread has run a command against since the
336    /// maintenance turn last took the mask.
337    ///
338    /// One bit per database. The thread ors into it and the turn takes the whole
339    /// of it with a swap, which is what keeps a mark that lands during the swap
340    /// from being lost: the worst that can happen is a bit the turn has already
341    /// taken being set again, and that costs one more look at a database with
342    /// nothing to collect.
343    dirty: AtomicU64,
344    /// The mask this thread's maintenance turn is working from.
345    ///
346    /// Its own and not a shared one, because a turn reads it in place and then
347    /// clears bits of it, and a shared mask cleared that way would lose whatever
348    /// another thread marked in between. Every thread turns a loop and every
349    /// loop maintains, so what stops the same work being done twice is not the
350    /// mask but the stripe lock underneath it: two threads that both look at
351    /// database nine take turns, and the second one finds nothing left to move.
352    ///
353    /// Starts with every database set, so a server that has just been built
354    /// looks at all of them once rather than waiting to be told about the ones
355    /// something was loaded into before any command ran.
356    turn: AtomicU64,
357    /// How many of this thread's clients are on the waiter list.
358    ///
359    /// The waiter list is one list behind one lock, and a thread can only answer
360    /// the waiters it parked itself, so a thread with none of its own has no
361    /// reason to take that lock at all. Without this the check is the server
362    /// wide count, and one client blocked anywhere puts every thread through the
363    /// shared lock after every command it runs and again on every disconnect.
364    ///
365    /// Only the thread this belongs to writes it, because parking, answering and
366    /// forgetting a waiter all happen on the thread that read the command, so
367    /// the load and the store either side of a change cannot lose one.
368    parked: AtomicUsize,
369    /// The millisecond this thread last took every thread's marks.
370    ///
371    /// One per thread rather than one for the server, which is the opposite of
372    /// [`Server::expire_ms`] and for a reason. Taking the marks moves them out
373    /// of the shared counters and into the mask of whoever took them, so a
374    /// thread that skips a collection is a thread that never hears about a
375    /// database somebody else wrote to. A server wide gate would leave every
376    /// thread but one with a stale mask.
377    ///
378    /// Only the thread this belongs to reads or writes it, so it is a plain
379    /// number in an atomic rather than anything that needs ordering.
380    collect_ms: AtomicU64,
381    /// Where this thread's maintenance turn starts looking for a segment to
382    /// hand back.
383    ///
384    /// One per thread rather than one for the server, for the same reason
385    /// [`Local::collect_ms`] is one per thread and for one more. The turn runs
386    /// after every batch on every thread and it moves the cursor whether or not
387    /// it found anything, so a shared cursor is a line every thread writes at
388    /// batch rate, and what that costs grows with the thread count rather than
389    /// staying still. It is the only shared write left on a maintenance turn
390    /// that has nothing to do, which is nearly every turn on a server that is
391    /// keeping up.
392    ///
393    /// Sharing bought one thing, which is two threads not looking at the same
394    /// database at the same time, and that was already worth very little: the
395    /// second one takes the stripe lock, finds the first has moved what was
396    /// there and goes on. Starting each thread at its own index keeps most of
397    /// that anyway.
398    ///
399    /// [`Server::next_db`] stays shared and stays where it is, because the path
400    /// that reads it runs when a server is over its memory limit and writes it
401    /// only when it moved something.
402    ///
403    /// Only the thread this belongs to reads or writes it, so it is a plain
404    /// number in an atomic rather than anything that needs ordering.
405    compact_db: AtomicUsize,
406    /// Which databases this thread has to weigh again before the total it
407    /// publishes means anything.
408    ///
409    /// One bit per database, the same shape as [`Local::dirty`] and set from the
410    /// same places, because the two questions have the same answer: a database
411    /// something ran against is a database whose memory may have moved. They are
412    /// two masks and not one because they are taken by different readers at
413    /// different rates, and a mask one of them cleared would be a mask the other
414    /// never saw.
415    ///
416    /// Starts with every database set, so the first reading a thread takes is a
417    /// walk over all of them rather than a sum of sixteen zeroes.
418    ///
419    /// Only the thread this belongs to reads or writes it. Another thread's
420    /// writes arrive through [`Server::collect_marks`], so a database that only
421    /// somebody else has written to is weighed again on the next collection
422    /// rather than on the next batch.
423    unmeasured: AtomicU64,
424    /// The millisecond this thread last took a memory reading.
425    ///
426    /// The gate that turns a reading a batch into a reading a millisecond. See
427    /// [`Server::refresh_memory_slice`] for why a reading that old is enough,
428    /// which comes down to the reading only having to be exact at the moment a
429    /// command is judged against the limit, and [`Server::make_room`] taking its
430    /// own at that moment.
431    ///
432    /// Only the thread this belongs to reads or writes it.
433    measure_ms: AtomicU64,
434    /// Which database this thread weighs again next whatever its mask says.
435    ///
436    /// One a reading, round robin, so a database that something changed without
437    /// marking it is out of date for at most sixteen readings rather than until
438    /// the next time a client happens to name it. What that costs is one
439    /// database's stripes on a reading that would otherwise have touched none.
440    ///
441    /// Only the thread this belongs to reads or writes it.
442    measure_db: AtomicUsize,
443}
444
445impl Local {
446    /// A thread's counters, starting its compaction cursor at `at`.
447    fn at(at: usize) -> Local {
448        Local {
449            stats: Stats::default(),
450            cmdstats: CommandStats::default(),
451            dirty: AtomicU64::new(0),
452            turn: AtomicU64::new(ALL_DATABASES),
453            parked: AtomicUsize::new(0),
454            collect_ms: AtomicU64::new(u64::MAX),
455            compact_db: AtomicUsize::new(at),
456            unmeasured: AtomicU64::new(ALL_DATABASES),
457            measure_ms: AtomicU64::new(u64::MAX),
458            measure_db: AtomicUsize::new(at),
459        }
460    }
461}
462
463impl Default for Local {
464    fn default() -> Local {
465        Local::at(0)
466    }
467}
468
469impl Local {
470    /// Note that a command has run against these databases.
471    fn mark(&self, dbs: u64) {
472        self.dirty.store(self.dirty.load(Relaxed) | dbs, Relaxed);
473        self.unmeasure(dbs);
474    }
475
476    /// Note that what these databases hold may have changed since they were last
477    /// weighed.
478    ///
479    /// Every write goes through [`Local::mark`], which calls this. The places
480    /// that call it on their own are the ones that change what a database holds
481    /// without a command having asked: the expiry sweep, compaction and
482    /// eviction. A path that forgot would be out of date until
483    /// [`Local::measure_db`] came round to it rather than wrong for good.
484    fn unmeasure(&self, dbs: u64) {
485        self.unmeasured
486            .store(self.unmeasured.load(Relaxed) | dbs, Relaxed);
487    }
488
489    /// Take the mask of databases to weigh again, leaving it empty.
490    ///
491    /// A swap and not a read, because the reading that follows is what makes
492    /// them measured. A mark that lands during it is left set and is weighed on
493    /// the next reading, which is the same one batch of slack every other number
494    /// on this path already carries.
495    fn to_weigh(&self) -> u64 {
496        self.unmeasured.swap(0, Relaxed)
497    }
498
499    /// Which database to weigh again this reading whatever the mask says, moving
500    /// the cursor on for the next one.
501    fn measure_next(&self) -> usize {
502        let at = self.measure_db.load(Relaxed) % DATABASES;
503        self.measure_db.store((at + 1) % DATABASES, Relaxed);
504        at
505    }
506
507    /// Whether this thread has yet to take a memory reading on millisecond
508    /// `now`.
509    ///
510    /// The same shape as [`Local::collecting`] and for the same reason: the
511    /// caller asks on every batch and pays for it a thousand times a second.
512    fn measuring(&self, now: u64) -> bool {
513        if self.measure_ms.load(Relaxed) == now {
514            return false;
515        }
516        self.measure_ms.store(now, Relaxed);
517        true
518    }
519
520    /// Add `dbs` to what this thread's turn is going to look at.
521    fn note(&self, dbs: u64) {
522        self.turn.store(self.turn.load(Relaxed) | dbs, Relaxed);
523    }
524
525    /// Take `at` off the list of databases this thread's turn will look at.
526    fn done(&self, at: usize) {
527        self.turn
528            .store(self.turn.load(Relaxed) & !(1u64 << at), Relaxed);
529    }
530
531    /// Whether this thread's turn still has database `at` to look at.
532    fn wanted(&self, at: usize) -> bool {
533        self.turn.load(Relaxed) & (1u64 << at) != 0
534    }
535
536    /// Whether this thread has yet to take the marks on millisecond `now`.
537    ///
538    /// Says yes once a millisecond and remembers that it did, so the caller can
539    /// ask on every batch and pay for it a thousand times a second.
540    fn collecting(&self, now: u64) -> bool {
541        if self.collect_ms.load(Relaxed) == now {
542            return false;
543        }
544        self.collect_ms.store(now, Relaxed);
545        true
546    }
547
548    /// Note that `n` more of this thread's clients are parked.
549    fn blocked(&self, n: usize) {
550        self.parked
551            .store(self.parked.load(Relaxed).saturating_add(n), Relaxed);
552    }
553
554    /// Note that `n` of them are not parked any more.
555    fn woke(&self, n: usize) {
556        self.parked
557            .store(self.parked.load(Relaxed).saturating_sub(n), Relaxed);
558    }
559}
560
561/// Room for one thread, which is what a server starts with.
562fn one_thread() -> Box<[Local]> {
563    slots(1)
564}
565
566/// Room for `threads` of them.
567fn slots(threads: usize) -> Box<[Local]> {
568    // By index, so that the compaction cursors start spread out over the
569    // databases rather than every thread walking in on the same one.
570    (0..threads.max(1)).map(Local::at).collect()
571}
572
573/// Where the process was started, which is what `dir` defaults to.
574///
575/// A dot if the working directory cannot be read, which happens when it has
576/// been deleted out from under a running process. That is not a reason to
577/// refuse to start a server, and it leaves `BACKUP` to fail with the real error
578/// from the filesystem if anybody asks for one.
579fn working_dir() -> PathBuf {
580    std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
581}
582
583/// One command's counters, for `INFO commandstats`.
584///
585/// Three of Redis's five. `usec` and `usec_per_call` are not here because
586/// nothing times a command, and timing one means two clock reads around a call
587/// that takes tens of nanoseconds to begin with. Redis pays that because Redis
588/// has room for it; this does not, and a zero under a name that says microseconds
589/// is worse than an absent field, which is the same rule the rest of `INFO`
590/// follows.
591#[derive(Debug, Clone, Copy, Default)]
592pub struct CommandStat {
593    /// Times the command ran, whatever it answered.
594    pub calls: u64,
595    /// Times it was turned away before it ran, which is the wrong number of
596    /// arguments or no room under `maxmemory`.
597    pub rejected: u64,
598    /// Times it ran and answered with an error.
599    pub failed: u64,
600}
601
602impl CommandStat {
603    /// Whether this command has ever been seen.
604    ///
605    /// A row that has not is left out of the reply, which is what Redis does and
606    /// is why the section is a handful of lines on a working server rather than
607    /// one line per command in the table.
608    const fn seen(&self) -> bool {
609        self.calls != 0 || self.rejected != 0 || self.failed != 0
610    }
611}
612
613/// One command's counters as one thread keeps them.
614///
615/// The same three numbers as [`CommandStat`], which is what they add up to when
616/// `INFO` asks. This is the written form and that is the read one.
617#[derive(Debug, Default)]
618struct Row {
619    /// Times the command ran.
620    calls: Counter,
621    /// Times it was turned away before it ran.
622    rejected: Counter,
623    /// Times it ran and answered with an error.
624    failed: Counter,
625}
626
627/// A counter per command, indexed the way [`table::index_of`] says.
628///
629/// A flat array and not a map, because the dispatcher is already holding the
630/// spec and the spec's position in the table is two addresses subtracted. That
631/// makes the counting a load, an add and a store on a row the previous command
632/// of the same name has already pulled into cache.
633#[derive(Debug)]
634struct CommandStats(Box<[Row]>);
635
636impl Default for CommandStats {
637    fn default() -> CommandStats {
638        CommandStats((0..table::count()).map(|_| Row::default()).collect())
639    }
640}
641
642impl CommandStats {
643    /// The row for one command.
644    fn at(&self, spec: &'static Spec) -> &Row {
645        &self.0[table::index_of(spec)]
646    }
647}
648
649/// Where a database gets its store from, asked by database number.
650///
651/// `None` means that database cannot have one. The caller owns whatever the
652/// stores are cut out of, which for `yodb` is one `.yo` file with a log per
653/// database, and this crate never learns what any of that is.
654pub type StoreSource = dyn FnMut(usize) -> Option<Store> + Send;
655
656/// Every thread that runs commands here shares this server, so it has to be
657/// `Send` and `Sync`, and the check is here so that a type added to it that is
658/// neither is a compile error where it was added rather than an error in the
659/// code that starts the threads.
660const _: () = {
661    const fn shareable<T: Send + Sync>() {}
662    shareable::<Server>();
663};
664
665/// Everything a server holds.
666///
667/// One per process, however many threads are serving out of it. What is inside
668/// is either shared outright, which is the counters and the settings, or behind
669/// a lock, which is the stripes and the few pieces of state a command can
670/// change. What makes this a server rather than a shard is that it is the whole
671/// of what a connection can address.
672pub struct Server {
673    dbs: Vec<Db>,
674    /// How many stripes each database is cut into, the same for all of them.
675    ///
676    /// Kept here as well as in each database so that the flat slot arithmetic
677    /// below is a multiply and a divide against a field on the server rather
678    /// than a walk asking each database how wide it is.
679    width: usize,
680    clock: Clock,
681    started_ms: u64,
682    /// Where the next hard compaction starts looking, so that a database under
683    /// constant write load cannot hold the other fifteen's space.
684    ///
685    /// Shared, because the thing that asks for one is a command that went over
686    /// the memory limit and is trying to get back under it, and that is any
687    /// thread. Two threads that read the same cursor start on the same
688    /// database, and what that costs is one of them finding the other has
689    /// already moved what was there. It is only written when a segment did
690    /// move, so a server that is not over its limit never touches it at all.
691    ///
692    /// The maintenance turn has its own cursor per thread rather than sharing
693    /// this one. See [`Local::compact_db`] for why.
694    next_db: AtomicUsize,
695    /// One bit per database, set when a command ran against it.
696    ///
697    /// The maintenance turn after every batch used to ask all sixteen
698    /// databases whether they had anything to collect, and asking costs a load
699    /// and a store in each one. Fifteen of those are cold lines on a server
700    /// where every client is on database zero, which is every server, and the
701    /// answer is no every time. This is the cheap half of the question: a
702    /// database nobody has touched since it last said no cannot have started
703    /// saying yes.
704    ///
705    /// What the connections are holding, kept by the engine.
706    ///
707    /// Shared, because every thread has connections and the memory total is one
708    /// total. Each thread adds and subtracts its own change rather than storing
709    /// a figure it worked out, so two threads whose buffers grew in the same
710    /// moment both count.
711    conn_bytes: AtomicUsize,
712    /// The `maxmemory` limit in bytes, zero when there is not one.
713    ///
714    /// Zero is the default and it is the whole reason the check in front of
715    /// every write is one comparison against a field that is already warm. It
716    /// is read by every command on every thread and written by a client that
717    /// sends `CONFIG SET`, so it is a number the threads can share rather than
718    /// a field one of them owns.
719    maxmemory: AtomicU64,
720    /// Where a database gets a store from the first time it needs one.
721    ///
722    /// A closure and not a store, because there are sixteen databases and a
723    /// server that fills memory on database zero should not have opened
724    /// anything for the other fifteen. Nothing is asked of this until a memory
725    /// limit is actually reached, so a server that never fills memory never
726    /// opens a file, and a server that has no file never has one of these.
727    ///
728    /// `None` from the closure means that database cannot have one, which is
729    /// how the caller says the file it opened has no more room for logs.
730    ///
731    /// Behind a lock because it is a closure the caller gave us and there is no
732    /// saying it can be run by two threads at once. It is asked once per
733    /// database, the first time that database has to move something, so a
734    /// server that has reached its memory limit takes this lock sixteen times
735    /// in its life.
736    store: Lock<Option<Box<StoreSource>>>,
737    /// The `maxstore` limit in bytes, `None` when there is not one.
738    ///
739    /// The storage limit, and the other half of the inversion `14` section 4.1
740    /// describes. `maxmemory` is a limit on memory and the right answer to a
741    /// memory limit on a system with a file under it is to move data to the
742    /// file, not to delete it. Deleting is the right answer to a limit on the
743    /// file, and this is that limit.
744    ///
745    /// Zero is not "no limit" here, which is the one place this reads
746    /// differently from `maxmemory` and is the difference that makes a drop in
747    /// cache possible. A storage budget of zero bytes means nothing may live on
748    /// the file, so migration cannot make room and eviction is the only thing
749    /// left, which is Redis exactly. `None` is no limit and is the default,
750    /// which with `noeviction` means the database grows until the disk is full
751    /// and then writes fail, which is what a database does.
752    ///
753    /// Shared between the threads the same way `maxmemory` is, and no limit is
754    /// [`NO_MAXSTORE`] rather than a second field saying whether the first one
755    /// counts. Two fields cannot be read as one, and a limit that was on when
756    /// the bytes were read and off by the time the number was is a limit that
757    /// answers from a server that never existed.
758    maxstore: AtomicU64,
759    /// What [`Server::memory_bytes`] said at the last maintenance turn.
760    ///
761    /// The reading is a walk over every collection in every database and cannot
762    /// go on a command path, so the command path reads this instead and is at
763    /// most one batch behind. What that costs is overshoot: a server can end a
764    /// batch holding one batch's worth of allocation more than its limit before
765    /// anything notices. A batch is 64 commands, so that is bounded by what 64
766    /// commands can allocate and not by how long the server runs.
767    ///
768    /// Only kept up to date when there is a limit to judge it against. A server
769    /// with no `maxmemory` never reads it and never pays for it.
770    ///
771    /// Shared, because it is read in front of every write on every thread and
772    /// written by whichever thread last took a reading. A reader that catches it
773    /// mid write gets one of the two readings and both of them were true a
774    /// moment ago, which is all this number ever claims to be.
775    used: AtomicUsize,
776    /// What each database was holding the last time anything read it.
777    ///
778    /// [`Server::settled_memory`] adds these up rather than walking the stripes
779    /// of every database, and re-reads a database only when something has marked
780    /// it since the last reading. Fifteen of the sixteen are empty on nearly
781    /// every server there is, and walking them was locking every stripe of every
782    /// one of them once a batch to be told the same number again.
783    ///
784    /// Shared and not per thread, so a database one thread re-read is a database
785    /// every thread has the fresh number for.
786    db_bytes: [AtomicUsize; DATABASES],
787    /// What the server was holding before a client had written anything.
788    ///
789    /// `MEMORY STATS` reports it as `startup.allocated` and subtracts it from
790    /// the total to work out what a key costs on average, which only means
791    /// something if the baseline is a real reading rather than a guess. So it is
792    /// taken once, at the end of building the server, and never again.
793    startup: AtomicUsize,
794    /// The largest total anything has ever seen here.
795    ///
796    /// See [`Server::peak_bytes`] for what "ever seen" means, which is not the
797    /// same as the largest total there ever was.
798    peak: AtomicUsize,
799    /// Which database the next eviction draws from.
800    ///
801    /// Its own cursor and not [`Server::next_db`], because eviction and
802    /// compaction move at different rates and sharing one would make the
803    /// database that gets compacted depend on how many keys were evicted.
804    ///
805    /// Shared for the same reason [`Server::next_db`] is, and with the same
806    /// answer: two threads evicting at once may pick the same database, and one
807    /// of them finds the other got there first and moves on.
808    evict_db: AtomicUsize,
809    /// Which database the next active expiry sweep starts at.
810    ///
811    /// A third cursor for the same reason there is a second one. A sweep runs on
812    /// every turn of the loop and compaction runs when there is dead space, so
813    /// sharing a cursor would make which database gets swept depend on which one
814    /// was last collected.
815    expire_db: AtomicUsize,
816    /// The millisecond the last active expiry sweep ran on, so the next one on
817    /// the same millisecond does not bother.
818    ///
819    /// One for the server and not one per thread, so the sweeping a server does
820    /// is a function of how long it has been running and not of how many threads
821    /// it was started with. Two threads that read the same millisecond can both
822    /// decide to sweep, which costs one extra sweep of a budget that is already
823    /// small and cannot happen twice for the same millisecond more than once per
824    /// thread.
825    expire_ms: AtomicU64,
826    /// Clients parked on a blocking command.
827    ///
828    /// Behind a lock because a client parks on the thread that ran its command
829    /// and is woken by whichever thread later puts something under a key it
830    /// named, and those are not the same thread. The lock is only ever taken to
831    /// park somebody, to serve somebody or to forget a connection that has gone,
832    /// so a command that does not block never touches it.
833    waiters: Lock<Waiters>,
834    /// How many clients are parked.
835    ///
836    /// Beside the list rather than read out of it, because every command asks
837    /// whether anybody is waiting and nearly every answer is no. Taking a lock
838    /// to be told no would be a cache line every thread has to own to ask, which
839    /// is the cost the list was put behind a lock to avoid.
840    ///
841    /// Written under the lock, by whoever changed the list, so the number and
842    /// the list agree except while a change is in progress. A reader that asks
843    /// during one is told about the moment before it, and the worst that costs
844    /// is a walk of the list that serves nobody or one that has not started yet
845    /// and happens on the next command instead.
846    parked: AtomicUsize,
847    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
848    ///
849    /// Empty on a server nobody has migrated a key out of, which is nearly all
850    /// of them, and it costs a vector's three words to be empty.
851    ///
852    /// Behind a lock because a socket cannot be written by two threads at once
853    /// and a cache of them cannot be searched by one while another is taking an
854    /// entry out. It is held for the whole of a migration, which is a round trip
855    /// to another server, so two threads migrating at the same time take turns.
856    /// That is the right way round: the alternative is a socket per thread per
857    /// peer, and a `MIGRATE` is not what a server spends its time on.
858    peers: Lock<migrate::Peers>,
859    /// What each thread that runs commands here keeps to itself.
860    ///
861    /// A fixed list, because a thread reading its own entry must not have the
862    /// list move under it, and how many threads there will be is known before
863    /// any of them starts. A server nobody told otherwise has one.
864    locals: Box<[Local]>,
865    /// How many entries have been handed out.
866    claimed: AtomicUsize,
867    /// The next client id, which is what `CLIENT ID` answers.
868    ///
869    /// On the server and not on a front, because CLIENT LIST and CLIENT KILL
870    /// name a client by this number across the whole server, and two threads
871    /// counting on their own would hand the same number to two clients. Starts
872    /// at one so that zero is never a client, which is what makes it usable as
873    /// the id of a command that came from nowhere.
874    next_client: AtomicU64,
875    /// Where `BACKUP` puts its files, and where `CONFIG GET dir` points.
876    ///
877    /// Absolute, and resolved once when the server is built rather than every
878    /// time somebody asks. `BACKUP LIST` answers absolute paths and a client is
879    /// entitled to hand one of them to a copy tool, so a relative path that
880    /// meant something different after a `chdir` would be a path that stops
881    /// working for reasons nobody could see.
882    dir: PathBuf,
883    /// What backup is running, if one is.
884    ///
885    /// On the server and not on a session, because a backup outlives the
886    /// connection that asked for it and any other connection can seal it.
887    ///
888    /// Behind a lock because there is one backup at a time and any thread can be
889    /// the one that starts, seals or abandons it. It is held while the base file
890    /// is written, which is what keeps two `BACKUP START` commands from writing
891    /// over each other's files.
892    backup: Lock<backup::State>,
893    /// Whether a sealed backup is sitting on disk.
894    ///
895    /// Beside the state rather than read out of it, because every batch of
896    /// commands asks whether there is a backup old enough to sweep away and on
897    /// nearly every server the answer is that there is no backup at all. A load
898    /// answers that. Written under the lock by whoever moved the phase, so a
899    /// reader that asks mid-change sees the moment before and sweeps one batch
900    /// later, which is a file staying on disk for a few microseconds longer than
901    /// it had to.
902    sealed: AtomicBool,
903    /// The search indexes and the names pointing at them.
904    ///
905    /// On the server and not on a database, which is the one collection in this
906    /// build that is. A real server keeps its indexes in the search module, the
907    /// module has one table, and `SELECT 1` followed by `FT._LIST` lists the
908    /// indexes made on database zero. `search.rs` has the rest of why.
909    ///
910    /// A server nobody has made an index on holds two empty vectors here, which
911    /// is six words and no allocation.
912    ///
913    /// Behind a lock because an index is made and dropped by whichever thread
914    /// ran the command, and the table it goes in is one table. Only the `FT`
915    /// commands take it, so nothing a working server spends its time on comes
916    /// through here.
917    search: Lock<Registry>,
918    /// The replies that came back in pieces and have pieces left.
919    ///
920    /// Beside the indexes rather than inside one, because a cursor is read
921    /// under its own number and a real server resolves the index name on a read
922    /// and then pays no attention to it, so a cursor made on one index reads
923    /// through the name of another. Behind a lock for the reason the registry is
924    /// behind one, and a server nobody has opened a cursor on holds an empty map
925    /// here.
926    cursors: Lock<Cursors>,
927    /// The script bodies `EVALSHA` runs, by their digests.
928    ///
929    /// On the server rather than on a connection, because that is the whole
930    /// point of the cache. A client loads its scripts once when it starts up,
931    /// on whichever connection it happened to open first, and then sends nothing
932    /// but digests forever after, from every connection in its pool.
933    ///
934    /// Behind a lock because loading is a write and every thread can be the one
935    /// doing it. Held only long enough to add a body or copy one out, never
936    /// across a run: a running script calls commands, and those take locks of
937    /// their own.
938    scripts: Lock<lua::Scripts>,
939    /// Every library `FUNCTION LOAD` has taken, and what each one registered.
940    ///
941    /// Data only. A callback is a Lua value and there is an interpreter per
942    /// thread, so what is here is the name, the code, the digest of the code and
943    /// one row per function, and every thread compiles the code for itself the
944    /// first time one of its clients calls into the library.
945    libraries: Lock<lua::library::Libraries>,
946    /// Set by `SHUTDOWN`, and read by whatever is turning the loop.
947    ///
948    /// A flag rather than an exit, because the command layer is not what owns
949    /// the process. It runs inside a batch that has other commands behind it
950    /// and inside a driver that has a socket file to take away and a file to
951    /// close, and a server that calls `exit` from a command handler skips all
952    /// of that. So the command says stop and the driver stops, on the same turn
953    /// and through the same door a signal uses.
954    stopping: AtomicBool,
955    /// Every key any connection is watching, with a stamp on each.
956    ///
957    /// Here and not on the connection, and that is the whole design of `WATCH`
958    /// rather than an implementation detail. A connection cannot see a write
959    /// another thread made, so what records the write has to sit beside the key.
960    /// See the `multi` module for the rest of it.
961    watches: Lock<Watches>,
962    /// How many watched keys there are, so the write path can ask without
963    /// taking the lock.
964    ///
965    /// Zero on every server nobody has sent `WATCH` to, which is very nearly all
966    /// of them, and that is what keeps the cost of watches on a server that has
967    /// none down to one relaxed load per write.
968    watched: AtomicUsize,
969    /// Who is listening on what, for pub/sub.
970    ///
971    /// Here and not on the connection for the reason the watches are: a publish
972    /// arrives on a connection that knows nothing about the subscribers, so what
973    /// finds them has to sit beside the name rather than beside the client. See
974    /// the `pubsub` module for the rest of it.
975    pubsub: Lock<pubsub::Registry>,
976    /// How many subscriptions there are, so a publish can ask without taking
977    /// the lock.
978    ///
979    /// Zero on every server nobody has subscribed on, which is what keeps
980    /// `PUBLISH` on a server with no listeners down to one relaxed load.
981    subs: AtomicUsize,
982    /// One inbox per thread, for messages published on another one.
983    ///
984    /// Its own array and not a field on [`Local`], which is a cache line per
985    /// thread precisely so that no other thread writes to it. A mailbox is a
986    /// line another thread is meant to write to, so it gets one of its own.
987    mail: Box<[pubsub::Mailbox]>,
988    /// Which classes of keyspace notification are turned on.
989    ///
990    /// Zero is off and is the default, so the read every write does costs one
991    /// relaxed load and a test. It is `notify-keyspace-events` and the bits are
992    /// Redis's own, kept in the `notify` module beside the two parsers that
993    /// turn them into the setting text and back.
994    notify: AtomicU32,
995    /// One row per open connection, which is what `CLIENT LIST` reads and what
996    /// `CLIENT KILL` writes to.
997    ///
998    /// Here and not on the front for the reason the watches and the
999    /// subscriptions are here: both commands are about connections the thread
1000    /// running them does not own and cannot borrow. See the `clients` module.
1001    clients: Lock<clients::Clients>,
1002    /// How many connections have been asked to close and not closed yet.
1003    ///
1004    /// Zero on every server nobody has run `CLIENT KILL` on, which is what keeps
1005    /// the check on the flush path down to one load.
1006    kills: AtomicUsize,
1007    /// When the pause `CLIENT PAUSE` armed runs out, and what it covers.
1008    ///
1009    /// One word rather than a deadline and a mode beside it, because every
1010    /// command on every thread reads this and a server that has never been
1011    /// paused should pay one load and one test for it. The low bit says whether
1012    /// everything is held or only the writes, and the rest is the deadline in
1013    /// milliseconds. Zero is no pause at all, which is why the deadline is
1014    /// shifted up rather than packed into the top bits: the whole word is zero
1015    /// exactly when nothing is armed.
1016    pause: AtomicU64,
1017    /// The connections `MONITOR` is feeding, and a count of them.
1018    ///
1019    /// Here for the third time and for the third version of the same reason:
1020    /// the command being reported is running on a thread that cannot reach the
1021    /// connection being told about it. See the `monitor` module.
1022    monitors: monitor::Monitors,
1023    /// Being a master: the identity, the stream and whoever is being fed it.
1024    ///
1025    /// Here for the fourth time and for the fourth version of the same reason:
1026    /// the write being copied is running on a thread that cannot reach the
1027    /// connection it has to be copied to. See the `repl` module.
1028    repl: repl::Replication,
1029    /// Being a replica: who this server follows and the link out to them.
1030    ///
1031    /// Beside [`Server::repl`] rather than inside it because the two are
1032    /// opposite halves of the same idea and a server is nearly always neither.
1033    /// See the `follow` module.
1034    follow: follow::Follower,
1035    /// Handing the master's job over on purpose, which is `FAILOVER`.
1036    ///
1037    /// Beside the other two because it is the one thing that reaches into both:
1038    /// it starts on a master, waits on a replica, and ends with this server
1039    /// being one. See the `failover` module.
1040    failover: failover::Failover,
1041    /// The sixteen thousand slots and who owns each of them, which is all of
1042    /// cluster mode and is idle on a server that was not started as a node.
1043    ///
1044    /// Beside the replication fields because it is the other half of the same
1045    /// subject: replication is how one server's keys reach a second, and this is
1046    /// how a keyspace too big for one server is cut up in the first place. See
1047    /// the `cluster` module.
1048    cluster: cluster::Cluster,
1049    /// A handle on this server, for the one thing that outlives the command
1050    /// that started it.
1051    ///
1052    /// The replica link is a thread, and a thread cannot borrow the server it
1053    /// runs against, so it has to hold a counted handle. Nothing inside a
1054    /// `Server` can make one of those out of a borrow, so the handle is put here
1055    /// by whoever wrapped the server up, which is `Wire::over` and is the one
1056    /// place that has both. Weak rather than strong, because a strong one would
1057    /// be a server holding itself alive forever.
1058    ///
1059    /// Empty on an embedded caller that never built an engine, and `REPLICAOF`
1060    /// says so rather than pretending to have started a link.
1061    myself: Lock<Weak<Server>>,
1062    /// What the saves have done, which is all `INFO persistence` has to report.
1063    persist: persist::Persistence,
1064    /// Who is allowed to run what, which is also where `requirepass` lives.
1065    acl: acl::Users,
1066    /// Every refusal the ACL has made, which is what `ACL LOG` reports.
1067    acllog: acl::Log,
1068    /// The file `ACL LOAD` reads and `ACL SAVE` writes, empty when there is
1069    /// none, which is the default and is every server nobody gave one to.
1070    ///
1071    /// Taken at startup and never changed, the same as on a real server, where
1072    /// `aclfile` is an immutable config: a server that could be pointed at a
1073    /// different ACL file while it was running would be a server an operator
1074    /// could not reason about.
1075    aclfile: PathBuf,
1076    /// The plain `requirepass`, kept only so `CONFIG GET` can report it.
1077    plain: acl::Plain,
1078    /// The knobs `DEBUG` turns, which is what a test suite reaches for.
1079    debug: debug::Knobs,
1080}
1081
1082impl Server {
1083    /// A server with [`DATABASES`] empty databases on the system clock.
1084    #[must_use]
1085    pub fn new() -> Server {
1086        let clock = Clock::system();
1087        let server = Server {
1088            dbs: (0..DATABASES)
1089                .map(|_| Db::with_clock(clock.clone(), 1))
1090                .collect(),
1091            width: 1,
1092            started_ms: clock.now_ms(),
1093            clock,
1094            next_db: AtomicUsize::new(0),
1095            conn_bytes: AtomicUsize::new(0),
1096            maxmemory: AtomicU64::new(0),
1097            store: Lock::new(None),
1098            maxstore: AtomicU64::new(NO_MAXSTORE),
1099            used: AtomicUsize::new(0),
1100            db_bytes: [const { AtomicUsize::new(0) }; DATABASES],
1101            startup: AtomicUsize::new(0),
1102            peak: AtomicUsize::new(0),
1103            evict_db: AtomicUsize::new(0),
1104            expire_db: AtomicUsize::new(0),
1105            expire_ms: AtomicU64::new(0),
1106            waiters: Lock::default(),
1107            parked: AtomicUsize::new(0),
1108            peers: Lock::default(),
1109            locals: one_thread(),
1110            claimed: AtomicUsize::new(0),
1111            next_client: AtomicU64::new(1),
1112            dir: working_dir(),
1113            backup: Lock::default(),
1114            sealed: AtomicBool::new(false),
1115            search: Lock::new(Registry::new()),
1116            cursors: Lock::default(),
1117            scripts: Lock::default(),
1118            libraries: Lock::default(),
1119            stopping: AtomicBool::new(false),
1120            watches: Lock::default(),
1121            watched: AtomicUsize::new(0),
1122            pubsub: Lock::default(),
1123            subs: AtomicUsize::new(0),
1124            notify: AtomicU32::new(0),
1125            clients: Lock::default(),
1126            kills: AtomicUsize::new(0),
1127            pause: AtomicU64::new(0),
1128            monitors: monitor::Monitors::default(),
1129            repl: repl::Replication::default(),
1130            follow: follow::Follower::default(),
1131            failover: failover::Failover::default(),
1132            cluster: cluster::Cluster::default(),
1133            myself: Lock::new(Weak::new()),
1134            persist: persist::Persistence::default(),
1135            acl: acl::Users::default(),
1136            acllog: acl::Log::default(),
1137            aclfile: PathBuf::new(),
1138            plain: acl::Plain::default(),
1139            debug: debug::Knobs::default(),
1140            mail: pubsub::boxes(1),
1141        };
1142        server.note_startup();
1143        server
1144    }
1145
1146    /// A server whose databases are cut into `width` stripes each.
1147    ///
1148    /// Not reachable from the command line yet. Every command group answers on
1149    /// a server of any width now and so does everything that walks a whole
1150    /// database, and the tests run each group at a width of one and a width of
1151    /// eight and check the two agree.
1152    ///
1153    /// What is left before this is what `--threads` sets is the engine. A
1154    /// database being several objects is what makes more than one thread
1155    /// possible, and it is not what makes more than one thread happen.
1156    #[must_use]
1157    pub fn with_width(width: usize) -> Server {
1158        let mut server = Server::new();
1159        // The server's own clock and not a fresh one, because a database
1160        // reading a different clock from the server it is on is a database
1161        // whose keys expire against a time nobody set.
1162        let clock = server.clock.clone();
1163        server.dbs = (0..DATABASES)
1164            .map(|_| Db::with_clock(clock.clone(), width))
1165            .collect();
1166        server.width = server.dbs[0].width();
1167        // Again, because the databases the first reading was taken of have just
1168        // been thrown away and replaced with wider ones, and a wider database
1169        // is a bigger baseline.
1170        server.note_startup();
1171        server
1172    }
1173
1174    /// A server on a clock the caller moves by hand, for tests.
1175    #[must_use]
1176    pub fn with_clock(clock: Clock) -> Server {
1177        let server = Server {
1178            dbs: (0..DATABASES)
1179                .map(|_| Db::with_clock(clock.clone(), 1))
1180                .collect(),
1181            width: 1,
1182            started_ms: clock.now_ms(),
1183            clock,
1184            next_db: AtomicUsize::new(0),
1185            conn_bytes: AtomicUsize::new(0),
1186            maxmemory: AtomicU64::new(0),
1187            store: Lock::new(None),
1188            maxstore: AtomicU64::new(NO_MAXSTORE),
1189            used: AtomicUsize::new(0),
1190            db_bytes: [const { AtomicUsize::new(0) }; DATABASES],
1191            startup: AtomicUsize::new(0),
1192            peak: AtomicUsize::new(0),
1193            evict_db: AtomicUsize::new(0),
1194            expire_db: AtomicUsize::new(0),
1195            expire_ms: AtomicU64::new(0),
1196            waiters: Lock::default(),
1197            parked: AtomicUsize::new(0),
1198            peers: Lock::default(),
1199            locals: one_thread(),
1200            claimed: AtomicUsize::new(0),
1201            next_client: AtomicU64::new(1),
1202            dir: working_dir(),
1203            backup: Lock::default(),
1204            sealed: AtomicBool::new(false),
1205            search: Lock::new(Registry::new()),
1206            cursors: Lock::default(),
1207            scripts: Lock::default(),
1208            libraries: Lock::default(),
1209            stopping: AtomicBool::new(false),
1210            watches: Lock::default(),
1211            watched: AtomicUsize::new(0),
1212            pubsub: Lock::default(),
1213            subs: AtomicUsize::new(0),
1214            notify: AtomicU32::new(0),
1215            clients: Lock::default(),
1216            kills: AtomicUsize::new(0),
1217            pause: AtomicU64::new(0),
1218            monitors: monitor::Monitors::default(),
1219            repl: repl::Replication::default(),
1220            follow: follow::Follower::default(),
1221            failover: failover::Failover::default(),
1222            cluster: cluster::Cluster::default(),
1223            myself: Lock::new(Weak::new()),
1224            persist: persist::Persistence::default(),
1225            acl: acl::Users::default(),
1226            acllog: acl::Log::default(),
1227            aclfile: PathBuf::new(),
1228            plain: acl::Plain::default(),
1229            debug: debug::Knobs::default(),
1230            mail: pubsub::boxes(1),
1231        };
1232        server.note_startup();
1233        server
1234    }
1235
1236    /// One database, by index.
1237    ///
1238    /// A caller that knows which key it wants names the one stripe the key is
1239    /// on rather than working over the whole thing, which is what `at` and its
1240    /// neighbours on [`Db`] are for. A caller that is about a database rather
1241    /// than about a key, which is the snapshot walk and a setting, works over
1242    /// all of them.
1243    ///
1244    /// The database is marked as having had something run against it, which is
1245    /// what this does that [`Server::striped_ref`] does not. Anything that only
1246    /// reads asks for that one and leaves the mark alone.
1247    ///
1248    /// The borrow is shared, and what makes that enough is that a database is
1249    /// several stripes behind a lock each. A caller that wants to change
1250    /// something holds the stripe it is changing, so two threads working on two
1251    /// keys work at once and two working on one key take turns, which is the
1252    /// whole point of cutting a database up.
1253    ///
1254    /// # Panics
1255    ///
1256    /// If `i` is not a database. `SELECT` is the only way a client changes the
1257    /// index and it checks, so an index that is out of range here is a bug in
1258    /// the caller and not something a client can ask for.
1259    pub fn striped(&self, i: usize) -> &Db {
1260        self.mine().mark(1u64 << i);
1261        &self.dbs[i]
1262    }
1263
1264    /// Every keyspace on the server, which is every stripe of every database.
1265    ///
1266    /// What the aggregates walk. A total over the whole server is a total over
1267    /// all of these and the stripe boundaries do not appear in it, which is
1268    /// what makes the numbers `INFO` reports the same numbers whatever the
1269    /// server was cut into.
1270    fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
1271        self.dbs
1272            .iter()
1273            .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
1274    }
1275
1276    /// How many keyspaces there are, counting every stripe of every database.
1277    ///
1278    /// The maintenance turns walk these rather than the databases, because a
1279    /// stripe is the thing that holds an arena and a deadline heap and so it is
1280    /// the thing that has anything to collect.
1281    const fn slots(&self) -> usize {
1282        DATABASES * self.width
1283    }
1284
1285    /// Which database slot `i` belongs to.
1286    const fn slot_db(&self, i: usize) -> usize {
1287        i / self.width
1288    }
1289
1290    /// Keyspace `i` of [`Server::slots`].
1291    fn slot(&self, i: usize) -> Held<'_, Keyspace> {
1292        let (db, stripe) = (i / self.width, i % self.width);
1293        self.dbs[db].hold_stripe(stripe)
1294    }
1295
1296    /// Where `BACKUP` writes and what `CONFIG GET dir` answers.
1297    #[must_use]
1298    pub fn dir(&self) -> &Path {
1299        &self.dir
1300    }
1301
1302    /// Point the server at a different directory, which `yodb serve --dir` does.
1303    ///
1304    /// Only before it is serving. There is no `CONFIG SET dir` here and there
1305    /// is none on a real server either without turning protected configs on,
1306    /// for the good reason that moving it out from under a running backup would
1307    /// leave files nothing can find again.
1308    pub fn set_dir(&mut self, dir: PathBuf) {
1309        self.dir = dir;
1310    }
1311
1312    /// The file `ACL LOAD` reads and `ACL SAVE` writes, or `None` for a server
1313    /// that was not given one.
1314    #[must_use]
1315    pub fn aclfile(&self) -> Option<&Path> {
1316        Some(self.aclfile.as_path()).filter(|p| !p.as_os_str().is_empty())
1317    }
1318
1319    /// Point the server at an ACL file, which `yodb serve --aclfile` does.
1320    ///
1321    /// Only before it is serving, and giving one does not read it: the caller
1322    /// asks for that with [`Server::load_acl`], so that a file that will not
1323    /// parse can stop the process before the port opens rather than after.
1324    pub fn set_aclfile(&mut self, path: PathBuf) {
1325        self.aclfile = path;
1326    }
1327
1328    /// Read the ACL file, if there is one, and make it the server's users.
1329    ///
1330    /// # Errors
1331    ///
1332    /// Everything the file got wrong, in one sentence. A caller starting a
1333    /// server should print it and stop, which is what a real server does: coming
1334    /// up with the users an operator did not ask for is worse than not coming up.
1335    pub fn load_acl(&self) -> std::result::Result<(), String> {
1336        match self.aclfile() {
1337            Some(path) => yo_alloc::allow(|| acl::load_file(self, path)),
1338            None => Ok(()),
1339        }
1340    }
1341
1342    /// Drop a sealed backup that has outlived `backup-sealed-ttl`.
1343    ///
1344    /// Once per batch, from the same maintenance turn that collects the arena.
1345    /// It reads two fields and returns on a server that has never taken a
1346    /// backup, which is nearly all of them.
1347    pub fn backup_expire(&self) {
1348        backup::expire(self);
1349    }
1350
1351    /// Ask for the server to stop, which is what `SHUTDOWN` does.
1352    ///
1353    /// It sets a flag and returns. Nothing here closes a socket, flushes a file
1354    /// or ends the process, because none of those belong to this layer, and a
1355    /// batch that is halfway through still has to finish and be written out.
1356    pub fn stop(&self) {
1357        self.stopping.store(true, Release);
1358    }
1359
1360    /// Whether somebody has asked the server to stop.
1361    ///
1362    /// Read once per turn by the loop, next to the flag a signal sets. The two
1363    /// mean the same thing and are separate only because one arrives from the
1364    /// operating system and the other from a client.
1365    #[must_use]
1366    pub fn stopping(&self) -> bool {
1367        self.stopping.load(Acquire)
1368    }
1369
1370    /// One database, by index, without taking it mutably.
1371    ///
1372    /// What the prefetch stage needs. It runs for all 64 commands in a batch
1373    /// before any of them executes, so it cannot hold the mutable borrow `run`
1374    /// is about to want, and it does not need one: warming a cache line reads
1375    /// nothing and changes nothing.
1376    #[must_use]
1377    pub fn striped_ref(&self, i: usize) -> &Db {
1378        &self.dbs[i]
1379    }
1380
1381    /// The stripe that answers for a database when a setting is read back.
1382    ///
1383    /// A ladder setting and an eviction policy are one number on a real server,
1384    /// and the fact that every stripe of every database carries a copy of it is
1385    /// ours rather than the client's problem. A write puts the same value on
1386    /// every one of them, so any stripe answers for all of them and this is the
1387    /// first one.
1388    fn settings(&self) -> Held<'_, Keyspace> {
1389        self.dbs[0].hold_stripe(0)
1390    }
1391
1392    /// Take a new clock reading, which every database is looking at.
1393    ///
1394    /// Once per turn of the event loop, which is the only place time moves. A
1395    /// command asking what the time is gets the answer the whole batch got, so
1396    /// two keys written by the same batch expire together (`04` section 3).
1397    ///
1398    /// Every thread does this on every turn of its own loop and they do not
1399    /// have to agree about when. The reading is only stored when the
1400    /// millisecond has changed, so what the threads are sharing is a line that
1401    /// is written about a thousand times a second and read millions.
1402    pub fn refresh_clock(&self) {
1403        self.clock.refresh();
1404    }
1405
1406    /// Move every clock here on by `ms`, for tests about expiry.
1407    ///
1408    /// The same thing [`Server::set_clock_ms`] does and by the same argument,
1409    /// except that it moves from wherever the clock is rather than to a stated
1410    /// moment, which is what a test that wants a key to have expired asks for.
1411    pub fn advance_clock_ms(&self, ms: u64) {
1412        let now = self.clock.now_ms() + ms;
1413        self.set_clock_ms(now);
1414    }
1415
1416    /// Move every clock here to `ms` by hand, for tests about expiry.
1417    ///
1418    /// A test cannot wait a hundred seconds and a test that waits a hundred
1419    /// milliseconds is a test that fails on a loaded machine, so time moves on
1420    /// request. The system clock underneath will overwrite this on the next
1421    /// [`Server::refresh_clock`], which is why this is only useful in a test
1422    /// that drives commands directly rather than through the event loop.
1423    pub fn set_clock_ms(&self, ms: u64) {
1424        self.clock.set(ms);
1425    }
1426
1427    /// Seconds since this server was built.
1428    #[must_use]
1429    pub fn uptime_secs(&self) -> u64 {
1430        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
1431    }
1432
1433    /// Bytes held by every database's index and arena, plus the read and reply
1434    /// buffers of every connection.
1435    ///
1436    /// The buffers are in here because they are real and because Redis counts
1437    /// its own, so leaving them out would make the one number people compare
1438    /// flattering rather than true. They are not a database, so nothing in the
1439    /// keyspace can change them and the engine has to say when they move.
1440    #[must_use]
1441    pub fn memory_bytes(&self) -> usize {
1442        self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes()
1443    }
1444
1445    /// What the server was holding before any client had written to it.
1446    ///
1447    /// `MEMORY STATS` reports this as `startup.allocated`.
1448    #[must_use]
1449    pub fn startup_bytes(&self) -> usize {
1450        self.startup.load(Relaxed)
1451    }
1452
1453    /// The largest total anything here has ever seen, this reading included.
1454    ///
1455    /// Peak memory is a sampled number on a real server too: `serverCron` takes
1456    /// a reading every hundred milliseconds and keeps the largest one. This is
1457    /// sampled as well, at the points where the total is already being worked
1458    /// out, which is once a batch on a server with a `maxmemory` and once a call
1459    /// on one without. So on a server with no limit that nobody is watching, the
1460    /// peak is the highest of the readings something asked for, which is the
1461    /// most a server that never takes a reading can honestly claim.
1462    #[must_use]
1463    pub fn peak_bytes(&self) -> usize {
1464        let now = self.memory_bytes();
1465        self.peak.fetch_max(now, Relaxed).max(now)
1466    }
1467
1468    /// Take the reading both of those start from.
1469    fn note_startup(&self) {
1470        let now = self.memory_bytes();
1471        self.startup.store(now, Relaxed);
1472        self.peak.store(now, Relaxed);
1473    }
1474
1475    /// What the keyspace itself is holding, live records only.
1476    ///
1477    /// `used_memory` minus this is what the store costs to run: the index, the
1478    /// space dead records are sitting in until compaction gets to them, and the
1479    /// connections' buffers.
1480    #[must_use]
1481    pub fn dataset_bytes(&self) -> usize {
1482        self.keyspaces()
1483            .map(|db| db.map().arena().live_bytes() as usize)
1484            .sum()
1485    }
1486
1487    /// Bytes the arenas are holding, live and dead together.
1488    #[must_use]
1489    pub fn arena_bytes(&self) -> usize {
1490        self.keyspaces()
1491            .map(|db| db.map().arena().reserved_bytes() as usize)
1492            .sum()
1493    }
1494
1495    /// Bytes the indexes are holding.
1496    #[must_use]
1497    pub fn index_bytes(&self) -> usize {
1498        self.keyspaces()
1499            .map(|db| db.map().index().memory_bytes())
1500            .sum()
1501    }
1502
1503    /// What arena compaction has cost, across every database.
1504    ///
1505    /// The write amplification of value separation, which is invisible from the
1506    /// outside otherwise: a client that writes a megabyte can leave the store
1507    /// copying several more, and the only sign of it without these is that the
1508    /// writes got slower.
1509    #[must_use]
1510    pub fn compaction(&self) -> yo_kv::Compaction {
1511        self.keyspaces().map(|db| db.map().compaction()).fold(
1512            yo_kv::Compaction::default(),
1513            |a, b| yo_kv::Compaction {
1514                walked: a.walked + b.walked,
1515                moved: a.moved + b.moved,
1516                bytes: a.bytes + b.bytes,
1517            },
1518        )
1519    }
1520
1521    /// Freed runs waiting on an arena size class list, across every database.
1522    ///
1523    /// How much of the store's own garbage is already back in circulation. A
1524    /// server whose value lengths repeat keeps a small number here and never
1525    /// compacts, and a server whose lengths wander keeps a large one and does,
1526    /// so the two numbers beside each other say which of the two collectors is
1527    /// doing the work.
1528    #[must_use]
1529    pub fn listed_runs(&self) -> usize {
1530        self.keyspaces()
1531            .map(|db| db.map().arena().listed_runs())
1532            .sum()
1533    }
1534
1535    /// Arena segments whose pages are real, across every database.
1536    #[must_use]
1537    pub fn segment_count(&self) -> usize {
1538        self.keyspaces()
1539            .map(|db| db.map().arena().resident_segments())
1540            .sum()
1541    }
1542
1543    /// What the connections' read and reply buffers are holding.
1544    #[must_use]
1545    pub fn conn_bytes(&self) -> usize {
1546        self.conn_bytes.load(Relaxed)
1547    }
1548
1549    /// Note that the connections are holding `delta` bytes more than they were,
1550    /// or fewer when it is negative.
1551    ///
1552    /// A delta and not a total because the alternative is a walk over every
1553    /// connection, and the walk would have to happen on a turn of the loop
1554    /// rather than when `INFO` asks, which puts the cost of a report on the
1555    /// command path of a server nobody is asking.
1556    pub fn note_conn_bytes(&self, delta: isize) {
1557        // A read and a write and not a fetch and add, because the number is a
1558        // sum of signed changes and the saturating part has to happen in the
1559        // middle. Two threads that change their buffers in the same instant can
1560        // lose one of the two changes, which is a report that is a few kilobytes
1561        // out until the next connection on either thread moves it again.
1562        self.conn_bytes
1563            .store(self.conn_bytes().saturating_add_signed(delta), Relaxed);
1564    }
1565
1566    /// Keys reclaimed by running into them after their deadline.
1567    #[must_use]
1568    pub fn expired_keys(&self) -> u64 {
1569        self.keyspaces().map(|db| db.expired_keys()).sum()
1570    }
1571
1572    /// Hash fields reclaimed after their own deadline passed.
1573    #[must_use]
1574    pub fn expired_fields(&self) -> u64 {
1575        self.keyspaces().map(|db| db.expired_fields()).sum()
1576    }
1577
1578    /// The share of those the cycle found rather than a command tripping over.
1579    #[must_use]
1580    pub fn expired_fields_active(&self) -> u64 {
1581        self.keyspaces().map(|db| db.expired_fields_active()).sum()
1582    }
1583
1584    /// Keys thrown away to make room, which is the other number entirely.
1585    #[must_use]
1586    pub fn evicted_keys(&self) -> u64 {
1587        self.keyspaces().map(|db| db.evicted_keys()).sum()
1588    }
1589
1590    /// Lookups a client's read made that found the key.
1591    #[must_use]
1592    pub fn keyspace_hits(&self) -> u64 {
1593        self.keyspaces().map(|db| db.hits()).sum()
1594    }
1595
1596    /// Lookups a client's read made that did not.
1597    #[must_use]
1598    pub fn keyspace_misses(&self) -> u64 {
1599        self.keyspaces().map(|db| db.misses()).sum()
1600    }
1601
1602    /// Every command that has been seen, with its counters.
1603    ///
1604    /// Only the ones that have. A server reports a handful of lines rather than
1605    /// one per command in the table, which is what Redis does and is the
1606    /// difference between a section a person can read and one they cannot.
1607    pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
1608        (0..table::count())
1609            .map(|at| (table::name_at(at), self.command_stat(at)))
1610            .filter(|(_, row)| row.seen())
1611    }
1612
1613    /// One command's counters, added up over every thread.
1614    fn command_stat(&self, at: usize) -> CommandStat {
1615        let mut sum = CommandStat::default();
1616        for thread in &self.locals {
1617            let row = &thread.cmdstats.0[at];
1618            sum.calls += row.calls.get();
1619            sum.rejected += row.rejected.get();
1620            sum.failed += row.failed.get();
1621        }
1622        sum
1623    }
1624
1625    /// The counters the calling thread writes into.
1626    ///
1627    /// The first call on a thread claims a set and every call after it is a
1628    /// thread local read and an index. A server asked to count from more threads
1629    /// than it was built for wraps round and shares a set, which loses the odd
1630    /// count between two threads and cannot happen to a server `yodb serve`
1631    /// built, because that one is told how many threads it will have before it
1632    /// starts any of them.
1633    pub fn counted(&self) -> &Stats {
1634        &self.mine().stats
1635    }
1636
1637    /// The next client id, taken.
1638    ///
1639    /// Every accept anywhere on this server comes through here, so no two
1640    /// clients share a number however many threads are accepting.
1641    pub fn next_client(&self) -> u64 {
1642        self.next_client.fetch_add(1, Relaxed)
1643    }
1644
1645    /// Say which handle this server is behind, so a background thread can hold
1646    /// one.
1647    ///
1648    /// Called by whoever wrapped it up, as many times as there are threads, and
1649    /// every call after the first says the same thing. It cannot be worked out
1650    /// from the inside, because a `&Server` has no way to reach the handle it
1651    /// is behind, so whoever made the handle has to say.
1652    pub fn is_behind(self: &Arc<Server>) {
1653        let mut myself = self.myself.lock();
1654        if myself.strong_count() == 0 {
1655            *myself = Arc::downgrade(self);
1656        }
1657    }
1658
1659    /// Put that handle down again, so the server can be reached mutably.
1660    ///
1661    /// `Arc::get_mut` counts weak handles as well as strong ones, so a server
1662    /// that knows what it is behind cannot be borrowed mutably while it knows
1663    /// it. Everything that wants a mutable one is startup, which happens before
1664    /// any thread could be holding the handle, so putting it down and picking it
1665    /// up at the next [`Server::is_behind`] costs nothing and keeps the startup
1666    /// path exactly as it was.
1667    pub fn forget_behind(&self) {
1668        let mut myself = self.myself.lock();
1669        *myself = Weak::new();
1670    }
1671
1672    /// A counted handle on this server, for a thread that outlives its caller.
1673    ///
1674    /// `None` on a server nobody wrapped up, and on one that is being dropped,
1675    /// which is the same answer for the same reason: there is no server here to
1676    /// hand a thread.
1677    #[must_use]
1678    pub(crate) fn myself(&self) -> Option<Arc<Server>> {
1679        self.myself.lock().upgrade()
1680    }
1681
1682    /// Which set of per thread state the calling thread is on.
1683    ///
1684    /// The number a blocked client is filed under, so that the thread holding
1685    /// that client's connection is the one that answers it. Claims a set on the
1686    /// first call the same way [`Server::counted`] does, and gives back the same
1687    /// number every time after.
1688    pub fn my_slot(&self) -> usize {
1689        self.mine_at()
1690    }
1691
1692    /// Everything the calling thread keeps to itself.
1693    fn mine(&self) -> &Local {
1694        &self.locals[self.mine_at()]
1695    }
1696
1697    /// The calling thread's place in `locals`, claiming one if it has none.
1698    ///
1699    /// Wraps round when more threads count here than the server was built for,
1700    /// which shares a set between two threads and loses the odd count. That
1701    /// cannot happen to the server `yodb serve` builds, because it is told how
1702    /// many threads it will have before it starts any of them.
1703    fn mine_at(&self) -> usize {
1704        let mut slot = SLOT.get();
1705        if slot == usize::MAX {
1706            slot = self.claimed.fetch_add(1, Relaxed);
1707            SLOT.set(slot);
1708        }
1709        slot % self.locals.len()
1710    }
1711
1712    /// Every thread's numbers added together, which is what `INFO` reports.
1713    #[must_use]
1714    pub fn totals(&self) -> Totals {
1715        let mut sum = Totals::default();
1716        for thread in &self.locals {
1717            sum.clients += thread.stats.clients.get();
1718            sum.connections += thread.stats.connections.get();
1719            sum.commands += thread.stats.commands.get();
1720        }
1721        sum
1722    }
1723
1724    /// The same numbers kept apart, one entry per thread, in slot order.
1725    ///
1726    /// [`Self::totals`] is the sum and it is the sum that answers how busy the
1727    /// server has been. What it cannot answer is whether the threads are
1728    /// carrying the same load as each other, and on a server where every thread
1729    /// keeps the connections it accepted for as long as they are open, that is
1730    /// a question with real consequences: an uneven split is paid by the clients
1731    /// on the crowded thread and is invisible in every number that adds the
1732    /// threads up first.
1733    ///
1734    /// The length is how many threads the server was built for rather than how
1735    /// many have counted anything, so a thread that has not run a command yet
1736    /// shows as zeroes instead of being missing.
1737    #[must_use]
1738    pub fn per_thread(&self) -> Vec<Totals> {
1739        self.locals
1740            .iter()
1741            .map(|thread| Totals {
1742                clients: thread.stats.clients.get(),
1743                connections: thread.stats.connections.get(),
1744                commands: thread.stats.commands.get(),
1745            })
1746            .collect()
1747    }
1748
1749    /// Put the totals back to zero, which is `CONFIG RESETSTAT`.
1750    ///
1751    /// Every thread's set and not only the one asking, since the number the
1752    /// client is resetting is the sum it was just shown. The open connections
1753    /// are left alone because that is a gauge and not a total: the connections
1754    /// are still open.
1755    pub fn reset_stats(&self) {
1756        for thread in &self.locals {
1757            thread.stats.connections.zero();
1758            thread.stats.commands.zero();
1759        }
1760        // These live on the stripes rather than on the threads, so resetting
1761        // them means holding each stripe for as long as it takes to write a
1762        // handful of zeroes. `CONFIG RESETSTAT` is a command a person types, and
1763        // the alternative is a set of numbers a dashboard cannot put back.
1764        for mut db in self.keyspaces() {
1765            db.zero_stats();
1766        }
1767    }
1768
1769    /// Say how many threads will run commands here, before any of them does.
1770    ///
1771    /// What it changes is how many sets of counters there are, and how many
1772    /// pub/sub mailboxes. Called once at startup by whoever is about to start
1773    /// the threads, and calling it on a running server throws away what has been
1774    /// counted so far, which is why it wants the server to itself.
1775    pub fn set_threads(&mut self, threads: usize) {
1776        self.locals = slots(threads);
1777        self.mail = pubsub::boxes(threads);
1778        self.claimed = AtomicUsize::new(0);
1779    }
1780
1781    /// How many threads will run commands here.
1782    ///
1783    /// The number [`set_threads`](Self::set_threads) was given, and one on a
1784    /// server nobody told, which is what `INFO` and `CONFIG GET io-threads`
1785    /// answer. It counts the threads the server was built for rather than the
1786    /// ones that have accepted a connection, for the same reason
1787    /// [`per_thread`](Self::per_thread) has a row for a thread that has done
1788    /// nothing: a thread that is waiting is still a thread that is there.
1789    #[must_use]
1790    pub fn io_threads(&self) -> usize {
1791        self.locals.len()
1792    }
1793
1794    /// The `maxmemory` limit in bytes, zero when there is not one.
1795    #[must_use]
1796    pub fn maxmemory(&self) -> u64 {
1797        self.maxmemory.load(Relaxed)
1798    }
1799
1800    /// Set the limit, and take a reading straight away.
1801    ///
1802    /// The reading is here rather than left to the next maintenance turn because
1803    /// a client that sets the limit and sends a write in the same batch expects
1804    /// the write to be judged against the limit it just set, and because the
1805    /// cached number is meaningless until the first time there is a limit to
1806    /// compare it with.
1807    ///
1808    /// Turning the limit on also turns on the running total every slab keeps of
1809    /// what its collections hold, and turning it off turns that back off, so a
1810    /// server with no limit is not paying to count something nobody reads. The
1811    /// first reading after switching it on is the walk that the total starts
1812    /// from, and it is the only walk.
1813    pub fn set_maxmemory(&self, bytes: u64) {
1814        self.maxmemory.store(bytes, Relaxed);
1815        for db in &self.dbs {
1816            db.track_memory(bytes != 0);
1817        }
1818        // Every database and not only the ones something has run against, since
1819        // this is the walk the running totals start from and a database that
1820        // takes its last reading from before the limit existed would be a
1821        // database counted at whatever it held then.
1822        self.mine().unmeasure(ALL_DATABASES);
1823        self.used.store(self.settled_memory(), Relaxed);
1824    }
1825
1826    /// Say where a database should get its store from when it needs one.
1827    ///
1828    /// This is what turns the eviction inversion on. Until it is called every
1829    /// database answers a memory limit by evicting, which is Redis, and after it
1830    /// is called a database under memory pressure moves values to whatever the
1831    /// closure hands back instead of throwing keys away.
1832    ///
1833    /// Called at most once per database and only under pressure, so a server
1834    /// that is given a file and never fills memory never touches it.
1835    pub fn set_store_source(
1836        &mut self,
1837        source: impl FnMut(usize) -> Option<Store> + Send + 'static,
1838    ) {
1839        *self.store.lock() = Some(Box::new(source));
1840    }
1841
1842    /// Whether this server has been given somewhere to put cold values.
1843    #[must_use]
1844    pub fn has_store_source(&self) -> bool {
1845        self.store.lock().is_some()
1846    }
1847
1848    /// Open database `at`'s store, if it has not got one and there is one to be
1849    /// had.
1850    ///
1851    /// A store that will not open leaves the database where it was, which is
1852    /// evicting, because a memory limit that cannot be answered by moving data
1853    /// still has to be answered.
1854    fn attach_store(&self, at: usize) {
1855        if self.slot(at).store_bytes().is_some() {
1856            return;
1857        }
1858        // The closure is run with its lock held and the keyspace is taken after
1859        // it has answered, so the file is opened once however many threads asked
1860        // for it and the stripe is not held while a file is being opened.
1861        let mut source = self.store.lock();
1862        let Some(source) = source.as_mut() else {
1863            return;
1864        };
1865        if let Some(blocks) = source(at) {
1866            self.slot(at).attach(blocks);
1867        }
1868    }
1869
1870    /// The `maxstore` limit in bytes, `None` when there is not one.
1871    #[must_use]
1872    pub fn maxstore(&self) -> Option<u64> {
1873        match self.maxstore.load(Relaxed) {
1874            NO_MAXSTORE => None,
1875            bytes => Some(bytes),
1876        }
1877    }
1878
1879    /// Set the storage limit, or clear it with `None`.
1880    ///
1881    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
1882    /// total, because this limit is compared against a number the store keeps
1883    /// and answers on demand, not against a walk.
1884    pub fn set_maxstore(&self, bytes: Option<u64>) {
1885        self.maxstore.store(bytes.unwrap_or(NO_MAXSTORE), Relaxed);
1886    }
1887
1888    /// What every attached store is holding, for `INFO memory`.
1889    ///
1890    /// Zero on a server with nothing attached, which is not the same as a server
1891    /// whose file is empty, and [`Server::regime`] is the field that tells those
1892    /// two apart.
1893    #[must_use]
1894    pub fn store_bytes(&self) -> u64 {
1895        self.keyspaces().filter_map(|db| db.store_bytes()).sum()
1896    }
1897
1898    /// What the file has been asked to do, added up over every database.
1899    ///
1900    /// Counters and not levels, so they only ever go up and a run is the
1901    /// difference between two readings. G9 is a ratio over these: the faults a
1902    /// run took, divided by the point reads it issued, has to come out at 1.05
1903    /// or less with a working set ten times memory. There is no way to work that
1904    /// out from outside the server, so it is reported rather than inferred.
1905    ///
1906    /// A fault is a read that went to the store. Whether it also went to the
1907    /// device depends on the store: a log serves a read out of a resident page
1908    /// without touching anything. At ten times memory almost every fault is a
1909    /// real read, which is why the gate is written against this number, but the
1910    /// two are not the same thing and a run tight against the bar should be
1911    /// checked against what the operating system says.
1912    #[must_use]
1913    pub fn cold_stats(&self) -> yo_kv::tier::Stats {
1914        let mut total = yo_kv::tier::Stats::default();
1915        for db in self.keyspaces() {
1916            let Some(tier) = db.tier() else { continue };
1917            let s = tier.stats();
1918            total.demoted += s.demoted;
1919            total.promoted += s.promoted;
1920            total.faults += s.faults;
1921            total.served += s.served;
1922            total.bytes_out += s.bytes_out;
1923            total.bytes_in += s.bytes_in;
1924        }
1925        total
1926    }
1927
1928    /// Which way this server answers a memory limit, in one word for `INFO`.
1929    ///
1930    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
1931    /// inversion: a memory limit moves values to the file and nothing stored is
1932    /// lost. A server reports one word rather than leaving an operator to work
1933    /// it out from a limit, a setting and whether a file happens to be open.
1934    #[must_use]
1935    pub fn regime(&self) -> &'static str {
1936        if (0..self.slots()).any(|at| self.migrates(at)) {
1937            "migrate"
1938        } else {
1939            "evict"
1940        }
1941    }
1942
1943    /// Whether database `at` answers a memory limit by moving values to the
1944    /// file rather than by throwing keys away.
1945    ///
1946    /// Three things have to hold. There has to be somewhere to move them, which
1947    /// is a store attached to that database or a source that can open one, and
1948    /// on a server that was never given a file this is false everywhere and
1949    /// every database behaves exactly as it did.
1950    /// The storage budget has to be more than nothing, which is what
1951    /// `maxstore 0` says it is not. And the file has to be under that budget,
1952    /// because a full file is a storage limit reached and eviction is the right
1953    /// answer to a storage limit.
1954    fn migrates(&self, at: usize) -> bool {
1955        let cap = self.maxstore();
1956        if cap == Some(0) {
1957            return false;
1958        }
1959        // Out of the stripe first. A match keeps whatever it is looking at
1960        // alive for the whole of itself, and that would be this stripe held
1961        // across the arms for no reason.
1962        let bytes = self.slot(at).store_bytes();
1963        match bytes {
1964            Some(held) => cap.is_none_or(|cap| held < cap),
1965            // Nothing attached, but somewhere to get one from the moment this
1966            // database needs it, which is what makes the answer yes rather than
1967            // no. Opening it here would mean `INFO` opened files.
1968            None => self.store.lock().is_some(),
1969        }
1970    }
1971
1972    /// The reading the shard loop takes, at most once a millisecond.
1973    ///
1974    /// The gate is the whole difference between this and
1975    /// [`Server::refresh_memory`], and it is the same gate
1976    /// [`Server::expire_slice`] puts in front of the expiry sweep. A maintenance
1977    /// turn runs on every batch and a batch is a hundred nanoseconds, so a
1978    /// reading a batch is ten thousand readings a millisecond of a number that
1979    /// moves by what sixty four commands allocated.
1980    ///
1981    /// What a reading that old costs is overshoot, and it is bounded by what a
1982    /// millisecond of writes can allocate. That is far inside the tolerance this
1983    /// number already has: space comes back a segment at a time and a segment is
1984    /// two megabytes, so the limit was never held to closer than that.
1985    ///
1986    /// The case that matters is a server sitting at its limit, and that one is
1987    /// not judged on this reading at all. [`Server::make_room`] takes its own the
1988    /// moment the cached one says the server is over, which is the moment the
1989    /// number has to be exact.
1990    pub fn refresh_memory_slice(&self) {
1991        if self.maxmemory() != 0 && self.mine().measuring(self.clock.now_ms()) {
1992            self.refresh_memory();
1993        }
1994    }
1995
1996    /// Take a fresh memory reading.
1997    ///
1998    /// Nothing at all when there is no limit, which is the default and is every
1999    /// server that has not asked for one.
2000    pub fn refresh_memory(&self) {
2001        if self.maxmemory() != 0 {
2002            let used = self.settled_memory();
2003            self.used.store(used, Relaxed);
2004            // The peak comes along for free here, because the walk that would
2005            // otherwise cost something has already happened. It is the reason a
2006            // server with a limit has a peak that means what it says and a
2007            // server without one has a peak that is only as good as the last
2008            // time somebody asked.
2009            self.peak.fetch_max(used, Relaxed);
2010        }
2011    }
2012
2013    /// [`Server::memory_bytes`], asked the cheap way.
2014    ///
2015    /// Two things make it cheaper and they cut different ways. A database that
2016    /// has been marked is asked only about the collections that could have moved
2017    /// since the last time rather than about everything it holds, which is
2018    /// [`Keyspace::settled_memory_bytes`]. A database that has not been marked is
2019    /// not asked at all and its last reading is used instead, which is what keeps
2020    /// the fifteen empty databases nearly every server has off a path that runs
2021    /// once a batch.
2022    ///
2023    /// One more database is weighed than the mask asked for, round robin, so
2024    /// that a reading cannot be stale for good if something changed a database
2025    /// without saying so.
2026    fn settled_memory(&self) -> usize {
2027        let mine = self.mine();
2028        let marked = mine.to_weigh() | 1u64 << mine.measure_next();
2029        let mut total = self.conn_bytes();
2030        for at in 0..DATABASES {
2031            if marked & (1u64 << at) == 0 {
2032                total += self.db_bytes[at].load(Relaxed);
2033                continue;
2034            }
2035            let db = &self.dbs[at];
2036            let now = (0..db.width())
2037                .map(|i| db.hold_stripe(i).settled_memory_bytes())
2038                .sum::<usize>();
2039            self.db_bytes[at].store(now, Relaxed);
2040            total += now;
2041        }
2042        total
2043    }
2044
2045    /// Make room under the `maxmemory` limit, throwing keys away if that is what
2046    /// it takes. Answers whether there is anything left it could throw away.
2047    ///
2048    /// Redis runs the same thing from `processCommand` before every command and
2049    /// so does this: a client that writes has to be judged at the moment it
2050    /// writes, not a batch later, or the limit is a suggestion.
2051    ///
2052    /// Three things happen in the loop and all three are needed. Eviction picks
2053    /// a key and drops it. Compaction gives the pages back, because dropping a
2054    /// key marks its record dead and returns nothing on its own, so a loop that
2055    /// only evicted would throw the whole keyspace away and watch the number
2056    /// stay where it was. The reading is taken again each time round, because
2057    /// the two of them together are the only thing that moves it.
2058    ///
2059    /// # Why running out of budget is not a no
2060    ///
2061    /// `false` means there was nothing left to evict, which is `noeviction`, or
2062    /// a `volatile` policy on a database where nothing has a deadline, or a
2063    /// keyspace that is already empty. It does not mean the server is still over
2064    /// its limit, and that difference is Redis's: `performEvictions` answers
2065    /// `EVICT_FAIL` only when it has run out of things to delete, and
2066    /// `processCommand` refuses the client on that and on nothing else. Running
2067    /// out of time part way through a job it is doing well comes back as
2068    /// `EVICT_RUNNING` and the command goes through, because a server that is
2069    /// evicting steadily and refusing every write while it does it is worse for
2070    /// the client than a little overshoot.
2071    ///
2072    /// # What the limit is worth
2073    ///
2074    /// Space comes back a segment at a time and a segment is two megabytes, so
2075    /// this holds a server to its limit give or take a segment. A `maxmemory` of
2076    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
2077    /// megabytes is asking for a precision this store does not have.
2078    pub fn make_room(&self) -> bool {
2079        let limit = self.maxmemory();
2080        if limit == 0 || self.used.load(Relaxed) as u64 <= limit {
2081            return true;
2082        }
2083        // The cached reading is a batch old and the batch may have compacted
2084        // since, so take a fresh one before throwing anything away. It is the
2085        // settled reading and not the walk, so what this costs is the handful of
2086        // collections the last batch touched and not the whole database.
2087        let mut used = self.settled_memory();
2088        self.used.store(used, Relaxed);
2089        let mut budget = EVICT_BUDGET;
2090        while used as u64 > limit {
2091            let over = used - limit as usize;
2092            if !self.relieve_step(over) {
2093                return false;
2094            }
2095            self.compact_hard_step();
2096            used = self.settled_memory();
2097            self.used.store(used, Relaxed);
2098            budget -= 1;
2099            if budget == 0 {
2100                break;
2101            }
2102        }
2103        true
2104    }
2105
2106    /// Give back `over` bytes from whichever database can, by moving values to
2107    /// the file where there is one and by throwing keys away where there is not.
2108    ///
2109    /// The two answers are the eviction inversion and which one a database gets
2110    /// is [`Server::migrates`]. Answers whether anything was given back at all,
2111    /// and `false` is what refuses the client's write.
2112    ///
2113    /// A store that will not take the bytes counts as nothing given back, so the
2114    /// write is refused rather than turned into a deletion. A disk that is
2115    /// misbehaving is a reason to stop accepting writes and it is not a reason
2116    /// to start losing data that was accepted already.
2117    ///
2118    /// Round robin from a cursor rather than always starting at database zero,
2119    /// so a server using more than one of them does not empty the first before
2120    /// touching the second. Almost every server is on database zero only, where
2121    /// this is one call that answers and fifteen that say the map is empty.
2122    fn relieve_step(&self, over: usize) -> bool {
2123        let from = self.evict_db.load(Relaxed);
2124        for turn in 0..self.slots() {
2125            let i = (from + turn) % self.slots();
2126            // An empty keyspace has nothing to move and opening a log for one
2127            // would cost a resident page window to find that out.
2128            let used = !self.slot(i).is_empty();
2129            let gave = if used && self.migrates(i) {
2130                self.attach_store(i);
2131                // Whether it made room and not whether it moved a key. A round
2132                // that demoted nothing and handed back a segment is a round
2133                // that made room, and reading only the count refuses the write
2134                // that provoked it.
2135                self.slot(i)
2136                    .relieve(over)
2137                    .is_ok_and(yo_kv::tier::Relief::made_room)
2138            } else {
2139                // Against this database rather than whichever one the write
2140                // that provoked the eviction was aimed at, since the key that
2141                // goes is this one's. The funnel is already armed above and
2142                // this is a second one inside it, which is what the answer
2143                // going back into the drain is for.
2144                let armed = notify::arm(self, self.slot_db(i));
2145                let gone = self.slot(i).evict_one();
2146                notify::drain(self, armed);
2147                gone
2148            };
2149            if gave {
2150                self.evict_db.store((i + 1) % self.slots(), Relaxed);
2151                self.mine().mark(1u64 << self.slot_db(i));
2152                return true;
2153            }
2154        }
2155        false
2156    }
2157
2158    /// The sweep the shard loop calls, at most once a millisecond.
2159    ///
2160    /// The gate is the whole difference between this and [`Server::expire_step`].
2161    /// A maintenance slice runs on every turn of the loop and a turn is a
2162    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
2163    /// thousand times per millisecond and spend a real share of the shard on
2164    /// looking for keys that cannot have died since the last look. Nothing in a
2165    /// database changes fast enough to be worth asking about more often than the
2166    /// clock can tell the difference, and the clock here is milliseconds.
2167    ///
2168    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
2169    /// hertz, so this is not the thing that decides how promptly memory comes
2170    /// back. What it decides is that an idle server sweeps a thousand times a
2171    /// second rather than a million.
2172    pub fn expire_slice(&self, budget: usize) -> usize {
2173        // `DEBUG SET-ACTIVE-EXPIRE 0`, which is what a test that wants to see a
2174        // key that is logically gone but still on the shelf turns off. Read
2175        // before the clock because it is the cheaper of the two and because a
2176        // server with the sweep off should not be paying for the clock either.
2177        if !self.expiring() {
2178            return 0;
2179        }
2180        let now = self.clock.now_ms();
2181        if now == self.expire_ms.load(Relaxed) {
2182            return 0;
2183        }
2184        self.expire_ms.store(now, Relaxed);
2185        self.expire_step(budget)
2186    }
2187
2188    /// Sweep dead keys out of the databases, spending at most `budget` looks.
2189    ///
2190    /// Answers what it spent, so the caller can charge its maintenance slice for
2191    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
2192    ///
2193    /// Round robin from its own cursor, and every database gets offered whatever
2194    /// is left of the budget rather than a sixteenth of it each, so a server on
2195    /// database zero only, which is nearly every server, spends the whole slice
2196    /// where the keys are. The fifteen empty ones cost a comparison apiece
2197    /// because a database with no key carrying a deadline says so without
2198    /// drawing anything.
2199    ///
2200    /// The cursor moves to the database after whichever one did the work, so two
2201    /// busy databases take turns instead of the lower numbered one starving the
2202    /// other.
2203    pub fn expire_step(&self, budget: usize) -> usize {
2204        let slots = self.slots();
2205        let mut spent = 0;
2206        let from = self.expire_db.load(Relaxed);
2207        for turn in 0..slots {
2208            if spent >= budget {
2209                break;
2210            }
2211            let i = (from + turn) % slots;
2212            // Nothing armed this thread, because nothing asked for any of this:
2213            // the shard loop is between commands. So the sweep arms and drains
2214            // around itself, and a key it takes is news to a subscriber in the
2215            // same way a key a lookup took on the way past is.
2216            let armed = notify::arm(self, self.slot_db(i));
2217            // Held once for both cycles rather than taken again for the second.
2218            // Two takes of a stripe lock to ask two questions about the same
2219            // stripe is one more line every other thread has to wait for, and
2220            // this asks on every turn of every worker's loop.
2221            let mut slot = self.slot(i);
2222            let c = slot.expire_cycle(budget - spent);
2223            // And the fields, which are the other thing with a deadline nobody
2224            // is waiting on. It draws from its own list and charges the same
2225            // budget, so a database with no hash field deadlines anywhere pays a
2226            // comparison for it and a database full of them cannot starve the
2227            // key sweep.
2228            let left = (budget - spent).saturating_sub(c.examined);
2229            let fields = slot.field_expire_cycle(left);
2230            drop(slot);
2231            notify::drain(self, armed);
2232            // The same deletions a lookup's would be, from the other end of the
2233            // same hook. A replica hears about a key the sweep took exactly as
2234            // it hears about one a `GET` took.
2235            repl::swept(self, self.slot_db(i));
2236            spent += c.examined + fields;
2237            if c.expired > 0 {
2238                self.expire_db.store((i + 1) % slots, Relaxed);
2239                self.mine().note(1u64 << self.slot_db(i));
2240                // Keys the sweep took are bytes the database no longer holds,
2241                // and nothing else is going to say so: no command ran.
2242                self.mine().unmeasure(1u64 << self.slot_db(i));
2243            }
2244        }
2245        spent
2246    }
2247
2248    /// One slice of compaction for a server that is over its limit.
2249    ///
2250    /// Round robin the way [`Server::compact_step`] is, from its own cursor
2251    /// rather than that one's, and it stops at the first database that had
2252    /// something to move and asks with the ratios off. See
2253    /// [`Keyspace::compact_hard`] for what that changes.
2254    fn compact_hard_step(&self) -> Option<usize> {
2255        let from = self.next_db.load(Relaxed);
2256        for turn in 0..self.slots() {
2257            let i = (from + turn) % self.slots();
2258            if let Some(moved) = self.slot(i).compact_hard() {
2259                self.next_db.store((i + 1) % self.slots(), Relaxed);
2260                // A segment handed back is the whole point of the call, and
2261                // `make_room` reads the total again on the next turn of its loop
2262                // to find out whether it worked.
2263                self.mine().unmeasure(1u64 << self.slot_db(i));
2264                return Some(moved);
2265            }
2266        }
2267        None
2268    }
2269
2270    /// Take what every thread has marked and add it to the turn's own mask.
2271    ///
2272    /// The mask the turn works from is its own and not a shared one, because a
2273    /// mask it read in place and then cleared a bit of would be a mask that lost
2274    /// whatever another thread marked in between. A swap cannot lose a mark: a
2275    /// thread that ors while the swap happens either gets its bit in before the
2276    /// swap or leaves it there afterwards, and the second one costs one look at
2277    /// a database the turn has already been through.
2278    fn collect_marks(&self) {
2279        let mut marked = 0;
2280        for thread in &self.locals {
2281            marked |= thread.dirty.swap(0, Relaxed);
2282        }
2283        let mine = self.mine();
2284        mine.note(marked);
2285        // The other half of what the marks are for. A thread weighs the
2286        // databases its own commands touched on every reading it takes, and this
2287        // is where it hears about the ones somebody else touched. Oring in a bit
2288        // it has already weighed costs one database on one reading, which is why
2289        // this can share a mask that was collected for something else.
2290        mine.unmeasure(marked);
2291    }
2292
2293    /// Give one database's dead space back, if any database has enough of it to
2294    /// be worth the move. `None` when no database had a candidate.
2295    ///
2296    /// Once per batch, next to the clock. Overwriting a key writes a new record
2297    /// and counts the old one dead, so without this a server holds everything
2298    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
2299    /// a key against Redis at 144 for the same load, and the whole difference
2300    /// was dead records nothing ever came back for.
2301    ///
2302    /// At most one segment moves per call and the search starts one database
2303    /// further along each time, so the cost of asking is a comparison per
2304    /// database and the cost of acting is bounded by a segment.
2305    pub fn compact_step(&self) -> Option<usize> {
2306        // `DEBUG DICT-RESIZING 0`. On a real server that stops a dictionary
2307        // giving back the room it grew into, and this is where the same thing
2308        // happens here: the arena keeps every segment it has taken until this
2309        // walks over and hands one back.
2310        if !self.resizing() {
2311            return None;
2312        }
2313        let slots = self.slots();
2314        let looks = COMPACT_LOOKS.min(slots);
2315        // Once a millisecond per thread rather than once a batch, because the
2316        // swap is over every thread's counter and a call per batch per worker is
2317        // the thread count squared per batch across the server. A mark a
2318        // millisecond old is still a database somebody wrote to, which is the
2319        // only thing the mask is ever asked.
2320        if self.mine().collecting(self.clock.now_ms()) {
2321            self.collect_marks();
2322        }
2323        let mine = self.mine();
2324        // This thread's cursor and not the server's. The load and the store
2325        // either side of this walk happen after every batch on every thread,
2326        // and on the server's cursor that is one line every thread is writing
2327        // to at batch rate for no reason other than to say where to start.
2328        let from = mine.compact_db.load(Relaxed);
2329        for turn in 0..looks {
2330            let i = (from + turn) % slots;
2331            // Nothing has run against this database since it last said it had
2332            // nothing to collect, so it still has nothing to collect and the
2333            // line it lives on stays where it is.
2334            let at = self.slot_db(i);
2335            if !mine.wanted(at) {
2336                continue;
2337            }
2338            if let Some(moved) = self.slot(i).compact_step() {
2339                mine.compact_db.store((i + 1) % slots, Relaxed);
2340                // The same as the hard step: the segment it gave back is memory
2341                // the next reading would otherwise still be counting.
2342                mine.unmeasure(1u64 << at);
2343                return Some(moved);
2344            }
2345            // Only once every stripe of the database has said it has nothing,
2346            // since the bit is per database and one stripe answering for all of
2347            // them would stop the others being asked at all.
2348            if i % self.width == self.width - 1 {
2349                mine.done(at);
2350            }
2351        }
2352        mine.compact_db.store((from + looks) % slots, Relaxed);
2353        None
2354    }
2355}
2356
2357impl Server {
2358    /// Whether anybody is watching anything.
2359    ///
2360    /// The one thing every write asks about watches, and it is a relaxed load of
2361    /// a word that is zero and shared on a server where no client has ever sent
2362    /// `WATCH`. Relaxed is enough because the answer only has to be right by the
2363    /// time it matters: a `WATCH` that has not been published yet has not
2364    /// returned to its client either, so no client can have started a
2365    /// transaction that depends on it.
2366    fn watching(&self) -> bool {
2367        self.watched.load(Relaxed) != 0
2368    }
2369
2370    /// Which classes of keyspace notification are turned on.
2371    ///
2372    /// Zero is off, which is the default and is what nearly every server runs
2373    /// with. Relaxed for the same reason the watch count is: a `CONFIG SET` that
2374    /// has not been published to another thread yet has not answered its client
2375    /// either.
2376    pub(crate) fn notify_flags(&self) -> u32 {
2377        self.notify.load(Relaxed)
2378    }
2379
2380    /// Turn a set of notification classes on, or turn them all off with zero.
2381    pub(crate) fn set_notify_flags(&self, flags: u32) {
2382        self.notify.store(flags, Relaxed);
2383    }
2384
2385    /// Note how many watched keys there are, after the table changed.
2386    ///
2387    /// Taken from the table under the same lock the change was made under, so
2388    /// the count can never say nobody is watching while somebody is.
2389    fn recount(&self, watches: &Watches) {
2390        self.watched.store(watches.len(), Relaxed);
2391    }
2392}
2393
2394impl Default for Server {
2395    fn default() -> Server {
2396        Server::new()
2397    }
2398}
2399
2400/// What one connection has chosen.
2401pub struct Session {
2402    db: usize,
2403    id: u64,
2404    /// Which connection slot on the front this session belongs to.
2405    ///
2406    /// Carried here so that a command can say where a reply for this connection
2407    /// goes without the front having to be asked. Pub/sub is what needs it: a
2408    /// subscription is a row on the server naming a slot, and the subscribe
2409    /// command is the only moment the connection and the server are both in
2410    /// hand. [`u32::MAX`] for a session that is not on a front, which is a test.
2411    conn: u32,
2412    name: Vec<u8>,
2413    /// The `HIMPORT` fieldsets this connection has prepared.
2414    ///
2415    /// Connection state and not keyspace state, which is the reference's design
2416    /// and not a shortcut: a fieldset is invisible to every other connection and
2417    /// the keys built from one outlive it.
2418    sets: himport::Fieldsets,
2419    /// Whether the command running right now was called by a script.
2420    ///
2421    /// The one thing it changes is what a blocking command does when it finds
2422    /// nothing to take. A client that sent `BLPOP` waits; a script that called
2423    /// `BLPOP` cannot, because the whole server is waiting on the script, and a
2424    /// script that parked would park everything behind it. So inside a script a
2425    /// blocking command times out at once and answers the null a client that
2426    /// waited its full timeout would have got. That is a real server's rule and
2427    /// it is why `BLPOP` is not on the list a script may not call.
2428    scripted: bool,
2429    /// The commands held since `MULTI`, `None` when no transaction is open.
2430    ///
2431    /// Connection state and nothing else. A transaction is invisible to every
2432    /// other connection until `EXEC` runs it, and a connection that goes away
2433    /// with one open has simply not run it.
2434    multi: Option<multi::Queue>,
2435    /// What this connection asked `WATCH` about, and what those keys looked
2436    /// like at the time.
2437    ///
2438    /// The other half is on the server, beside the keys, because a write by
2439    /// another thread has to reach it. See `multi` for why keeping the value
2440    /// here and comparing it at `EXEC` is not the same thing.
2441    watching: Vec<multi::Watched>,
2442    /// Whether the command running right now was handed over by `EXEC`.
2443    ///
2444    /// The one thing it changes is the RESP2 subscribe mode refusal, which a
2445    /// real server makes in `processCommand` and so does not make for a command
2446    /// that was queued: `MULTI`, `SUBSCRIBE z`, `GET x`, `EXEC` runs the `GET`
2447    /// on 8.10.1 even though sending it on its own would have been refused.
2448    running: bool,
2449    /// The buffer `EXEC` decodes the queued commands through.
2450    ///
2451    /// It lives here rather than in `exec` so that its capacity survives the
2452    /// transaction. A fresh one has no room for spans, so the first command of
2453    /// every transaction would allocate, and a client that runs transactions in
2454    /// a loop would be allocating on a command path forever. Everywhere else
2455    /// the buffer belongs to the connection already and the same reserve is
2456    /// free after the first command.
2457    replay: crate::request::Argv,
2458    /// What this connection has subscribed to, `None` until it subscribes to
2459    /// anything.
2460    ///
2461    /// Boxed so that a connection that never subscribes carries a null pointer
2462    /// rather than three empty vectors. The other half is on the server, keyed
2463    /// by name, because a publish arrives on a connection that cannot see this
2464    /// one. See the `pubsub` module.
2465    subs: Option<Box<pubsub::Subs>>,
2466    /// The library name and version a client library announces with
2467    /// `CLIENT SETINFO`, empty when it has not.
2468    ///
2469    /// Nothing on the server reads them. They are here because an operator
2470    /// looking at `CLIENT LIST` on a server with a hundred connections wants to
2471    /// know which of them is the Python worker and which is the dashboard, and
2472    /// every mainstream client library sends them on connect.
2473    lib_name: Vec<u8>,
2474    lib_ver: Vec<u8>,
2475    /// `CLIENT NO-EVICT`, which asks that this connection's buffers are not the
2476    /// ones given up when the server is short of memory.
2477    ///
2478    /// Nothing gives up a connection's buffers here yet, so this is remembered
2479    /// and reported and does nothing else, which is the honest half of the
2480    /// command: a client that sets it and reads it back sees what it set.
2481    no_evict: bool,
2482    /// `CLIENT NO-TOUCH`, which asks that reads by this connection do not move
2483    /// a key's place in the eviction order.
2484    no_touch: bool,
2485    /// What this connection has asked to be told about, which is `CLIENT REPLY`.
2486    reply: Reply,
2487    /// The row every other thread sees this connection through.
2488    ///
2489    /// Shared rather than owned, because `CLIENT LIST` and `CLIENT KILL` run on
2490    /// whichever thread the client asking is on and that is very often not this
2491    /// one. Everything the report says about the socket lives in there and
2492    /// nowhere else, and the handful of things the session needs for itself are
2493    /// kept here as well and written to both. See the `clients` module for why
2494    /// the row is words and a small lock rather than one lock.
2495    sock: Arc<Client>,
2496    /// Whether this connection has got past the password, if there is one.
2497    ///
2498    /// Decided when the connection is accepted and not when it first sends
2499    /// something, which is what makes `CONFIG SET requirepass` leave the clients
2500    /// that are already connected alone. False here rather than true because a
2501    /// session nobody told is a session on a server nobody gave a password to,
2502    /// and the gate only reads this when there is one. See the `auth` module.
2503    authenticated: bool,
2504    /// Which user this connection is, and the copy of it its commands are
2505    /// checked against.
2506    ///
2507    /// Boxed because it is three allocations and a connection on a server that
2508    /// has no ACL never reads past the first field of it. See the `acl` module
2509    /// for why a copy rather than a lookup.
2510    acl: Box<acl::Identity>,
2511    /// Whether what this session runs arrived from a master this server is
2512    /// following.
2513    ///
2514    /// False on every connection anybody made, which is what keeps this to a
2515    /// field read on the command path. It exempts the master's stream from the
2516    /// three refusals that are about clients and not about it, being the
2517    /// password, the access control list and the read only refusal, and from
2518    /// `CLIENT PAUSE`. See the `follow` module for why each of those.
2519    master: bool,
2520    /// Whether the command running right now was preceded by `ASKING`.
2521    ///
2522    /// It is what lets a client reach a key in a slot this node is receiving and
2523    /// does not own yet, and it lasts exactly one command, which is what makes
2524    /// it safe: a client that has been told to ask over here says so again for
2525    /// every command it sends, and a client that has not cannot stumble into a
2526    /// half moved slot.
2527    ///
2528    /// Two fields for a one command life, the same pair `CLIENT REPLY SKIP`
2529    /// uses, because the flag has to be set by a command that has not finished
2530    /// and read by the next one. `ASKING` sets the second and the end of every
2531    /// command moves the second into the first.
2532    asking: bool,
2533    asking_next: bool,
2534    /// Whether this connection is another node of the cluster rather than a
2535    /// client.
2536    ///
2537    /// Set by `AUTH "internal connection" <secret>`, where the secret is the
2538    /// forty characters the bus has gossiped the whole cluster onto, so a client
2539    /// cannot set it without already knowing something only the nodes know. What
2540    /// it opens is the slot migration protocol, which changes state a client has
2541    /// no business changing and which is deliberately not guarded against being
2542    /// driven out of order, since the only thing that ever drives it is another
2543    /// node following the same state machine.
2544    internal: bool,
2545    /// Whether the socket goes as soon as the reply to the command running right
2546    /// now has been written, which is the reference's `CLIENT_CLOSE_AFTER_REPLY`.
2547    ///
2548    /// One command sets it, which is a `CLUSTER SYNCSLOTS` from a connection
2549    /// that is not a node. The refusal on its own would be enough to be correct
2550    /// and the hang up is what makes it expensive to sit there guessing.
2551    closing: bool,
2552    /// Which node is on the other end of this connection, empty until one says.
2553    ///
2554    /// Only a node ever says, with `CLUSTER SYNCSLOTS CONF NODE-ID`, and the only
2555    /// thing that reads it is the slot migration that follows on the same
2556    /// connection: the node asking for a slot range is the node the range is
2557    /// going to, and there is nothing else on the connection that says who that
2558    /// is. See the `cluster` module.
2559    node_id: Vec<u8>,
2560}
2561
2562/// What a connection has asked to hear back, which is `CLIENT REPLY`.
2563///
2564/// The two skipping states are one command apart on purpose. `CLIENT REPLY
2565/// SKIP` says nothing itself and skips the reply of the command after it, so
2566/// the state has to survive one command and no more, and the way Redis does
2567/// that is with a pair of flags that step forward once a command.
2568#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
2569pub enum Reply {
2570    /// Everything, which is where every connection starts.
2571    #[default]
2572    On,
2573    /// Nothing at all until the client says `ON` again.
2574    Off,
2575    /// Nothing for the command after this one.
2576    SkipNext,
2577    /// This is that command.
2578    SkipNow,
2579}
2580
2581impl Session {
2582    /// A new connection, on database zero with no name.
2583    #[must_use]
2584    pub fn new(id: u64) -> Session {
2585        Session {
2586            db: 0,
2587            id,
2588            conn: u32::MAX,
2589            name: Vec::new(),
2590            sets: himport::Fieldsets::default(),
2591            scripted: false,
2592            multi: None,
2593            watching: Vec::new(),
2594            running: false,
2595            replay: crate::request::Argv::new(),
2596            subs: None,
2597            lib_name: Vec::new(),
2598            lib_ver: Vec::new(),
2599            no_evict: false,
2600            no_touch: false,
2601            reply: Reply::On,
2602            sock: Arc::new(Client::new(id)),
2603            authenticated: false,
2604            master: false,
2605            asking: false,
2606            asking_next: false,
2607            internal: false,
2608            closing: false,
2609            node_id: Vec::new(),
2610            acl: Box::default(),
2611        }
2612    }
2613
2614    /// Say whether this connection starts out past the password.
2615    ///
2616    /// Called once by whoever accepted it, which is the one place that can see
2617    /// both the connection and the server. A connection nobody tells is
2618    /// unauthenticated and gets through anyway on a server with no password,
2619    /// which is every embedded caller and every test.
2620    pub fn admit(&mut self, yes: bool) {
2621        self.authenticated = yes;
2622    }
2623
2624    /// Whether this connection has got past the password.
2625    #[must_use]
2626    pub(crate) fn authenticated(&self) -> bool {
2627        self.authenticated
2628    }
2629
2630    /// Say that everything this session runs comes from a master.
2631    ///
2632    /// Called once, by the replica link, which is the only thing that can say
2633    /// it. A session nobody tells is an ordinary client, which is every
2634    /// connection on every server that is nobody's replica.
2635    pub(crate) fn serve_master(&mut self, yes: bool) {
2636        self.master = yes;
2637    }
2638
2639    /// Whether what this session runs came from a master.
2640    #[must_use]
2641    pub(crate) fn serving_master(&self) -> bool {
2642        self.master
2643    }
2644
2645    /// Say whether this connection is another node of the cluster.
2646    ///
2647    /// The one thing that says yes is `AUTH "internal connection"` with the
2648    /// right secret, and `DEBUG MARK-INTERNAL-CLIENT` says it too so that a test
2649    /// can drive the protocol without a second node.
2650    pub(crate) fn serve_internal(&mut self, yes: bool) {
2651        self.internal = yes;
2652    }
2653
2654    /// Whether this connection is another node of the cluster.
2655    ///
2656    /// A master's own stream counts as one, because everything a replica is told
2657    /// by its master is by definition from a node, which is the reference's rule
2658    /// as well.
2659    #[must_use]
2660    pub(crate) fn internal(&self) -> bool {
2661        self.internal || self.master
2662    }
2663
2664    /// Say which node is on the other end, which only a node ever does.
2665    pub(crate) fn set_node_id(&mut self, id: &[u8]) {
2666        yo_alloc::allow(|| {
2667            self.node_id.clear();
2668            self.node_id.extend_from_slice(id);
2669        });
2670    }
2671
2672    /// Which node is on the other end, empty for every connection a client made.
2673    #[must_use]
2674    pub(crate) fn node_id(&self) -> &[u8] {
2675        &self.node_id
2676    }
2677
2678    /// Ask that the socket goes once the reply being written has gone out.
2679    pub(crate) fn hang_up(&mut self) {
2680        self.closing = true;
2681    }
2682
2683    /// Whether it has been asked.
2684    #[must_use]
2685    pub(crate) fn hanging_up(&self) -> bool {
2686        self.closing
2687    }
2688
2689    /// Let the command after this one into a slot this node is receiving, which
2690    /// is what `ASKING` does and it lasts exactly that one command.
2691    pub(crate) fn ask_next(&mut self) {
2692        self.asking_next = true;
2693    }
2694
2695    /// The row every other thread sees this connection through.
2696    ///
2697    /// Handed to the server once, when the connection is accepted, so that
2698    /// `CLIENT LIST` can find it. A session nobody hands over is one no other
2699    /// thread can see, which is every embedded caller and every test.
2700    #[must_use]
2701    pub fn row(&self) -> &Arc<Client> {
2702        &self.sock
2703    }
2704
2705    /// Say when this connection was opened, which is what `age` counts from.
2706    ///
2707    /// Called by whoever opened it, which is the only place that knows. A
2708    /// session nobody tells has no age and reports zero, which is every
2709    /// embedded caller and every test.
2710    pub fn opened(&mut self, now_ms: u64) {
2711        self.sock.since_ms.store(now_ms, Relaxed);
2712        self.sock.last_ms.store(now_ms, Relaxed);
2713    }
2714
2715    /// Say what the socket under this connection is.
2716    ///
2717    /// Called once, by whoever accepted it, which is the only place that knows.
2718    /// The two addresses are already in the spelling `CLIENT INFO` reports them
2719    /// in, because turning a socket address into that spelling is the job of the
2720    /// layer that has the socket.
2721    pub fn set_socket(&mut self, peer: &str, local: &str, fd: i32, unix: bool) {
2722        yo_alloc::allow(|| {
2723            let mut text = self.sock.text.lock();
2724            text.peer.clear();
2725            text.peer.extend_from_slice(peer.as_bytes());
2726            text.local.clear();
2727            text.local.extend_from_slice(local.as_bytes());
2728        });
2729        self.sock.fd.store(fd, Relaxed);
2730        self.sock.set_flag(clients::UNIX, unix);
2731    }
2732
2733    /// Note bytes that arrived, and that a read carried them.
2734    pub fn read_bytes(&mut self, n: usize) {
2735        let row = &self.sock;
2736        row.net_in
2737            .store(row.net_in.load(Relaxed) + n as u64, Relaxed);
2738        row.reads.store(row.reads.load(Relaxed) + 1, Relaxed);
2739    }
2740
2741    /// Note bytes that went out.
2742    pub fn wrote_bytes(&mut self, n: usize) {
2743        let row = &self.sock;
2744        row.net_out
2745            .store(row.net_out.load(Relaxed) + n as u64, Relaxed);
2746    }
2747
2748    /// Note what the two buffers are holding, and which protocol they are in.
2749    ///
2750    /// `waiting` is the framed bytes that have not been read yet, `room` is what
2751    /// is left in the read buffer after them, `held` is what the reply buffer
2752    /// still owes and `reply` is its capacity. The high water mark is kept here
2753    /// rather than by the caller so that the caller only has to say what is true
2754    /// now.
2755    pub fn note_buffers(&mut self, waiting: usize, room: usize, held: usize, reply: usize) {
2756        let row = &self.sock;
2757        row.qbuf.store(waiting as u64, Relaxed);
2758        row.qbuf_free.store(room as u64, Relaxed);
2759        row.obl.store(held as u64, Relaxed);
2760        row.rbs.store(reply as u64, Relaxed);
2761        row.rbp
2762            .store(row.rbp.load(Relaxed).max(reply as u64), Relaxed);
2763    }
2764
2765    /// Note which protocol this connection is being answered in.
2766    ///
2767    /// Written after each command rather than with the buffers, because `HELLO`
2768    /// changes it in the reply buffer and a connection that switched to RESP3
2769    /// halfway through a pipeline should be listed as being on it.
2770    pub fn note_proto(&mut self, version: i64) {
2771        self.sock.resp.store(version as u32, Relaxed);
2772    }
2773
2774    /// Note which command is running, before it runs.
2775    ///
2776    /// The clock is passed in because the session has no way to reach one, and
2777    /// the caller is holding the server anyway. `at` is where the command is in
2778    /// the table, since an index is a word another thread can read and a name is
2779    /// not.
2780    pub(crate) fn ran(&mut self, at: usize, sub: Option<&[u8]>, argv: u64, now_ms: u64) {
2781        self.sock.last_ms.store(now_ms, Relaxed);
2782        self.sock.argv_mem.store(argv, Relaxed);
2783        self.sock.note_command(at, sub);
2784    }
2785
2786    /// Note that the command running is over, and put what it changed about this
2787    /// connection where another thread can see it.
2788    ///
2789    /// The count goes up here and not where the command name is noted, so that
2790    /// a connection asking `CLIENT INFO` is told how many commands it had sent
2791    /// before this one. That is what a real server answers: it counts in
2792    /// `commandProcessed` and that runs after the body.
2793    ///
2794    /// The rest is the publishing. Which database a connection is in, what it is
2795    /// subscribed to, whether it is in a transaction and how many keys it is
2796    /// watching are all things a command can have just changed, and they are all
2797    /// things `CLIENT LIST` on another thread reports. Rather than hunting down
2798    /// every command that can move one of them, all six are written out here,
2799    /// which is six ordinary stores to a line this thread already owns.
2800    pub fn finished(&mut self) {
2801        // One command's worth of `ASKING` steps forward here, which is where a
2802        // real server clears its flag: in `resetClient`, after the body, and
2803        // only for a command that was not `ASKING` itself.
2804        self.asking = core::mem::take(&mut self.asking_next);
2805        let (sub, psub, ssub) = self.sub_counts();
2806        let (multi, multi_mem) = self.queued();
2807        let subscribed = self.subscribed();
2808        let in_multi = self.in_multi();
2809        let watching = self.watching.len();
2810        let db = self.db;
2811        let row = &self.sock;
2812        row.cmds.store(row.cmds.load(Relaxed) + 1, Relaxed);
2813        row.db.store(db as u32, Relaxed);
2814        row.sub.store(sub as u32, Relaxed);
2815        row.psub.store(psub as u32, Relaxed);
2816        row.ssub.store(ssub as u32, Relaxed);
2817        row.watch.store(watching as u32, Relaxed);
2818        row.multi.store(multi, Relaxed);
2819        row.multi_mem.store(multi_mem, Relaxed);
2820        row.set_flag(clients::SUBSCRIBED, subscribed);
2821        row.set_flag(clients::IN_MULTI, in_multi);
2822    }
2823
2824    /// What this connection has asked to hear back.
2825    #[must_use]
2826    pub const fn reply_mode(&self) -> Reply {
2827        self.reply
2828    }
2829
2830    /// Step the skipping state on by one command.
2831    ///
2832    /// Called after every command by whoever is deciding whether to keep the
2833    /// reply, so that `SKIP` covers exactly the one command after it.
2834    pub const fn step_reply(&mut self) {
2835        self.reply = match self.reply {
2836            Reply::SkipNext => Reply::SkipNow,
2837            Reply::SkipNow => Reply::On,
2838            other => other,
2839        };
2840    }
2841
2842    /// Whether a script is what is asking, which only a blocking command reads.
2843    pub(crate) const fn scripted(&self) -> bool {
2844        self.scripted
2845    }
2846
2847    /// Whether `EXEC` is what is asking.
2848    pub(crate) const fn running(&self) -> bool {
2849        self.running
2850    }
2851
2852    /// Whether this connection has sent `MONITOR` and stopped being a client.
2853    ///
2854    /// Read off the row rather than kept beside it, so there is one answer to
2855    /// the question and not two that could disagree. The row is a line this
2856    /// session has already touched by the time anything asks, since noting the
2857    /// command it is running writes to it.
2858    pub(crate) fn monitoring(&self) -> bool {
2859        self.sock.flag(clients::MONITOR)
2860    }
2861
2862    /// Whether this connection has sent `PSYNC` and stopped being a client.
2863    ///
2864    /// Off the row for the same reason the question above it is, and read on the
2865    /// way out rather than on the way in: a replica does keep sending commands,
2866    /// `REPLCONF ACK` once a second forever, and what changes is that none of
2867    /// them is answered.
2868    pub(crate) fn replicating(&self) -> bool {
2869        self.sock.flag(clients::REPLICA)
2870    }
2871
2872    /// Say which connection slot this session is in.
2873    ///
2874    /// Called by the front when it opens the connection, which is the only place
2875    /// that knows. A session nobody tells is not on a front, and the one thing
2876    /// that reads this checks the client id before it acts on it.
2877    pub(crate) fn set_conn(&mut self, conn: u32) {
2878        self.conn = conn;
2879        self.sock.conn.store(conn, Relaxed);
2880    }
2881
2882    /// The connection id, which `HELLO` reports and `CLIENT` will.
2883    #[must_use]
2884    pub const fn id(&self) -> u64 {
2885        self.id
2886    }
2887
2888    /// Which database this connection is working in.
2889    #[must_use]
2890    pub const fn db(&self) -> usize {
2891        self.db
2892    }
2893
2894    /// The name the client gave itself, empty if it gave none.
2895    #[must_use]
2896    pub fn name(&self) -> &[u8] {
2897        &self.name
2898    }
2899
2900    /// Put everything back the way it was when the connection was opened.
2901    ///
2902    /// The protocol is not here because it is not here: it lives in the reply
2903    /// buffer, and `RESET` sets it back there.
2904    pub fn reset(&mut self) {
2905        self.db = 0;
2906        self.name.clear();
2907        self.sock.set_text(|text| &mut text.name, b"");
2908        // `SELECT` leaves these alone and `RESET` does not, both checked
2909        // against 8.10.1, which is the one pair of answers you could not guess
2910        // from what the command is for.
2911        self.sets.clear();
2912        // The three `CLIENT` settings that are a choice about this connection go
2913        // back to their defaults, and the library name and version stay, since
2914        // the library behind the socket is the same library it was. Both halves
2915        // are `clearClientConnectionState`'s.
2916        self.reply = Reply::On;
2917        self.set_no_evict(false);
2918        self.set_no_touch(false);
2919        // Back on the default user, whatever it had authenticated as. The
2920        // password half of that is the caller's, because only it can see the
2921        // server and know whether there is one to ask for.
2922        self.forget_user();
2923    }
2924
2925    /// Record the name from `HELLO ... SETNAME` or `CLIENT SETNAME`.
2926    fn set_name(&mut self, name: &[u8]) {
2927        yo_alloc::allow(|| {
2928            self.name.clear();
2929            self.name.extend_from_slice(name);
2930        });
2931        self.sock.set_text(|text| &mut text.name, name);
2932    }
2933
2934    /// Record what `CLIENT SETINFO LIB-NAME` was told.
2935    fn set_lib_name(&mut self, value: &[u8]) {
2936        yo_alloc::allow(|| {
2937            self.lib_name.clear();
2938            self.lib_name.extend_from_slice(value);
2939        });
2940        self.sock.set_text(|text| &mut text.lib_name, value);
2941    }
2942
2943    /// Record what `CLIENT SETINFO LIB-VER` was told.
2944    fn set_lib_ver(&mut self, value: &[u8]) {
2945        yo_alloc::allow(|| {
2946            self.lib_ver.clear();
2947            self.lib_ver.extend_from_slice(value);
2948        });
2949        self.sock.set_text(|text| &mut text.lib_ver, value);
2950    }
2951
2952    /// Record `CLIENT NO-EVICT`.
2953    fn set_no_evict(&mut self, on: bool) {
2954        self.no_evict = on;
2955        self.sock.set_flag(clients::NO_EVICT, on);
2956    }
2957
2958    /// Record `CLIENT NO-TOUCH`.
2959    fn set_no_touch(&mut self, on: bool) {
2960        self.no_touch = on;
2961        self.sock.set_flag(clients::NO_TOUCH, on);
2962    }
2963}
2964
2965/// Give back everything a connection was holding on the server.
2966///
2967/// The transaction, the watches, the subscriptions and the monitor, and it is
2968/// here rather than in [`Session::reset`] because letting go of any of the four
2969/// is a change to the server. A `Session` on its own cannot reach one, and a
2970/// connection that dropped its lists without saying so would leave rows nobody
2971/// is watching, subscriptions nobody is listening to and a monitor nobody is
2972/// reading, which would keep every write, every publish and every command on the
2973/// server paying for clients that are not there.
2974pub fn forget_session(server: &Server, session: &mut Session) {
2975    multi::release(server, session);
2976    pubsub::release(server, session);
2977    if session.monitoring() {
2978        server.watch_no_more(session.row());
2979    }
2980    if session.replicating() {
2981        server.drop_replica(session.row().id);
2982    }
2983    // A slot migration this connection was one of the two halves of cannot go on
2984    // without it, and half a slot range on the far side is the one outcome
2985    // nobody may be left with.
2986    if session.internal() {
2987        server.asm_forget(session.row().id);
2988    }
2989}
2990
2991/// Run one command and write its reply.
2992///
2993/// The name is looked up and the arity is checked here, once, so that no body
2994/// has to. Everything after that is the command's own.
2995pub fn execute(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
2996    // The decoder never produces a command with no name. If one ever arrives,
2997    // it is not something to answer.
2998    if args.is_empty() {
2999        return Flow::Continue;
3000    }
3001    let flow = resolved(server, session, lookup(args.name()), args, out);
3002    // The engine does this itself, after the reply has been decided, because it
3003    // is also what settles `CLIENT REPLY`. An embedded caller has no engine, so
3004    // it happens here instead, and the two paths never both run: the engine
3005    // reaches the funnel through `resolved` and not through this.
3006    //
3007    // Not for a command the pause held, because that command has not run and is
3008    // going to be run again. An embedded caller has nowhere to park it, so it
3009    // gets the answer back and decides for itself; a caller that has not paused
3010    // its own server, which is nearly all of them, never sees this.
3011    if flow != Flow::Hold {
3012        session.finished();
3013    }
3014    flow
3015}
3016
3017/// The commands that are a container for a set of subcommands.
3018///
3019/// A hand written list because the table has one row per container and none per
3020/// subcommand, so there is nothing to ask. It goes away with D-114, which gives
3021/// every subcommand a row of its own and makes this a flag on the container.
3022const CONTAINERS: [&str; 13] = [
3023    "acl", "backup", "client", "cluster", "command", "config", "function", "memory", "object",
3024    "pubsub", "script", "xgroup", "xinfo",
3025];
3026
3027/// The subcommand a container command was given, for the `cmd` field of
3028/// `CLIENT INFO`, which reads `client|info` and not `client`.
3029///
3030/// `None` for everything else, and for a container called with nothing after
3031/// it, which is a wrong arity and has no subcommand to name.
3032fn container_sub<'a>(spec: &Spec, args: &Args<'a>) -> Option<&'a [u8]> {
3033    (args.len() > 1 && CONTAINERS.contains(&spec.name)).then(|| args.get(1))
3034}
3035
3036/// The six commands Redis marks `may-replicate` and does not mark `write`.
3037///
3038/// A short list rather than a flag on every row, because six is what it is and
3039/// the only thing that asks is the pause gate below. It goes away with the flag
3040/// if anything else ever needs the same question answered.
3041const MAY_REPLICATE: [&str; 6] = ["eval", "evalsha", "fcall", "pfcount", "publish", "spublish"];
3042
3043/// Whether `CLIENT PAUSE WRITE` holds this command.
3044///
3045/// The writes, the six above, and `EXEC` when the transaction it is about to run
3046/// holds one of either. That last part is why this is asked of the session as
3047/// well as of the command: a transaction of nothing but reads runs through a
3048/// write pause, and one write anywhere in it makes the whole transaction wait.
3049fn may_replicate(spec: &Spec, session: &Session) -> bool {
3050    spec.flags.contains(&"write")
3051        || MAY_REPLICATE.contains(&spec.name)
3052        || (spec.name == "exec" && session.queued_writes())
3053}
3054
3055/// Whether a monitor is refused this command, which is anything that goes near
3056/// the keyspace.
3057///
3058/// The writes, the reads and the six above, which is Redis's list read out of
3059/// the same three questions in the same order. `EXEC` is not on it and does not
3060/// need to be: a monitor cannot have queued one of these, because the refusal is
3061/// in front of the queue.
3062fn touches_keyspace(spec: &Spec) -> bool {
3063    spec.flags.contains(&"write")
3064        || spec.flags.contains(&"readonly")
3065        || MAY_REPLICATE.contains(&spec.name)
3066}
3067
3068/// The same, for a caller that has already found the command.
3069///
3070/// The engine frames a command before it runs it, and between those two it also
3071/// asks which key the command touches so the record can be prefetched. That is
3072/// two more chances to look the name up, and looking it up three times to run it
3073/// once is three times the cost of the cheapest thing in the path. So the engine
3074/// resolves the name where it frames the command, carries the answer on the
3075/// framed command, and both the other two take it from there.
3076///
3077/// `spec` is `None` for a name that is not a command, which is the same thing
3078/// [`lookup`] says and lands in the same reply.
3079pub fn resolved(
3080    server: &Server,
3081    session: &mut Session,
3082    spec: Option<&'static Spec>,
3083    args: Args<'_>,
3084    out: &mut Out,
3085) -> Flow {
3086    if args.is_empty() {
3087        return Flow::Continue;
3088    }
3089    server.mine().stats.commands.bump();
3090
3091    // The four refusals below are the ones a real server makes in
3092    // `processCommand`, before the command's own body is reached, and they are
3093    // the ones that kill an open transaction. That is the whole of the rule: an
3094    // error raised here means `EXEC` will refuse to run anything, and an error
3095    // raised by a command body does not, which is why `MULTI` inside `MULTI`
3096    // complains and leaves the transaction alive.
3097    let Some(spec) = spec else {
3098        multi::refuse(server, session, None, &args::unknown_command(args), out);
3099        return Flow::Continue;
3100    };
3101    if !arity_ok(spec, args.len()) {
3102        server.mine().cmdstats.at(spec).rejected.bump();
3103        multi::refuse(
3104            server,
3105            session,
3106            Some(spec),
3107            &args::wrong_arity(spec.name),
3108            out,
3109        );
3110        return Flow::Continue;
3111    }
3112    // What this connection is doing, which only `CLIENT` reads back. Here and
3113    // not further down because a command that is about to be refused or queued
3114    // is still the last command the connection sent, which is what a real
3115    // server reports: it notes the name in `processCommand` before any of the
3116    // decisions below.
3117    let argv = (0..args.len()).map(|i| args.get(i).len() as u64).sum();
3118    session.ran(
3119        table::index_of(spec),
3120        container_sub(spec, &args),
3121        argv,
3122        server.now_ms(),
3123    );
3124
3125    // The password, and this is the whole of it on the command path: one
3126    // acquire load on a server nobody gave a password to. Here, after the two
3127    // refusals above and before everything below, which is where a real server
3128    // puts it, so a command with the wrong number of arguments is told that
3129    // rather than told to authenticate, and everything else is told to
3130    // authenticate before it is told anything at all.
3131    //
3132    // The commands carrying `no_auth` go through, which is `AUTH` itself and the
3133    // three that a client has to be able to send before it has a password
3134    // accepted: `HELLO`, which carries the option that authenticates, `RESET`,
3135    // which is how a client says it is starting over, and `QUIT`.
3136    if server.guarded()
3137        && !session.authenticated()
3138        && !session.serving_master()
3139        && !spec.flags.contains(&"no_auth")
3140    {
3141        server.mine().cmdstats.at(spec).rejected.bump();
3142        if spec.name == "exec" {
3143            multi::abort(server, session, auth::NOAUTH, out);
3144        } else {
3145            session.dirty_multi();
3146            out.error(auth::NOAUTH.as_bytes());
3147        }
3148        return Flow::Continue;
3149    }
3150
3151    if session.in_multi()
3152        && let Some(e) = multi::refused_in_multi(spec)
3153    {
3154        server.mine().cmdstats.at(spec).rejected.bump();
3155        multi::refuse(server, session, Some(spec), &e, out);
3156        return Flow::Continue;
3157    }
3158
3159    // The ACL, here and in this order because this is where a real server puts
3160    // it: after the refusal above and before the memory limit, so a user who may
3161    // not run a command is told that rather than told the server is full.
3162    //
3163    // One relaxed load on a server nobody has written an ACL for, which is every
3164    // server that only ever set `requirepass`, because setting a password leaves
3165    // the default user able to do everything and a user who can do everything
3166    // cannot be refused anything.
3167    if server.restricted()
3168        && !session.serving_master()
3169        && let Some(said) = acl::gate(server, session, spec, args, out)
3170    {
3171        server.mine().cmdstats.at(spec).rejected.bump();
3172        if spec.name == "exec" {
3173            multi::abort(server, session, &said, out);
3174        } else {
3175            session.dirty_multi();
3176            out.error(said.as_bytes());
3177        }
3178        return Flow::Continue;
3179    }
3180
3181    // Where this command's keys say it should run, which is the whole of
3182    // routing and is one field read on a server that is not a cluster node,
3183    // which is nearly every server there is. Here, after the access control list
3184    // and before the queue below, which is where a real server puts it: a user
3185    // who may not touch a key is told that rather than told to go somewhere
3186    // else, and a command queued inside a transaction is refused as it is queued
3187    // so the whole transaction comes back as an `EXECABORT`.
3188    //
3189    // A command the master sent goes through untouched. A replica applies
3190    // whatever its master wrote, including writes to slots the master owned and
3191    // it does not, and a replica that redirected its own master would be a
3192    // replica that stopped following.
3193    if server.cluster_enabled()
3194        && !session.serving_master()
3195        && let Some(said) = cluster::gate(
3196            server,
3197            session.db,
3198            session.asking || cluster::asks(spec),
3199            spec,
3200            args,
3201        )
3202    {
3203        server.mine().cmdstats.at(spec).rejected.bump();
3204        if spec.name == "exec" {
3205            multi::abort(server, session, said.message(), out);
3206        } else {
3207            session.dirty_multi();
3208            out.error(said.message().as_bytes());
3209        }
3210        return Flow::Continue;
3211    }
3212
3213    // The limit first, so a server with no `maxmemory`, which is the default and
3214    // is nearly all of them, pays one comparison against a field that is already
3215    // warm. Every command and not only the writes, because that is where Redis
3216    // puts it: making room is the server's job whatever the client asked for,
3217    // and the flag only decides who gets told no when there is no room to make.
3218    //
3219    // The flag is Redis's own `denyoom` and the list of commands carrying it is
3220    // Redis's list, so a command that only frees is let through with nothing
3221    // left, which is what lets a client dig itself out with `DEL`.
3222    if server.maxmemory() != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
3223        server.mine().cmdstats.at(spec).rejected.bump();
3224        session.dirty_multi();
3225        out.error_line(b"OOM ", OOM);
3226        return Flow::Continue;
3227    }
3228
3229    // A write from a client on a replica is refused, which is what
3230    // `replica-read-only` is and is on by default. Here, after the memory limit
3231    // and before the queue below, which is where a real server puts it, so a
3232    // write queued inside a transaction on a replica is refused as it is queued
3233    // and the whole transaction comes back as an `EXECABORT`.
3234    //
3235    // Two loads on a server that is nobody's replica, both of a bool that is
3236    // false, and the first of them is the one that is nearly always the answer.
3237    // The master's own stream goes through, which is the entire point: a replica
3238    // that refused its master's writes would be a replica of nothing.
3239    if server.read_only_replica() && !session.serving_master() && spec.flags.contains(&"write") {
3240        server.mine().cmdstats.at(spec).rejected.bump();
3241        if spec.name == "exec" {
3242            multi::abort(server, session, follow::READONLY, out);
3243        } else {
3244            session.dirty_multi();
3245            out.error(follow::READONLY.as_bytes());
3246        }
3247        return Flow::Continue;
3248    }
3249
3250    // A RESP2 connection that has subscribed to something may only send a
3251    // handful of commands, because RESP2 sends a published message as an
3252    // ordinary array and a client with a reply outstanding could not tell the
3253    // two apart. Here, after the refusals above and before the queue below,
3254    // which is where a real server puts it: `EXEC` sent while subscribed comes
3255    // back as an `EXECABORT` rather than as this error, and a command `EXEC`
3256    // hands over is not asked at all.
3257    if let Some(e) = pubsub::refused(session, spec, out) {
3258        server.mine().cmdstats.at(spec).rejected.bump();
3259        multi::refuse(server, session, Some(spec), &e, out);
3260        return Flow::Continue;
3261    }
3262
3263    // A monitor may not touch the keyspace. Redis flags one a replica and this
3264    // is the refusal a replica gets, which reads like an accident of the
3265    // implementation and is not one: a monitor is exempt from the pause below,
3266    // so a connection that could pause the server and then become a monitor
3267    // would have a way past its own pause that nothing else has.
3268    //
3269    // Here, in front of the pause and in front of the queue, which is where a
3270    // real server puts it. In front of the queue is what makes `MULTI`, `GET x`,
3271    // `EXEC` on a monitor come back as an `EXECABORT`: the `GET` is refused as
3272    // it is queued rather than as it runs.
3273    if session.monitoring() && touches_keyspace(spec) {
3274        server.mine().cmdstats.at(spec).rejected.bump();
3275        multi::refuse(server, session, Some(spec), &monitor::replica(), out);
3276        return Flow::Continue;
3277    }
3278
3279    // `CLIENT PAUSE`, and this is the whole of it on the command path: one
3280    // relaxed load on a server nobody has paused. Here, after every refusal
3281    // above and before the queue below, which is where a real server puts it. So
3282    // a command that would have been refused is still refused while the server
3283    // is paused, and `MULTI` on a paused server waits rather than opening a
3284    // transaction that would queue commands nobody is allowed to send yet.
3285    //
3286    // Nothing is exempt but a monitor, not even `CLIENT UNPAUSE`, which is
3287    // Redis's behaviour and is worth being clear about: a `CLIENT PAUSE 10000
3288    // ALL` cannot be called off, by anybody, until it runs out. The monitor is
3289    // exempt because a real server exempts its replicas and a monitor is flagged
3290    // one, and it costs nothing to let through because the gate above has
3291    // already refused it everything that reaches a key.
3292    // A command `EXEC` is replaying is not a command the client just sent, and a
3293    // real server runs those through `call` rather than through
3294    // `processCommand`, so the gate is not in front of them. Holding one would
3295    // mean a transaction that has written half of itself and stopped.
3296    if !session.running
3297        && !session.monitoring()
3298        && !session.serving_master()
3299        && let Some(all) = server.paused(server.now_ms())
3300        && (all || may_replicate(spec, session))
3301    {
3302        return Flow::Hold;
3303    }
3304
3305    // And the same thing for the moment a full resync is taking its image. A
3306    // write held here runs a moment later against a keyspace it has not missed
3307    // anything of, which is the whole reason it is held: the image and the
3308    // offset stamped with it have to be the two halves of one instant, and a
3309    // write that landed between them would be in both or in neither. Only the
3310    // writes, and never a command `EXEC` is replaying, for the same reasons the
3311    // pause above gives.
3312    if !session.running && server.frozen() && may_replicate(spec, session) {
3313        return Flow::Hold;
3314    }
3315
3316    // Held rather than run, and the reply is `QUEUED`. After the refusals above
3317    // and before everything below, which is where a real server puts it: a
3318    // command has to be a real command with the right number of arguments to be
3319    // queued at all, and nothing it would have done gets done now.
3320    if session.queues(spec.name) {
3321        return multi::queue(session, spec, args, out);
3322    }
3323
3324    // Which databases the maintenance turn after this batch has to ask. Marked
3325    // for every command and not only for the writes, because a read can make
3326    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
3327    // record it dropped is exactly the kind of thing the collector is for.
3328    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
3329    // two groups that hold them mark all of them rather than the session's.
3330    server.mine().mark(match spec.group {
3331        "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
3332        | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
3333            1u64 << session.db
3334        }
3335        _ => ALL_DATABASES,
3336    });
3337
3338    // Everybody watching, told about a command that is going to run. The load is
3339    // what this costs a server nobody is watching, which is nearly all of them.
3340    //
3341    // A script is reported before it runs and everything else after, because a
3342    // script's own calls come back through here and a reader wants the `EVAL`
3343    // in front of what it did. Every other command goes below, next to where a
3344    // real server feeds from, which is what puts `EXEC` after the commands it
3345    // replayed rather than in front of them.
3346    let watched = server.monitored() && !monitor::hidden(spec, args);
3347    if watched && monitor::SCRIPTS.contains(&spec.name) {
3348        monitor::feed(server, session, args);
3349    }
3350
3351    let mark = out.len();
3352    // Before the group, because the five that block are list commands and would
3353    // otherwise land in `lists`, which is handed one database and nothing that
3354    // could park a client. The flag is the right thing to branch on rather than
3355    // a list of names: it is what `COMMAND INFO` reports about exactly these
3356    // commands, and the sorted set and stream ones that arrive later carry it
3357    // too.
3358    // What the command is about to do to the keyspace, for anybody subscribed to
3359    // hear about it. Armed here and drained after the group, because the bodies
3360    // below are handed a database and their arguments and have no way to reach
3361    // the pub/sub registry from there. Off costs one thread local store.
3362    let armed = notify::arm(server, session.db);
3363    // And whether what it does has to reach a replica, or the node a slot range
3364    // is being handed to, which the bodies ask about for the same reason and get
3365    // an answer by the same route. That
3366    // arming is done by `notify::arm` above, since the two listeners hear about
3367    // an expired key through the same hook and only one of them can install it.
3368    // What is left here is whether the command as the client sent it would be a
3369    // fair thing to hand a replica, which is the write flag and nothing else: a
3370    // read sends only what its body pushed, which is normally nothing.
3371    let copying = server.propagating();
3372    let verbatim = spec.flags.contains(&"write");
3373    // Which of the keys this command reads are not there. A real server says
3374    // this from inside each lookup and this says all of them in front, which is
3375    // the same order for every command whose first act is to read what it was
3376    // given, and that is nearly all of them.
3377    misses::report(&server.dbs[session.db], session.db, spec, args);
3378    // And whether the lookups it is about to make are reads, for the two
3379    // counters in `INFO stats`. Armed after the walk above so that the walk's
3380    // own probes are not counted, and dropped after the body so that nothing the
3381    // dispatcher does afterwards is either.
3382    let reading = lookups::reading(misses::reading(spec, args));
3383    let done = if spec.flags.contains(&"blocking") {
3384        blocking::execute(server, session, spec, args, out)
3385    } else {
3386        match spec.group {
3387            "string" => {
3388                let db = session.db;
3389                strings::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3390            }
3391            // Its own group and its own file, and the same values underneath:
3392            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
3393            // something a `SET` left behind works.
3394            "bitmap" => {
3395                let db = session.db;
3396                bits::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3397            }
3398            // The same again: a sketch is a string with a documented layout, so
3399            // `GET` hands one to a client and `SET` takes it back.
3400            "hyperloglog" => {
3401                let db = session.db;
3402                hll::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3403            }
3404            "set" => {
3405                let db = session.db;
3406                sets::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3407            }
3408            // The one hash command whose state is not in the keyspace. A
3409            // fieldset belongs to the connection, so this is handed the session
3410            // as well as the database, the same exception `MIGRATE` gets in the
3411            // keyspace group for the socket it keeps.
3412            "hash" if spec.name == "himport" => {
3413                let db = session.db;
3414                himport::execute(&server.dbs[db], &mut session.sets, args, out)
3415                    .map(|()| Flow::Continue)
3416            }
3417            // The one group that reaches back into the server after it has
3418            // written its reply, because a hash is what a search index is
3419            // made of. What comes back is what the indexes have to be told,
3420            // which is not the same as whether the command was a write.
3421            "hash" => {
3422                let db = session.db;
3423                let changed = hashes::execute(&server.dbs[db], db, spec, args, out);
3424                changed.map(|changed| {
3425                    indexing::changed(server, db, args.get(1), changed);
3426                    Flow::Continue
3427                })
3428            }
3429            "list" => {
3430                let db = session.db;
3431                lists::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3432            }
3433            "zset" => {
3434                let db = session.db;
3435                zsets::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3436            }
3437            // A geo key is a sorted set and these are sorted set commands with
3438            // arithmetic on the way in and on the way out, so a client can ZREM
3439            // a place out of one and ZCARD it to count them.
3440            "geo" => {
3441                let db = session.db;
3442                geo::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3443            }
3444            "array" => {
3445                let db = session.db;
3446                arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3447            }
3448            "graph" => {
3449                let db = session.db;
3450                graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3451            }
3452            // A document under a key, reached by a path. The group is Redis's
3453            // module surface and the storage is ours, the same trade the vector
3454            // set group makes.
3455            "json" => {
3456                let db = session.db;
3457                json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3458            }
3459            "vector" => {
3460                let db = session.db;
3461                vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3462            }
3463            "bloom" => {
3464                let db = session.db;
3465                bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3466            }
3467            "cuckoo" => {
3468                let db = session.db;
3469                cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3470            }
3471            "cms" => {
3472                let db = session.db;
3473                cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3474            }
3475            "topk" => {
3476                let db = session.db;
3477                topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3478            }
3479            "tdigest" => {
3480                let db = session.db;
3481                tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3482            }
3483            "ts" => {
3484                let db = session.db;
3485                ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3486            }
3487            // The clock is read before the database is borrowed, because every
3488            // stream command needs the time and it lives on the server. An
3489            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
3490            // `XINFO` reporting it all have to agree about what moment this is.
3491            "stream" => {
3492                let db = session.db;
3493                let now = server.now_ms();
3494                streams::execute(&server.dbs[db], db, spec, args, now, out).map(|()| Flow::Continue)
3495            }
3496            // The one keyspace command that needs more than the databases,
3497            // because the socket it talks down is held on the server between
3498            // commands and not opened again for each one.
3499            "keyspace" if spec.name == "migrate" => {
3500                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
3501            }
3502            // Every database and not the one the session is on, because `COPY` takes
3503            // a `DB n` and writes into a database nobody selected. The other group
3504            // that reaches back into the server afterwards, and it hands back a list
3505            // rather than one answer, because `DEL a b c` is three keys and a rename
3506            // is two.
3507            // `RESTORE-ASKING` is `RESTORE` with an `ASKING` built into it and
3508            // runs the same body, but the reference files it under the server
3509            // group rather than the keyspace one, so it has to be named here to
3510            // reach the arm below.
3511            "keyspace" | "server" if spec.group == "keyspace" || spec.name == "restore-asking" => {
3512                let mut touched = indexing::Touched::new(server);
3513                let done =
3514                    keyspace::execute(&server.dbs, session.db, spec, args, out, &mut touched);
3515                done.map(|()| {
3516                    indexing::touched(server, &touched);
3517                    Flow::Continue
3518                })
3519            }
3520            // No database at all, because an index is not a key. The registry
3521            // is the whole of what these sixteen commands touch, and then
3522            // `FT.CREATE` hands back the name it made so the keys that
3523            // already match its prefix can be read into it. The lock goes
3524            // before the scan runs, since the scan takes it again for every
3525            // key it reads.
3526            "search" if spec.name == "FT.SEARCH" => {
3527                // The two search commands that read documents, and so the two
3528                // that need the keyspace as well as the registry. They take and
3529                // let go of the registry themselves, because they cannot hold
3530                // that and a stripe at the same time.
3531                search::find(server, session.db, args, out).map(|()| Flow::Continue)
3532            }
3533            "search" if spec.name == "FT.AGGREGATE" => {
3534                search::roll(server, session.db, args, out).map(|()| Flow::Continue)
3535            }
3536            "search" if spec.name == "FT.HYBRID" => {
3537                search::hybrid(server, session.db, args, out).map(|()| Flow::Continue)
3538            }
3539            "search" if spec.name == "FT.PROFILE" => {
3540                // Which is one of those two with the working shown, so it needs
3541                // everything they need and takes the same route to it.
3542                search::profiled(server, session.db, args, out).map(|()| Flow::Continue)
3543            }
3544            // The four search commands that name a key rather than an index.
3545            // A suggestion dictionary is a real key with a type of its own, so
3546            // these are handed a database and never touch the registry.
3547            "search" if spec.name.starts_with("FT.SUG") => {
3548                let db = session.db;
3549                suggest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3550            }
3551            // The five deprecated document commands, which are the other search
3552            // commands that need the keyspace as well as the registry: what they
3553            // write and read is an ordinary hash.
3554            "search"
3555                if matches!(
3556                    spec.name,
3557                    "FT.ADD" | "FT.SAFEADD" | "FT.GET" | "FT.MGET" | "FT.DEL"
3558                ) =>
3559            {
3560                let db = session.db;
3561                search::docs::execute(server, db, spec, args, out).map(|()| Flow::Continue)
3562            }
3563            "search" if spec.name == "FT.CURSOR" => {
3564                // Its own arm because the cursors are not in the registry, and
3565                // it takes and lets go of the registry itself to look up the
3566                // index name it is given.
3567                search::cursor::execute(server, args, out).map(|()| Flow::Continue)
3568            }
3569            "search" => {
3570                let db = session.db;
3571                let made = search::execute(server, &mut server.search.lock(), db, spec, args, out);
3572                made.map(|made| {
3573                    match made {
3574                        Some(search::After::Scan(fill)) => indexing::scan(server, db, &fill),
3575                        Some(search::After::Sweep(keys)) => indexing::sweep(server, db, &keys),
3576                        None => {}
3577                    }
3578                    Flow::Continue
3579                })
3580            }
3581            "scripting" => {
3582                scripting::execute(server, session, spec, args, out).map(|()| Flow::Continue)
3583            }
3584            "transactions" => multi::execute(server, session, spec, args, out),
3585            // No database either, and the one group whose replies do not all go
3586            // to the connection that asked. The session is in it because a
3587            // subscription is connection state as well as server state.
3588            "pubsub" => pubsub::execute(server, session, spec, args, out),
3589            _ => server::execute(server, session, spec, args, out),
3590        }
3591    };
3592    drop(reading);
3593    // The other half of the feed. After the body, so that `SELECT 3` is reported
3594    // on the database it moved to, and before the reply is written, which is
3595    // where a real server has it.
3596    if watched && !monitor::SCRIPTS.contains(&spec.name) {
3597        monitor::feed(server, session, args);
3598    }
3599    // Before the error is written and not after, because a command that failed
3600    // half way through still changed whatever it changed before it failed and a
3601    // real server has already published those. Draining here also keeps the
3602    // notifications of a command run by `EXEC` in front of the next one's.
3603    // And back out the misses reported in front of a command that turned out to
3604    // have failed on its own arguments, since a server that fires from inside
3605    // the lookup never reached one.
3606    if let Err(e) = &done {
3607        misses::undo(spec, e);
3608    }
3609    notify::drain(server, armed);
3610
3611    // And copy it to the replicas. Only the writes, because a read changes
3612    // nothing there is anything to copy, and only the ones that got through,
3613    // because a command that was refused on its own arguments would be refused
3614    // there too and sending it would be asking a second server to make the same
3615    // mistake. `EVAL` and `EXEC` are not writes and are not sent: what they did
3616    // came through here one command at a time and each of those was sent on its
3617    // own, which is effect replication and is what a real server settled on for
3618    // the same reason.
3619    //
3620    // On the database the command ran on rather than the one the session is on
3621    // now, which are the same thing for everything but `SELECT`, and `SELECT` is
3622    // not a write.
3623    // Whatever went away on its own goes first, ahead of the command's own
3624    // effect and whether or not the command has one. A read that reaped a key on
3625    // the way past has a deletion to send and nothing else.
3626    repl::swept(server, session.db);
3627    if copying {
3628        if done.is_ok() {
3629            repl::feed(server, session.db, args, verbatim);
3630        } else {
3631            repl::forget();
3632        }
3633    }
3634
3635    let flow = match done {
3636        Ok(flow) => flow,
3637        Err(e) => {
3638            out.truncate(mark);
3639            write_error(out, &e);
3640            Flow::Continue
3641        }
3642    };
3643
3644    // After the command rather than before, so that whether each key it named is
3645    // there is read at the moment a real server would have signalled the change.
3646    // The load is what this costs a server nobody has sent `WATCH` to, and the
3647    // flag is Redis's own, so a command that only reads is never asked.
3648    if server.watching() && spec.flags.contains(&"write") {
3649        multi::touched(server, session, spec, args);
3650    }
3651
3652    // Counted here and not before the call, which is where Redis counts it, so
3653    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
3654    // same way theirs does.
3655    //
3656    // Failure is read off the reply rather than off the `Result`, because the
3657    // two are not the same set. A command that ran out of arguments comes back
3658    // as an `Err` and a command that was sent the wrong password writes its own
3659    // error line and comes back `Ok`, and both of those are a call that failed.
3660    // The first byte at the mark is what a client would branch on, and it is `-`
3661    // for an error on either protocol and `!` for RESP3's long form.
3662    let row = server.mine().cmdstats.at(spec);
3663    row.calls.bump();
3664    if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
3665        row.failed.bump();
3666    }
3667    // Last of all, because the reply the client is being hung up on still has to
3668    // be written first. A command that asked for this has decided the connection
3669    // is not one it wants to keep talking to, which so far is only a client
3670    // caught reaching for the slot migration protocol.
3671    if session.hanging_up() {
3672        return Flow::Close;
3673    }
3674    flow
3675}
3676
3677/// The error line for an error value.
3678///
3679/// The prefix is what a client branches on, and there are three of them:
3680/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
3681/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
3682/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
3683/// than routed through here. `OOM` is not a [`Code`] of its own because
3684/// [`Code::Full`] already covers the string that is too long for
3685/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
3686fn write_error(out: &mut Out, e: &Error) {
3687    let prefix: &[u8] = match e.code() {
3688        Code::WrongType => b"WRONGTYPE ",
3689        // Only the HyperLogLog commands answer this one, and the prefix is the
3690        // sentence a client branches on to tell a sketch it cannot read from a
3691        // sketch it sent wrong.
3692        Code::Corrupt => b"INVALIDOBJ ",
3693        _ => b"ERR ",
3694    };
3695    out.error_line(prefix, e.message().as_bytes());
3696}
3697
3698#[cfg(test)]
3699mod tests {
3700    use super::*;
3701    use crate::proto::{Limits, Proto};
3702    use crate::request::Argv;
3703
3704    /// Build the wire bytes for a command.
3705    ///
3706    /// Tests go through the codec rather than around it, so an argument in a
3707    /// test is the same borrowed slice a connection produces.
3708    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
3709        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
3710        for p in parts {
3711            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
3712            wire.extend_from_slice(p);
3713            wire.extend_from_slice(b"\r\n");
3714        }
3715        wire
3716    }
3717
3718    /// A server, a connection and a buffer, driven the way the reactor will.
3719    struct Fixture {
3720        server: Server,
3721        session: Session,
3722        argv: Argv,
3723        out: Out,
3724        /// How far into the replication stream [`Fixture::crossed`] has read.
3725        mark: u64,
3726    }
3727
3728    /// The number out of an integer reply, for a test that compares two of them
3729    /// rather than checking one against a constant.
3730    fn int_of(reply: &str) -> i64 {
3731        reply
3732            .strip_prefix(':')
3733            .and_then(|s| s.strip_suffix("\r\n"))
3734            .unwrap_or_else(|| panic!("not an integer reply: {reply:?}"))
3735            .parse()
3736            .expect("an integer reply holds an integer")
3737    }
3738
3739    impl Fixture {
3740        fn new() -> Fixture {
3741            Fixture::on(Server::new())
3742        }
3743
3744        /// The same, on a server whose databases are cut into `width` stripes.
3745        fn striped(width: usize) -> Fixture {
3746            Fixture::on(Server::with_width(width))
3747        }
3748
3749        fn on(server: Server) -> Fixture {
3750            Fixture {
3751                server,
3752                session: Session::new(7),
3753                argv: Argv::new(),
3754                out: Out::new(Proto::Resp2),
3755                mark: 0,
3756            }
3757        }
3758
3759        /// The same, on a server that believes it has a replica.
3760        ///
3761        /// Nothing is attached to it. What the tests below read is the
3762        /// replication stream itself, which is written whether or not there is
3763        /// anybody to send it to, so a server told this is a master in every
3764        /// way that these tests can see.
3765        fn replicated() -> Fixture {
3766            let f = Fixture::new();
3767            f.server.pretend_replica();
3768            f
3769        }
3770
3771        /// Run one command and answer with what crossed to a replica.
3772        ///
3773        /// Only what this command added, so a test reads one line rather than
3774        /// the whole history, and the `SELECT` the stream opens with is part of
3775        /// the first answer for the same reason it is part of the stream.
3776        fn crossed(&mut self, parts: &[&[u8]]) -> String {
3777            self.run(parts);
3778            let (text, upto) = self.server.stream_since(self.mark);
3779            self.mark = upto;
3780            text
3781        }
3782
3783        /// Run one command and answer with the bytes it wrote.
3784        fn run(&mut self, parts: &[&[u8]]) -> String {
3785            self.flow(parts).1
3786        }
3787
3788        /// Run one command and answer with the bytes exactly as written.
3789        ///
3790        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
3791        /// every reply that is text and destroys a `DUMP` payload, since a
3792        /// payload is arbitrary bytes and a checksum on the end of them.
3793        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
3794            let wire = encode(parts);
3795            self.argv.decode(&wire, &Limits::default()).unwrap();
3796            self.out.clear();
3797            execute(
3798                &self.server,
3799                &mut self.session,
3800                Args::new(&self.argv, &wire),
3801                &mut self.out,
3802            );
3803            self.out.as_slice().to_vec()
3804        }
3805
3806        /// Move every clock in the server on by `ms`.
3807        fn advance(&mut self, ms: u64) {
3808            self.server.advance_clock_ms(ms);
3809        }
3810
3811        /// Run one command as a second connection to the same server.
3812        ///
3813        /// What `WATCH` is for is a write another connection made, and a test
3814        /// that only has one connection cannot tell the two apart.
3815        fn other(&mut self, parts: &[&[u8]]) -> String {
3816            self.other_in(self.session.db(), parts)
3817        }
3818
3819        /// The same, on a database of its own.
3820        fn other_in(&mut self, db: usize, parts: &[&[u8]]) -> String {
3821            let mut session = Session::new(8);
3822            session.db = db;
3823            let reply = self.by(&mut session, parts);
3824            forget_session(&self.server, &mut session);
3825            reply
3826        }
3827
3828        /// Run one command on a session the caller holds.
3829        fn by(&mut self, session: &mut Session, parts: &[&[u8]]) -> String {
3830            let wire = encode(parts);
3831            let mut argv = Argv::new();
3832            argv.decode(&wire, &Limits::default()).unwrap();
3833            let mut out = Out::new(Proto::Resp2);
3834            execute(&self.server, session, Args::new(&argv, &wire), &mut out);
3835            String::from_utf8_lossy(out.as_slice()).into_owned()
3836        }
3837
3838        /// The same, with what the connection should do next.
3839        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
3840            let wire = encode(parts);
3841            self.argv.decode(&wire, &Limits::default()).unwrap();
3842            self.out.clear();
3843            let flow = execute(
3844                &self.server,
3845                &mut self.session,
3846                Args::new(&self.argv, &wire),
3847                &mut self.out,
3848            );
3849            (
3850                flow,
3851                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
3852            )
3853        }
3854    }
3855
3856    #[test]
3857    fn multi_holds_commands_and_exec_runs_them() {
3858        let mut f = Fixture::new();
3859        assert_eq!(f.run(&[b"MULTI"]), "+OK\r\n");
3860        assert_eq!(f.run(&[b"SET", b"k", b"1"]), "+QUEUED\r\n");
3861        assert_eq!(f.run(&[b"INCR", b"k"]), "+QUEUED\r\n");
3862        // Nothing ran while it was being queued.
3863        assert_eq!(f.other(&[b"GET", b"k"]), "$-1\r\n");
3864        assert_eq!(f.run(&[b"EXEC"]), "*2\r\n+OK\r\n:2\r\n");
3865        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n2\r\n");
3866    }
3867
3868    /// The test the `high_water` claim in `multi::exec` asks for.
3869    ///
3870    /// A `Vec` reaches the allocator exactly when its capacity changes, so a
3871    /// replay buffer whose room is the same before and after is one that did
3872    /// not allocate. The first transaction is what sets the room, which is the
3873    /// high water mark, and the second is the one that has to be free. Before
3874    /// the buffer moved onto the session this failed on every transaction,
3875    /// because `exec` made a new one each time and the room went back to zero.
3876    #[test]
3877    fn the_second_exec_of_a_shape_does_not_grow_the_buffer() {
3878        let mut f = Fixture::new();
3879        for _ in 0..2 {
3880            f.run(&[b"MULTI"]);
3881            f.run(&[b"SET", b"k", b"1"]);
3882            f.run(&[b"INCR", b"k"]);
3883            f.run(&[b"EXEC"]);
3884        }
3885        let room = f.session.replay.room();
3886        assert!(room > 0, "the first transaction should have set the room");
3887        f.run(&[b"MULTI"]);
3888        f.run(&[b"SET", b"k", b"1"]);
3889        f.run(&[b"INCR", b"k"]);
3890        f.run(&[b"EXEC"]);
3891        assert_eq!(f.session.replay.room(), room);
3892    }
3893
3894    #[test]
3895    fn an_empty_transaction_answers_an_empty_array() {
3896        let mut f = Fixture::new();
3897        f.run(&[b"MULTI"]);
3898        assert_eq!(f.run(&[b"EXEC"]), "*0\r\n");
3899    }
3900
3901    #[test]
3902    fn exec_and_discard_want_a_transaction_to_be_open() {
3903        let mut f = Fixture::new();
3904        assert_eq!(f.run(&[b"EXEC"]), "-ERR EXEC without MULTI\r\n");
3905        assert_eq!(f.run(&[b"DISCARD"]), "-ERR DISCARD without MULTI\r\n");
3906        // And `UNWATCH` does not, which is the one of the three that is happy
3907        // being sent for no reason.
3908        assert_eq!(f.run(&[b"UNWATCH"]), "+OK\r\n");
3909    }
3910
3911    #[test]
3912    fn an_error_a_command_body_raises_leaves_the_transaction_alive() {
3913        let mut f = Fixture::new();
3914        f.run(&[b"MULTI"]);
3915        assert_eq!(
3916            f.run(&[b"MULTI"]),
3917            "-ERR MULTI calls can not be nested\r\n",
3918            "nested MULTI is raised by the command and not by the funnel"
3919        );
3920        assert_eq!(
3921            f.run(&[b"WATCH", b"k"]),
3922            "-ERR WATCH inside MULTI is not allowed\r\n"
3923        );
3924        f.run(&[b"SET", b"k", b"1"]);
3925        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n+OK\r\n");
3926    }
3927
3928    #[test]
3929    fn an_error_the_funnel_raises_kills_the_transaction() {
3930        for bad in [
3931            &[b"NOSUCHCOMMAND".as_slice()] as &[&[u8]],
3932            &[b"GET".as_slice()],
3933        ] {
3934            let mut f = Fixture::new();
3935            f.run(&[b"MULTI"]);
3936            assert!(f.run(bad).starts_with("-ERR "));
3937            assert_eq!(
3938                f.run(&[b"SET", b"k", b"1"]),
3939                "+QUEUED\r\n",
3940                "a dead transaction still answers QUEUED, which is Redis"
3941            );
3942            assert_eq!(
3943                f.run(&[b"EXEC"]),
3944                "-EXECABORT Transaction discarded because of previous errors.\r\n"
3945            );
3946            assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3947        }
3948    }
3949
3950    #[test]
3951    fn exec_with_an_argument_is_an_abort_and_not_an_arity_error() {
3952        let mut f = Fixture::new();
3953        f.run(&[b"MULTI"]);
3954        f.run(&[b"SET", b"k", b"1"]);
3955        assert_eq!(
3956            f.run(&[b"EXEC", b"x"]),
3957            "-EXECABORT Transaction discarded because of: wrong number of arguments for 'exec' command\r\n"
3958        );
3959        assert_eq!(f.run(&[b"EXEC"]), "-ERR EXEC without MULTI\r\n");
3960        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3961    }
3962
3963    #[test]
3964    fn a_command_a_transaction_may_not_hold_kills_it() {
3965        let mut f = Fixture::new();
3966        f.run(&[b"MULTI"]);
3967        assert_eq!(
3968            f.run(&[b"SHUTDOWN", b"NOSAVE"]),
3969            "-ERR Command not allowed inside a transaction\r\n"
3970        );
3971        assert_eq!(
3972            f.run(&[b"EXEC"]),
3973            "-EXECABORT Transaction discarded because of previous errors.\r\n"
3974        );
3975    }
3976
3977    #[test]
3978    fn a_failing_command_inside_exec_is_an_element_and_the_rest_still_runs() {
3979        let mut f = Fixture::new();
3980        f.run(&[b"RPUSH", b"l", b"v"]);
3981        f.run(&[b"MULTI"]);
3982        f.run(&[b"INCR", b"l"]);
3983        f.run(&[b"SET", b"y", b"2"]);
3984        assert_eq!(
3985            f.run(&[b"EXEC"]),
3986            "*2\r\n-WRONGTYPE Operation against a key holding the wrong kind of value\r\n+OK\r\n"
3987        );
3988        assert_eq!(f.run(&[b"GET", b"y"]), "$1\r\n2\r\n");
3989    }
3990
3991    #[test]
3992    fn discard_and_reset_both_throw_the_queue_away() {
3993        let mut f = Fixture::new();
3994        f.run(&[b"MULTI"]);
3995        f.run(&[b"SET", b"k", b"1"]);
3996        assert_eq!(f.run(&[b"DISCARD"]), "+OK\r\n");
3997        assert_eq!(f.run(&[b"EXEC"]), "-ERR EXEC without MULTI\r\n");
3998        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3999
4000        f.run(&[b"MULTI"]);
4001        f.run(&[b"SET", b"k", b"1"]);
4002        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
4003        assert_eq!(f.run(&[b"EXEC"]), "-ERR EXEC without MULTI\r\n");
4004        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
4005    }
4006
4007    #[test]
4008    fn select_is_queued_and_applied_when_exec_runs_it() {
4009        let mut f = Fixture::new();
4010        f.run(&[b"MULTI"]);
4011        assert_eq!(f.run(&[b"SELECT", b"3"]), "+QUEUED\r\n");
4012        f.run(&[b"SET", b"k", b"1"]);
4013        assert_eq!(f.run(&[b"EXEC"]), "*2\r\n+OK\r\n+OK\r\n");
4014        assert_eq!(f.session.db(), 3, "the SELECT applied and stayed applied");
4015        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n1\r\n");
4016    }
4017
4018    #[test]
4019    fn a_write_by_another_connection_fails_the_transaction() {
4020        let mut f = Fixture::new();
4021        f.run(&[b"SET", b"k", b"1"]);
4022        assert_eq!(f.run(&[b"WATCH", b"k"]), "+OK\r\n");
4023        f.other(&[b"SET", b"k", b"2"]);
4024        f.run(&[b"MULTI"]);
4025        f.run(&[b"GET", b"k"]);
4026        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
4027    }
4028
4029    #[test]
4030    fn a_write_that_puts_the_same_value_back_still_fails_it() {
4031        let mut f = Fixture::new();
4032        f.run(&[b"SET", b"k", b"1"]);
4033        f.run(&[b"WATCH", b"k"]);
4034        f.other(&[b"SET", b"k", b"1"]);
4035        f.run(&[b"MULTI"]);
4036        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
4037    }
4038
4039    #[test]
4040    fn a_read_by_another_connection_does_not() {
4041        let mut f = Fixture::new();
4042        f.run(&[b"SET", b"k", b"1"]);
4043        f.run(&[b"WATCH", b"k"]);
4044        f.other(&[b"GET", b"k"]);
4045        f.other(&[b"STRLEN", b"k"]);
4046        f.run(&[b"MULTI"]);
4047        f.run(&[b"GET", b"k"]);
4048        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n$1\r\n1\r\n");
4049    }
4050
4051    #[test]
4052    fn deleting_a_key_that_was_never_there_does_not_fail_a_watch_on_it() {
4053        let mut f = Fixture::new();
4054        f.run(&[b"WATCH", b"k"]);
4055        f.other(&[b"DEL", b"k"]);
4056        f.run(&[b"MULTI"]);
4057        f.run(&[b"PING"]);
4058        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n+PONG\r\n");
4059        // And creating it does, which is the other half of the same rule.
4060        f.run(&[b"WATCH", b"k"]);
4061        f.other(&[b"SET", b"k", b"1"]);
4062        f.run(&[b"MULTI"]);
4063        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
4064    }
4065
4066    #[test]
4067    fn a_watched_key_that_expires_fails_the_transaction() {
4068        let mut f = Fixture::new();
4069        f.run(&[b"SET", b"k", b"1", b"PX", b"50"]);
4070        f.run(&[b"WATCH", b"k"]);
4071        f.run(&[b"MULTI"]);
4072        f.advance(100);
4073        assert_eq!(
4074            f.run(&[b"EXEC"]),
4075            "*-1\r\n",
4076            "nothing wrote to the key, so only the liveness check can catch this"
4077        );
4078    }
4079
4080    #[test]
4081    fn every_way_a_transaction_ends_lets_go_of_the_watches() {
4082        for end in [
4083            &[b"EXEC".as_slice()] as &[&[u8]],
4084            &[b"DISCARD".as_slice()],
4085            &[b"UNWATCH".as_slice()],
4086            &[b"RESET".as_slice()],
4087        ] {
4088            let mut f = Fixture::new();
4089            f.run(&[b"SET", b"k", b"1"]);
4090            f.run(&[b"WATCH", b"k"]);
4091            if end[0] != b"UNWATCH" && end[0] != b"RESET" {
4092                f.run(&[b"MULTI"]);
4093            }
4094            f.run(end);
4095            assert!(!f.server.watching(), "{end:?} left a row behind");
4096            // And the connection can start again with nothing carried over.
4097            f.other(&[b"SET", b"k", b"2"]);
4098            f.run(&[b"MULTI"]);
4099            f.run(&[b"GET", b"k"]);
4100            assert_eq!(f.run(&[b"EXEC"]), "*1\r\n$1\r\n2\r\n");
4101        }
4102    }
4103
4104    #[test]
4105    fn a_connection_going_away_lets_go_of_its_watches() {
4106        let mut f = Fixture::new();
4107        f.run(&[b"SET", b"k", b"1"]);
4108        f.run(&[b"WATCH", b"k"]);
4109        assert!(f.server.watching());
4110        forget_session(&f.server, &mut f.session);
4111        assert!(!f.server.watching());
4112    }
4113
4114    #[test]
4115    fn watching_the_same_key_twice_is_one_watch() {
4116        let mut f = Fixture::new();
4117        f.run(&[b"SET", b"k", b"1"]);
4118        f.run(&[b"WATCH", b"k", b"k"]);
4119        f.run(&[b"UNWATCH"]);
4120        assert!(
4121            !f.server.watching(),
4122            "the row counts watchers, so a doubled watch would leave one behind"
4123        );
4124    }
4125
4126    #[test]
4127    fn two_connections_can_watch_the_same_key() {
4128        let mut f = Fixture::new();
4129        f.run(&[b"SET", b"k", b"1"]);
4130        f.run(&[b"WATCH", b"k"]);
4131        let mut second = Session::new(9);
4132        second.db = f.session.db();
4133        assert_eq!(f.by(&mut second, &[b"WATCH", b"k"]), "+OK\r\n");
4134        // One lets go and the other's watch still works.
4135        forget_session(&f.server, &mut second);
4136        assert!(f.server.watching());
4137        f.other(&[b"SET", b"k", b"2"]);
4138        f.run(&[b"MULTI"]);
4139        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
4140    }
4141
4142    #[test]
4143    fn flushdb_fails_a_watch_on_a_key_that_was_there() {
4144        let mut f = Fixture::new();
4145        f.run(&[b"SET", b"k", b"1"]);
4146        f.run(&[b"WATCH", b"k"]);
4147        f.other(&[b"FLUSHDB"]);
4148        f.run(&[b"MULTI"]);
4149        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
4150    }
4151
4152    #[test]
4153    fn flushdb_does_not_fail_a_watch_on_a_key_that_was_not() {
4154        let mut f = Fixture::new();
4155        f.run(&[b"WATCH", b"k"]);
4156        f.other(&[b"FLUSHDB"]);
4157        f.run(&[b"MULTI"]);
4158        f.run(&[b"PING"]);
4159        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n+PONG\r\n");
4160    }
4161
4162    #[test]
4163    fn a_watch_is_on_a_database_and_a_key_and_not_on_a_key() {
4164        let mut f = Fixture::new();
4165        f.run(&[b"SET", b"k", b"1"]);
4166        f.run(&[b"WATCH", b"k"]);
4167        // The same name in another database is another key.
4168        let elsewhere = f.session.db() + 1;
4169        f.other_in(elsewhere, &[b"SET", b"k", b"9"]);
4170        f.run(&[b"MULTI"]);
4171        f.run(&[b"GET", b"k"]);
4172        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n$1\r\n1\r\n");
4173    }
4174
4175    #[test]
4176    fn a_write_that_reaches_a_key_it_did_not_name_still_fails_a_watch() {
4177        let mut f = Fixture::new();
4178        f.run(&[b"RPUSH", b"src", b"1"]);
4179        f.run(&[b"WATCH", b"dst"]);
4180        f.other(&[b"SORT", b"src", b"STORE", b"dst"]);
4181        f.run(&[b"MULTI"]);
4182        assert_eq!(
4183            f.run(&[b"EXEC"]),
4184            "*-1\r\n",
4185            "SORT is movablekeys, so every watched key in the database is asked"
4186        );
4187    }
4188
4189    #[test]
4190    fn a_server_nobody_is_watching_says_so() {
4191        let mut f = Fixture::new();
4192        assert!(!f.server.watching());
4193        f.run(&[b"SET", b"k", b"1"]);
4194        assert!(!f.server.watching());
4195    }
4196
4197    /// The count on the end of a subscribe reply is channels and patterns
4198    /// together, which is a thing a client uses to know when it is out of
4199    /// subscribe mode and so has to be the number the mode is decided on.
4200    /// Shard channels are counted on their own because they are their own
4201    /// namespace.
4202    #[test]
4203    fn the_count_a_subscribe_answers_covers_channels_and_patterns() {
4204        let mut f = Fixture::new();
4205        assert_eq!(
4206            f.run(&[b"SUBSCRIBE", b"a", b"b"]),
4207            "*3\r\n$9\r\nsubscribe\r\n$1\r\na\r\n:1\r\n*3\r\n$9\r\nsubscribe\r\n$1\r\nb\r\n:2\r\n"
4208        );
4209        assert_eq!(
4210            f.run(&[b"PSUBSCRIBE", b"c*"]),
4211            "*3\r\n$10\r\npsubscribe\r\n$2\r\nc*\r\n:3\r\n"
4212        );
4213        assert_eq!(
4214            f.run(&[b"SSUBSCRIBE", b"s"]),
4215            "*3\r\n$10\r\nssubscribe\r\n$1\r\ns\r\n:1\r\n"
4216        );
4217        // Subscribing again to something already held answers again with the
4218        // count unchanged, rather than counting it twice or saying nothing.
4219        assert_eq!(
4220            f.run(&[b"SUBSCRIBE", b"a"]),
4221            "*3\r\n$9\r\nsubscribe\r\n$1\r\na\r\n:3\r\n"
4222        );
4223    }
4224
4225    /// Unsubscribe has three shapes and a client has to be able to tell them
4226    /// apart, because the last one is what tells it the mode is over.
4227    #[test]
4228    fn unsubscribe_answers_for_names_it_was_not_holding_too() {
4229        let mut f = Fixture::new();
4230        f.run(&[b"SUBSCRIBE", b"a"]);
4231
4232        // A name that was never subscribed still gets a reply, with the count
4233        // as it stands.
4234        assert_eq!(
4235            f.run(&[b"UNSUBSCRIBE", b"zz"]),
4236            "*3\r\n$11\r\nunsubscribe\r\n$2\r\nzz\r\n:1\r\n"
4237        );
4238        // With no names, one reply per channel held, counting down.
4239        f.run(&[b"SUBSCRIBE", b"b"]);
4240        f.run(&[b"PSUBSCRIBE", b"p*"]);
4241        assert_eq!(
4242            f.run(&[b"UNSUBSCRIBE"]),
4243            "*3\r\n$11\r\nunsubscribe\r\n$1\r\na\r\n:2\r\n*3\r\n$11\r\nunsubscribe\r\n$1\r\nb\r\n:1\r\n"
4244        );
4245        // With no names and none of that family held, one reply with a nil
4246        // where the name goes and the count that is left.
4247        assert_eq!(
4248            f.run(&[b"UNSUBSCRIBE"]),
4249            "*3\r\n$11\r\nunsubscribe\r\n$-1\r\n:1\r\n",
4250            "the pattern is still held, so the count is one"
4251        );
4252        assert_eq!(
4253            f.run(&[b"SUNSUBSCRIBE"]),
4254            "*3\r\n$12\r\nsunsubscribe\r\n$-1\r\n:0\r\n",
4255            "shard channels are counted on their own"
4256        );
4257    }
4258
4259    /// The gate is on the funnel and the funnel is what `EXEC` goes through
4260    /// for the commands it queued, so it has to know it is running one.
4261    /// Redis lets a queued command through, and a transaction that subscribes
4262    /// and then reads is the case that says which way round it is.
4263    #[test]
4264    fn the_subscribe_gate_does_not_reach_inside_exec() {
4265        let mut f = Fixture::new();
4266        f.run(&[b"SET", b"k", b"1"]);
4267        f.run(&[b"MULTI"]);
4268        assert_eq!(f.run(&[b"SUBSCRIBE", b"z"]), "+QUEUED\r\n");
4269        assert_eq!(f.run(&[b"GET", b"k"]), "+QUEUED\r\n");
4270        assert_eq!(
4271            f.run(&[b"EXEC"]),
4272            "*2\r\n*3\r\n$9\r\nsubscribe\r\n$1\r\nz\r\n:1\r\n$1\r\n1\r\n"
4273        );
4274        // And once EXEC is done the connection really is subscribed, so the
4275        // gate is back on.
4276        assert_eq!(
4277            f.run(&[b"GET", b"k"]),
4278            "-ERR Can't execute 'get': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context\r\n"
4279        );
4280    }
4281
4282    /// `EXEC` sent by a subscribed RESP2 client is refused by the gate like
4283    /// anything else, and a refusal on the funnel kills the transaction.
4284    #[test]
4285    fn exec_sent_by_a_subscriber_aborts_the_transaction() {
4286        let mut f = Fixture::new();
4287        f.run(&[b"MULTI"]);
4288        f.run(&[b"SET", b"k", b"1"]);
4289        f.run(&[b"SUBSCRIBE", b"z"]);
4290        f.run(&[b"EXEC"]);
4291        f.run(&[b"MULTI"]);
4292        assert_eq!(
4293            f.run(&[b"EXEC"]),
4294            "-EXECABORT Transaction discarded because of: Can't execute 'exec': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context\r\n"
4295        );
4296    }
4297
4298    /// `RESET` is one of the few things a subscriber may send, and what it
4299    /// resets includes every subscription it is holding.
4300    #[test]
4301    fn reset_lets_go_of_every_subscription() {
4302        let mut f = Fixture::new();
4303        f.run(&[b"SUBSCRIBE", b"a"]);
4304        f.run(&[b"PSUBSCRIBE", b"p*"]);
4305        f.run(&[b"SSUBSCRIBE", b"s"]);
4306        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
4307        assert_eq!(f.run(&[b"PUBSUB", b"NUMPAT"]), ":0\r\n");
4308        assert_eq!(f.run(&[b"PUBSUB", b"CHANNELS"]), "*0\r\n");
4309        assert_eq!(f.run(&[b"PUBSUB", b"SHARDCHANNELS"]), "*0\r\n");
4310        // And the connection takes ordinary commands again.
4311        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
4312    }
4313
4314    /// What `PUBSUB` can be asked, on a server with one subscriber holding one
4315    /// of each.
4316    #[test]
4317    fn pubsub_reports_channels_patterns_and_shard_channels_apart() {
4318        let mut f = Fixture::new();
4319        let mut sub = Session::new(9);
4320        f.by(&mut sub, &[b"SUBSCRIBE", b"a"]);
4321        f.by(&mut sub, &[b"PSUBSCRIBE", b"a*"]);
4322        f.by(&mut sub, &[b"SSUBSCRIBE", b"a"]);
4323
4324        assert_eq!(f.run(&[b"PUBSUB", b"CHANNELS"]), "*1\r\n$1\r\na\r\n");
4325        assert_eq!(f.run(&[b"PUBSUB", b"CHANNELS", b"b*"]), "*0\r\n");
4326        assert_eq!(f.run(&[b"PUBSUB", b"SHARDCHANNELS"]), "*1\r\n$1\r\na\r\n");
4327        assert_eq!(f.run(&[b"PUBSUB", b"NUMPAT"]), ":1\r\n");
4328        assert_eq!(
4329            f.run(&[b"PUBSUB", b"NUMSUB", b"a", b"zz"]),
4330            "*4\r\n$1\r\na\r\n:1\r\n$2\r\nzz\r\n:0\r\n"
4331        );
4332        assert_eq!(
4333            f.run(&[b"PUBSUB", b"SHARDNUMSUB", b"a"]),
4334            "*2\r\n$1\r\na\r\n:1\r\n",
4335            "the shard channel and the channel share a name and not a count"
4336        );
4337        assert_eq!(f.run(&[b"PUBSUB", b"NUMSUB"]), "*0\r\n");
4338
4339        forget_session(&f.server, &mut sub);
4340        assert_eq!(f.run(&[b"PUBSUB", b"NUMPAT"]), ":0\r\n");
4341        assert_eq!(f.run(&[b"PUBSUB", b"CHANNELS"]), "*0\r\n");
4342    }
4343
4344    /// The one setting whose value is neither a number nor a word, and whose
4345    /// spelling on the way out is not the spelling on the way in.
4346    #[test]
4347    fn the_notification_setting_reads_back_in_the_servers_own_spelling() {
4348        let mut f = Fixture::new();
4349        assert_eq!(
4350            f.run(&[b"CONFIG", b"GET", b"notify-keyspace-events"]),
4351            "*2\r\n$22\r\nnotify-keyspace-events\r\n$0\r\n\r\n"
4352        );
4353        assert_eq!(
4354            f.run(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"KEA"]),
4355            "+OK\r\n"
4356        );
4357        // `A` is a class of its own on the way in and stays one on the way out,
4358        // and the two channel letters move to the end.
4359        assert_eq!(
4360            f.run(&[b"CONFIG", b"GET", b"notify-keyspace-events"]),
4361            "*2\r\n$22\r\nnotify-keyspace-events\r\n$3\r\nAKE\r\n"
4362        );
4363        assert_eq!(
4364            f.run(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"Kg"]),
4365            "+OK\r\n"
4366        );
4367        assert_eq!(
4368            f.run(&[b"CONFIG", b"GET", b"notify-keyspace-events"]),
4369            "*2\r\n$22\r\nnotify-keyspace-events\r\n$2\r\ngK\r\n"
4370        );
4371    }
4372
4373    #[test]
4374    fn a_letter_the_notification_setting_does_not_know_is_refused() {
4375        let mut f = Fixture::new();
4376        assert_eq!(
4377            f.run(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"KEQ"]),
4378            "-ERR CONFIG SET failed (possibly related to argument 'notify-keyspace-events') \
4379             - Invalid event class character. Use 'Ag$lshzxeKEtmdnocaSTIV'.\r\n"
4380        );
4381        // And nothing was applied, since the whole setting is parsed before any
4382        // of it is stored.
4383        assert_eq!(
4384            f.run(&[b"CONFIG", b"GET", b"notify-keyspace-events"]),
4385            "*2\r\n$22\r\nnotify-keyspace-events\r\n$0\r\n\r\n"
4386        );
4387    }
4388
4389    /// One mistake in a `PUBSUB` subcommand has two error shapes depending on
4390    /// which subcommand it is, because the ones with a fixed argument count are
4391    /// checked by the subcommand table and the ones without fall through to
4392    /// the generic syntax error. Both are copied here rather than tidied,
4393    /// since a client that matches on the text sees the difference.
4394    #[test]
4395    fn pubsub_says_no_two_different_ways() {
4396        let mut f = Fixture::new();
4397        assert_eq!(
4398            f.run(&[b"PUBSUB"]),
4399            "-ERR wrong number of arguments for 'pubsub' command\r\n"
4400        );
4401        assert_eq!(
4402            f.run(&[b"PUBSUB", b"NOPE"]),
4403            "-ERR unknown subcommand 'NOPE'. Try PUBSUB HELP.\r\n"
4404        );
4405        assert_eq!(
4406            f.run(&[b"PUBSUB", b"CHANNELS", b"a*", b"b"]),
4407            "-ERR unknown subcommand or wrong number of arguments for 'CHANNELS'. Try PUBSUB HELP.\r\n"
4408        );
4409        assert_eq!(
4410            f.run(&[b"PUBSUB", b"NUMPAT", b"x"]),
4411            "-ERR wrong number of arguments for 'pubsub|numpat' command\r\n"
4412        );
4413        assert_eq!(
4414            f.run(&[b"PUBSUB", b"HELP", b"x"]),
4415            "-ERR wrong number of arguments for 'pubsub|help' command\r\n"
4416        );
4417    }
4418
4419    /// Publishing to nobody costs a lookup and answers zero, which is the
4420    /// common case on a server that has pub/sub compiled in and not in use.
4421    #[test]
4422    fn publishing_to_nobody_answers_zero() {
4423        let mut f = Fixture::new();
4424        assert_eq!(f.run(&[b"PUBLISH", b"a", b"hi"]), ":0\r\n");
4425        assert_eq!(f.run(&[b"SPUBLISH", b"a", b"hi"]), ":0\r\n");
4426        // An empty channel name is a name like any other.
4427        assert_eq!(f.run(&[b"PUBLISH", b"", b"hi"]), ":0\r\n");
4428    }
4429
4430    /// A publish counts everybody it reached, which is not the same as the
4431    /// number of subscribers: one connection holding two patterns that both
4432    /// match is two.
4433    #[test]
4434    fn a_publish_counts_the_deliveries_and_not_the_clients() {
4435        let mut f = Fixture::new();
4436        let mut sub = Session::new(9);
4437        f.by(&mut sub, &[b"SUBSCRIBE", b"news"]);
4438        f.by(&mut sub, &[b"PSUBSCRIBE", b"ne*"]);
4439        f.by(&mut sub, &[b"PSUBSCRIBE", b"n*s"]);
4440        assert_eq!(f.run(&[b"PUBLISH", b"news", b"hi"]), ":3\r\n");
4441        forget_session(&f.server, &mut sub);
4442    }
4443
4444    /// What a client does all day: write the same keys again and again. Every
4445    /// one of those writes leaves the previous record behind, so a server that
4446    /// never compacts holds every version of every key it has ever been sent.
4447    ///
4448    /// Not under Miri, and not because of anything it would find. The bound
4449    /// only means something once several megabytes have gone through the
4450    /// arena, which reclaims a segment at a time and has segments of two
4451    /// megabytes, so a server that reclaimed nothing would still be under the
4452    /// bound in any smaller version of this. Thirty two megabytes is thirty
4453    /// two thousand commands and was over forty minutes interpreted. The paths
4454    /// it walks are walked by the hundreds of tests around it that write a key
4455    /// and read it back, which do run there.
4456    #[cfg_attr(miri, ignore = "megabytes through the arena")]
4457    #[test]
4458    fn rewriting_the_same_keys_does_not_grow_the_server() {
4459        let mut f = Fixture::new();
4460        let val = vec![b'v'; 1024];
4461        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
4462
4463        for k in &keys {
4464            f.run(&[b"SET", k, &val]);
4465        }
4466        f.server.compact_step();
4467        let after_first = f.server.memory_bytes();
4468
4469        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
4470        // of it. Thirty two megabytes written to hold sixty four kilobytes,
4471        // which is the shape of a real workload and is enough churn to fill
4472        // sixteen segments if nothing ever comes back.
4473        for _ in 0..500 {
4474            for k in &keys {
4475                f.run(&[b"SET", k, &val]);
4476            }
4477            f.server.compact_step();
4478        }
4479
4480        assert!(
4481            f.server.memory_bytes() <= after_first * 2,
4482            "held {} after five hundred passes against {after_first} after one",
4483            f.server.memory_bytes()
4484        );
4485        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
4486        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
4487    }
4488
4489    /// The same churn on a database nobody starts on, either side of a quiet
4490    /// spell long enough for the maintenance turn to stop asking about it.
4491    ///
4492    /// The turn after each batch skips a database that has already said it has
4493    /// nothing to collect and has not been touched since, which is what keeps a
4494    /// server whose clients are all on database zero from loading and storing
4495    /// in the other fifteen every batch to be told no. Two things could go
4496    /// wrong with that. A database might never be marked at all, so this uses
4497    /// database nine, which nothing marks by accident. And a database whose
4498    /// mark was cleared might never get it back, so this drains the collector
4499    /// until it says there is nothing left, checks the mark really is gone, and
4500    /// then writes another thirty two megabytes through the same sixty four
4501    /// keys. If either went wrong the server would hold all of it.
4502    ///
4503    /// Not under Miri, for the reason on the test above: the volume is the
4504    /// claim, and the volume is what the interpreter charges for.
4505    #[cfg_attr(miri, ignore = "megabytes through the arena")]
4506    #[test]
4507    fn a_database_nobody_started_on_is_still_collected() {
4508        let mut f = Fixture::new();
4509        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
4510        let val = vec![b'v'; 1024];
4511        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
4512
4513        for k in &keys {
4514            f.run(&[b"SET", k, &val]);
4515        }
4516        // A call looks at [`COMPACT_LOOKS`] stripes and not at all of them, so
4517        // draining takes calls in proportion to the width and one call saying
4518        // there was nothing to move is not the whole database saying it.
4519        let drain = |f: &Fixture| {
4520            for _ in 0..4 * f.server.slots() {
4521                if f.server.compact_step().is_none() && !f.server.mine().wanted(9) {
4522                    return;
4523                }
4524            }
4525            panic!("compaction never got to the end of database nine");
4526        };
4527        drain(&f);
4528        assert!(
4529            !f.server.mine().wanted(9),
4530            "database nine was drained and should not be asked again until it is written to"
4531        );
4532        let after_first = f.server.memory_bytes();
4533
4534        for _ in 0..500 {
4535            for k in &keys {
4536                f.run(&[b"SET", k, &val]);
4537            }
4538            f.server.compact_step();
4539        }
4540
4541        assert!(
4542            f.server.memory_bytes() <= after_first * 2,
4543            "held {} after five hundred passes against {after_first} after one",
4544            f.server.memory_bytes()
4545        );
4546        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
4547        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
4548        // And nothing landed anywhere else on the way.
4549        f.run(&[b"SELECT", b"0"]);
4550        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4551    }
4552
4553    /// The maintenance turn moves its own cursor and leaves the server's alone.
4554    ///
4555    /// The turn runs after every batch on every thread, so anything it writes
4556    /// that the whole server can see is a line every thread is writing to at
4557    /// batch rate, and the cost of that goes up with the thread count instead
4558    /// of staying still. The cursor is the last thing in the turn that was
4559    /// shared, and it was shared for a reason that only ever applied to the
4560    /// other caller: [`Server::compact_hard_step`] runs when a server is over
4561    /// its memory limit, which is a rare thing and not a per batch thing.
4562    ///
4563    /// What is checked is both halves of that. The turn is asked to walk, and
4564    /// afterwards this thread's cursor has moved and the server's has not.
4565    #[test]
4566    fn the_maintenance_turn_does_not_write_a_shared_cursor() {
4567        let f = Fixture::new();
4568        let before = f.server.next_db.load(Relaxed);
4569        let mine = f.server.mine().compact_db.load(Relaxed);
4570        // Nothing to compact, which is the case that matters: a turn that found
4571        // nothing is nearly every turn, and it used to write the shared cursor
4572        // anyway just to say where the next one should start.
4573        assert!(f.server.compact_step().is_none());
4574        assert_eq!(
4575            f.server.next_db.load(Relaxed),
4576            before,
4577            "the turn wrote the cursor the over limit path reads"
4578        );
4579        assert_ne!(
4580            f.server.mine().compact_db.load(Relaxed),
4581            mine,
4582            "the turn did not move on, so it will look at the same stripes forever"
4583        );
4584    }
4585
4586    /// Two threads start their walk in different places.
4587    ///
4588    /// Splitting the cursor gave up the one thing sharing bought, which is two
4589    /// threads not arriving at the same database at the same moment. Seeding
4590    /// each thread's cursor at its own index buys most of it back for nothing,
4591    /// and this is that: a server built for eight threads has eight cursors and
4592    /// no two of them start together.
4593    #[test]
4594    fn each_thread_starts_its_compaction_somewhere_else() {
4595        let mut server = Server::new();
4596        server.set_threads(8);
4597        let starts: Vec<usize> = server
4598            .locals
4599            .iter()
4600            .map(|t| t.compact_db.load(Relaxed))
4601            .collect();
4602        assert_eq!(starts, (0..8).collect::<Vec<usize>>());
4603    }
4604
4605    #[test]
4606    fn a_command_goes_from_bytes_to_bytes() {
4607        let mut f = Fixture::new();
4608        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
4609        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
4610        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
4611        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
4612        // The name is matched whatever case it came in, and so are the options.
4613        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
4614        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
4615    }
4616
4617    #[test]
4618    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
4619        let mut f = Fixture::new();
4620        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
4621        // A key named twice exists twice and can only be deleted once, and both
4622        // of those are Redis's answers rather than tidier ones.
4623        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
4624        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
4625        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
4626        // UNLINK is the same body and reports the same way.
4627        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
4628        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4629    }
4630
4631    #[test]
4632    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
4633        let mut f = Fixture::new();
4634        f.run(&[b"SET", b"k", b"v"]);
4635        // A simple string on both protocols, which is unusual: most replies
4636        // that carry a word are bulk strings.
4637        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
4638        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
4639    }
4640
4641    #[test]
4642    fn touch_counts_the_way_exists_counts() {
4643        let mut f = Fixture::new();
4644        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
4645        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
4646        assert_eq!(
4647            f.run(&[b"TOUCH", b"a", b"a"]),
4648            ":2\r\n",
4649            "twice counts twice"
4650        );
4651        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
4652        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
4653    }
4654
4655    #[test]
4656    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
4657        let mut f = Fixture::new();
4658        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
4659        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
4660
4661        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
4662        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
4663        assert_eq!(
4664            f.run(&[b"TTL", b"b"]),
4665            ":100\r\n",
4666            "the source's and not b's"
4667        );
4668        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
4669    }
4670
4671    #[test]
4672    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
4673        let mut f = Fixture::new();
4674        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
4675        // The source is checked before the destination, so this is the error
4676        // and not the zero RENAMENX would otherwise answer for a taken name.
4677        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
4678    }
4679
4680    #[test]
4681    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
4682        let mut f = Fixture::new();
4683        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
4684
4685        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
4686        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
4687        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
4688        // one call the two disagree about and neither does any work for.
4689        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
4690        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
4691        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
4692        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
4693    }
4694
4695    #[test]
4696    fn renaming_a_set_does_not_touch_a_member() {
4697        let mut f = Fixture::new();
4698        for i in 0..300 {
4699            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
4700        }
4701        let before = f.server.memory_bytes();
4702
4703        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
4704        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
4705        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
4706        assert!(
4707            f.server.memory_bytes().abs_diff(before) < 256,
4708            "the members were copied: {} against {before}",
4709            f.server.memory_bytes()
4710        );
4711    }
4712
4713    #[test]
4714    fn a_copy_is_a_second_value_and_not_a_second_name() {
4715        let mut f = Fixture::new();
4716        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
4717
4718        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
4719        f.run(&[b"SADD", b"t", b"m3"]);
4720        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
4721        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
4722    }
4723
4724    /// Every type a key can hold, copied, because two of them used to panic.
4725    ///
4726    /// `COPY` reads the value out of the source through one match on the type
4727    /// tag, and that match had a catch all at the bottom from back when a set
4728    /// and a hash were the only bodies. The list and the sorted set landed after
4729    /// it and nobody came back, so `COPY mylist other` took the shard down. It
4730    /// is an ordinary command against a type the server supports everywhere
4731    /// else, so this walks all five rather than the two that were broken: the
4732    /// point is that the next type cannot land the same way.
4733    #[test]
4734    fn every_type_can_be_copied() {
4735        let mut f = Fixture::new();
4736        f.run(&[b"SET", b"str", b"v1"]);
4737        f.run(&[b"SADD", b"set", b"m1"]);
4738        f.run(&[b"HSET", b"hash", b"f", b"v"]);
4739        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
4740        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
4741
4742        for name in [
4743            &b"str"[..],
4744            &b"set"[..],
4745            &b"hash"[..],
4746            &b"list"[..],
4747            &b"zset"[..],
4748        ] {
4749            let dst = [name, b":copy"].concat();
4750            assert_eq!(
4751                f.run(&[b"COPY", name, &dst]),
4752                ":1\r\n",
4753                "copying {}",
4754                String::from_utf8_lossy(name)
4755            );
4756            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
4757        }
4758
4759        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
4760            let mut want = String::from("*2\r\n");
4761            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
4762            want
4763        });
4764        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
4765
4766        // And the copy is its own value, not a second name for the source.
4767        f.run(&[b"RPUSH", b"list:copy", b"c"]);
4768        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
4769        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
4770    }
4771
4772    #[test]
4773    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
4774        let mut f = Fixture::new();
4775        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
4776        f.run(&[b"SET", b"b", b"v2"]);
4777
4778        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
4779        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
4780        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
4781        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
4782        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
4783        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
4784    }
4785
4786    #[test]
4787    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
4788        let mut f = Fixture::new();
4789        f.run(&[b"SET", b"a", b"v1"]);
4790
4791        // Same key, different database, so this is not the same object and is
4792        // an ordinary copy. Same key in the same database is the error below.
4793        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
4794        f.run(&[b"SELECT", b"1"]);
4795        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
4796        assert_eq!(
4797            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
4798            ":0\r\n",
4799            "taken"
4800        );
4801        assert_eq!(
4802            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
4803            ":1\r\n"
4804        );
4805    }
4806
4807    #[test]
4808    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
4809        let mut f = Fixture::new();
4810        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
4811        assert_eq!(
4812            f.run(&[b"SORT", b"l"]),
4813            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
4814        );
4815        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
4816        assert_eq!(
4817            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
4818            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
4819        );
4820        assert_eq!(
4821            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
4822            "*1\r\n$1\r\n2\r\n"
4823        );
4824    }
4825
4826    #[test]
4827    fn sort_reads_a_key_per_element_for_by_and_for_get() {
4828        let mut f = Fixture::new();
4829        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
4830        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
4831        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
4832        // misses, which is a nil in the middle of the array and not a short one.
4833        assert_eq!(
4834            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
4835            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
4836        );
4837    }
4838
4839    #[test]
4840    fn sort_store_writes_a_list_and_answers_its_length() {
4841        let mut f = Fixture::new();
4842        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
4843        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
4844        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
4845        assert_eq!(
4846            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
4847            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
4848        );
4849        // An empty result takes the destination with it rather than leaving a
4850        // list that holds nothing.
4851        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
4852        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
4853    }
4854
4855    #[test]
4856    fn sort_ro_does_not_know_the_word_store() {
4857        let mut f = Fixture::new();
4858        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
4859        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
4860        assert_eq!(
4861            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
4862            "-ERR syntax error\r\n"
4863        );
4864        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4865    }
4866
4867    #[test]
4868    fn sort_refuses_what_it_cannot_sort() {
4869        let mut f = Fixture::new();
4870        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
4871        f.run(&[b"SET", b"s", b"x"]);
4872        assert_eq!(
4873            f.run(&[b"SORT", b"s"]),
4874            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
4875        );
4876        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
4877        assert_eq!(
4878            f.run(&[b"SORT", b"words"]),
4879            "-ERR One or more scores can't be converted into double\r\n"
4880        );
4881        assert_eq!(
4882            f.run(&[b"SORT", b"words", b"ALPHA"]),
4883            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
4884        );
4885        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
4886    }
4887
4888    #[test]
4889    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
4890        let mut f = Fixture::new();
4891        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
4892        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
4893        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
4894        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4895        assert_eq!(
4896            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
4897            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
4898        );
4899        // And back, which proves the body survived the trip rather than being
4900        // rebuilt from a copy that happened to look the same.
4901        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
4902        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
4903    }
4904
4905    #[test]
4906    fn move_answers_zero_when_either_end_says_no() {
4907        let mut f = Fixture::new();
4908        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
4909        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
4910        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4911        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
4912        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
4913        // The destination is taken, so nothing moves and the source is still
4914        // there with what it had.
4915        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
4916        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
4917        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4918        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
4919    }
4920
4921    #[test]
4922    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
4923        let mut f = Fixture::new();
4924        assert_eq!(
4925            f.run(&[b"MOVE", b"a", b"0"]),
4926            "-ERR source and destination objects are the same\r\n"
4927        );
4928        assert_eq!(
4929            f.run(&[b"MOVE", b"a", b"99"]),
4930            "-ERR DB index is out of range\r\n"
4931        );
4932        assert_eq!(
4933            f.run(&[b"MOVE", b"a", b"-1"]),
4934            "-ERR DB index is out of range\r\n"
4935        );
4936        assert_eq!(
4937            f.run(&[b"MOVE", b"a", b"x"]),
4938            "-ERR value is not an integer or out of range\r\n"
4939        );
4940    }
4941
4942    #[test]
4943    fn swapdb_swaps_what_two_connections_would_see() {
4944        let mut f = Fixture::new();
4945        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
4946        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4947        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
4948        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
4949
4950        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
4951        // Still on database zero, and database zero is a different database.
4952        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
4953        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4954        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
4955        // A database swapped with itself is fine and changes nothing.
4956        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
4957        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
4958    }
4959
4960    /// Every database on a server reads the server's clock and not one of its
4961    /// own. They used to be told the time one at a time and now they share the
4962    /// reading, so a server that built its databases from a second clock would
4963    /// answer a deadline worked out against a time nobody had set.
4964    #[test]
4965    fn a_wide_server_puts_its_databases_on_its_own_clock() {
4966        let mut f = Fixture::striped(8);
4967        f.server.set_clock_ms(1_700_000_000_000);
4968        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"100"]), "+OK\r\n");
4969        assert_eq!(f.run(&[b"EXPIRETIME", b"k"]), ":1700000100\r\n");
4970        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
4971        f.server.set_clock_ms(1_700_000_050_000);
4972        assert_eq!(f.run(&[b"TTL", b"k"]), ":50\r\n");
4973    }
4974
4975    /// The swap is stripe by stripe, so a database cut into more than one
4976    /// stripe is the case that would catch it exchanging some of the keys and
4977    /// leaving the rest. Sixteen keys over four stripes is enough that every
4978    /// stripe has something in it whatever the hashes come out as.
4979    #[test]
4980    fn swapdb_swaps_every_stripe_of_a_wide_database() {
4981        let mut f = Fixture::striped(4);
4982        for i in 0..16u32 {
4983            let key = format!("k{i}");
4984            assert_eq!(f.run(&[b"SET", key.as_bytes(), b"zero"]), "+OK\r\n");
4985        }
4986        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4987        assert_eq!(f.run(&[b"SET", b"only", b"one"]), "+OK\r\n");
4988        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
4989
4990        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
4991        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4992        assert_eq!(f.run(&[b"GET", b"only"]), "$3\r\none\r\n");
4993        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4994        assert_eq!(f.run(&[b"DBSIZE"]), ":16\r\n");
4995        for i in 0..16u32 {
4996            let key = format!("k{i}");
4997            assert_eq!(f.run(&[b"GET", key.as_bytes()]), "$4\r\nzero\r\n");
4998        }
4999    }
5000
5001    #[test]
5002    fn swapdb_says_which_index_it_could_not_read() {
5003        let mut f = Fixture::new();
5004        assert_eq!(
5005            f.run(&[b"SWAPDB", b"x", b"1"]),
5006            "-ERR invalid first DB index\r\n"
5007        );
5008        assert_eq!(
5009            f.run(&[b"SWAPDB", b"0", b"y"]),
5010            "-ERR invalid second DB index\r\n"
5011        );
5012        // A number too big to be an index on a server that keeps one in an int
5013        // is the same complaint, and a plausible one that is not ours is the
5014        // range complaint instead. The split is Redis's.
5015        assert_eq!(
5016            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
5017            "-ERR invalid first DB index\r\n"
5018        );
5019        assert_eq!(
5020            f.run(&[b"SWAPDB", b"0", b"99"]),
5021            "-ERR DB index is out of range\r\n"
5022        );
5023        assert_eq!(
5024            f.run(&[b"SWAPDB", b"-1", b"0"]),
5025            "-ERR DB index is out of range\r\n"
5026        );
5027    }
5028
5029    #[test]
5030    fn wait_answers_zero_replicas_without_waiting() {
5031        let mut f = Fixture::new();
5032        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
5033        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
5034        // A replica that is never going to arrive, and a timeout that would be
5035        // a real wait on a server that had one.
5036        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
5037        // Negative replicas is not an error, because zero is already more than
5038        // it asked for.
5039        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
5040        assert_eq!(
5041            f.run(&[b"WAIT", b"x", b"0"]),
5042            "-ERR value is not an integer or out of range\r\n"
5043        );
5044        assert_eq!(
5045            f.run(&[b"WAIT", b"0", b"-1"]),
5046            "-ERR timeout is negative\r\n"
5047        );
5048        assert_eq!(
5049            f.run(&[b"WAIT", b"0", b"1.5"]),
5050            "-ERR timeout is not an integer or out of range\r\n"
5051        );
5052    }
5053
5054    #[test]
5055    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
5056        let mut f = Fixture::new();
5057        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
5058        assert_eq!(
5059            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
5060            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
5061        );
5062        assert_eq!(
5063            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
5064            "-ERR value is out of range, value must between 0 and 1\r\n"
5065        );
5066        assert_eq!(
5067            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
5068            "-ERR value is out of range, must be positive\r\n"
5069        );
5070        // The arguments are all read before the server looks at itself, so a
5071        // bad timeout beats the append only complaint even with numlocal set.
5072        assert_eq!(
5073            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
5074            "-ERR timeout is negative\r\n"
5075        );
5076    }
5077
5078    /// The bytes inside a bulk reply, with the header and the trailing break
5079    /// taken off. Every `DUMP` test needs this and none of them care how the
5080    /// length was written.
5081    fn payload(reply: &[u8]) -> Vec<u8> {
5082        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
5083        reply[head + 2..reply.len() - 2].to_vec()
5084    }
5085
5086    #[test]
5087    fn a_value_survives_a_dump_and_a_restore() {
5088        let mut f = Fixture::new();
5089        f.run(&[b"SET", b"s", b"hello"]);
5090        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
5091        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
5092        f.run(&[b"SADD", b"u", b"x", b"y"]);
5093        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
5094        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
5095
5096        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
5097            let mut copy = key.to_vec();
5098            copy.push(b'2');
5099            let bytes = payload(&f.raw(&[b"DUMP", key]));
5100            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
5101            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
5102        }
5103
5104        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
5105        assert_eq!(
5106            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
5107            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5108        );
5109        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
5110        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
5111        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
5112        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
5113        // The encoding survives too, since the payload names the plainest legal
5114        // type and the loader puts the value back on the rung it belongs on.
5115        assert_eq!(
5116            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
5117            f.run(&[b"OBJECT", b"ENCODING", b"t"])
5118        );
5119    }
5120
5121    #[test]
5122    fn a_dumped_hash_keeps_its_field_deadlines() {
5123        let mut f = Fixture::new();
5124        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
5125        assert_eq!(
5126            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
5127            "*1\r\n:1\r\n"
5128        );
5129        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
5130        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
5131        assert_eq!(
5132            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
5133            "*2\r\n:-1\r\n:100\r\n"
5134        );
5135    }
5136
5137    #[test]
5138    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
5139        let mut f = Fixture::new();
5140        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
5141        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
5142        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
5143        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
5144        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
5145        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
5146        // An absolute deadline that has already gone is not an error. The key is
5147        // not created and the reply is the same OK a live one gets.
5148        assert_eq!(
5149            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
5150            "+OK\r\n"
5151        );
5152        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5153    }
5154
5155    #[test]
5156    fn dump_answers_nothing_for_a_key_that_is_not_there() {
5157        let mut f = Fixture::new();
5158        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
5159        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
5160        f.advance(50);
5161        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
5162    }
5163
5164    #[test]
5165    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
5166        let mut f = Fixture::new();
5167        f.run(&[b"SET", b"a", b"first"]);
5168        f.run(&[b"SET", b"b", b"second"]);
5169        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
5170        assert_eq!(
5171            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
5172            "-BUSYKEY Target key name already exists.\r\n"
5173        );
5174        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
5175        assert_eq!(
5176            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
5177            "+OK\r\n"
5178        );
5179        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
5180    }
5181
5182    /// The busy key comes before the payload, which is not the order the
5183    /// arguments read in. Whether a key is taken should not depend on whether
5184    /// the bytes behind it happened to be good.
5185    #[test]
5186    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
5187        let mut f = Fixture::new();
5188        f.run(&[b"SET", b"a", b"v"]);
5189        assert_eq!(
5190            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
5191            "-BUSYKEY Target key name already exists.\r\n"
5192        );
5193        // And the options come before even that, so a bad FREQ beats the busy
5194        // key the same way a bad DB beats a missing source in COPY.
5195        assert_eq!(
5196            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
5197            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
5198        );
5199    }
5200
5201    #[test]
5202    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
5203        let mut f = Fixture::new();
5204        f.run(&[b"SET", b"a", b"hello"]);
5205        let good = payload(&f.raw(&[b"DUMP", b"a"]));
5206
5207        let mut flipped = good.clone();
5208        flipped[2] ^= 0x40;
5209        assert_eq!(
5210            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
5211            "-ERR DUMP payload version or checksum are wrong\r\n"
5212        );
5213        assert_eq!(
5214            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
5215            "-ERR DUMP payload version or checksum are wrong\r\n"
5216        );
5217        // A footer that is right over a body that is not. The type byte says
5218        // string and there is nothing behind it, so the checksum agrees and the
5219        // value does not exist.
5220        let mut truncated = good[..1].to_vec();
5221        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
5222        let crc = yo_common::crc::crc64(0, &truncated);
5223        truncated.extend_from_slice(&crc.to_le_bytes());
5224        assert_eq!(
5225            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
5226            "-ERR Bad data format\r\n"
5227        );
5228        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
5229    }
5230
5231    #[test]
5232    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
5233        let mut f = Fixture::new();
5234        f.run(&[b"SET", b"a", b"v"]);
5235        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
5236        assert_eq!(
5237            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
5238            "-ERR Invalid TTL value, must be >= 0\r\n"
5239        );
5240        assert_eq!(
5241            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
5242            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
5243        );
5244        assert_eq!(
5245            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
5246            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
5247        );
5248        // Both are accepted and both are then dropped, which is D-26.
5249        assert_eq!(
5250            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
5251            "+OK\r\n"
5252        );
5253        assert_eq!(
5254            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
5255            "+OK\r\n"
5256        );
5257    }
5258
5259    /// Neither word is refused for being the wrong one. Each is only accepted
5260    /// while the other is unset, so the second of the two falls through to the
5261    /// plain syntax error rather than getting a message of its own.
5262    #[test]
5263    fn restore_takes_idletime_or_freq_and_not_both() {
5264        let mut f = Fixture::new();
5265        f.run(&[b"SET", b"a", b"v"]);
5266        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
5267        assert_eq!(
5268            f.run(&[
5269                b"RESTORE",
5270                b"b",
5271                b"0",
5272                &bytes,
5273                b"IDLETIME",
5274                b"1",
5275                b"FREQ",
5276                b"2"
5277            ]),
5278            "-ERR syntax error\r\n"
5279        );
5280        assert_eq!(
5281            f.run(&[
5282                b"RESTORE",
5283                b"b",
5284                b"0",
5285                &bytes,
5286                b"FREQ",
5287                b"2",
5288                b"IDLETIME",
5289                b"1"
5290            ]),
5291            "-ERR syntax error\r\n"
5292        );
5293        assert_eq!(
5294            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
5295            "-ERR syntax error\r\n"
5296        );
5297        assert_eq!(
5298            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
5299            "-ERR syntax error\r\n"
5300        );
5301    }
5302
5303    #[test]
5304    fn copy_checks_its_options_before_it_looks_for_anything() {
5305        let mut f = Fixture::new();
5306        // No key exists at all, and every one of these is still the option
5307        // complaint rather than a zero, which is the order a real server uses.
5308        assert_eq!(
5309            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
5310            "-ERR DB index is out of range\r\n"
5311        );
5312        assert_eq!(
5313            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
5314            "-ERR DB index is out of range\r\n"
5315        );
5316        assert_eq!(
5317            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
5318            "-ERR value is not an integer or out of range\r\n"
5319        );
5320        assert_eq!(
5321            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
5322            "-ERR syntax error\r\n"
5323        );
5324        assert_eq!(
5325            f.run(&[b"COPY", b"a", b"a"]),
5326            "-ERR source and destination objects are the same\r\n"
5327        );
5328        // Repeated, reordered and lowercased, and the last DB wins.
5329        assert_eq!(
5330            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
5331            ":0\r\n"
5332        );
5333    }
5334
5335    #[test]
5336    fn time_is_two_bulk_strings_and_moves() {
5337        let mut f = Fixture::new();
5338        let first = f.run(&[b"TIME"]);
5339        assert!(first.starts_with("*2\r\n$"), "got {first}");
5340        let parts: Vec<&str> = first.split("\r\n").collect();
5341        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
5342        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
5343        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
5344        assert!((0..1_000_000).contains(&micros), "got {micros}");
5345        // The coarse clock the keyspace uses is a cached millisecond that a
5346        // background tick refreshes, so a TIME built on it would answer the
5347        // same microsecond twice in a row here.
5348        assert_ne!(first, f.run(&[b"TIME"]));
5349    }
5350
5351    #[test]
5352    fn a_keyspace_scan_walks_every_key_once() {
5353        // The count below is thirty two, so ninety six keys is three pages of
5354        // cursor and says the same thing as five hundred at a fifth of the
5355        // interpreted work.
5356        let n = if cfg!(miri) { 96 } else { 500 };
5357        let mut f = Fixture::new();
5358        for i in 0..n {
5359            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
5360        }
5361
5362        let mut seen: Vec<String> = Vec::new();
5363        let mut cursor = "0".to_owned();
5364        let mut calls = 0;
5365        loop {
5366            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
5367            seen.extend(keys);
5368            cursor = next;
5369            calls += 1;
5370            assert!(calls < 10_000, "the cursor is not advancing");
5371            if cursor == "0" {
5372                break;
5373            }
5374        }
5375
5376        seen.sort();
5377        seen.dedup();
5378        assert_eq!(seen.len(), n, "every key once and only once");
5379        // And more than one call to get them, or the COUNT is being ignored and
5380        // the loop above proved nothing about resuming.
5381        assert!(calls > 1, "{n} keys came back in one batch");
5382    }
5383
5384    #[test]
5385    fn a_scan_narrows_by_pattern_and_by_type() {
5386        let mut f = Fixture::new();
5387        f.run(&[b"SET", b"str", b"v"]);
5388        f.run(&[b"SADD", b"members", b"a"]);
5389        f.run(&[b"HSET", b"fields", b"f", b"v"]);
5390
5391        let all = |f: &mut Fixture, args: &[&[u8]]| {
5392            let mut out: Vec<String> = Vec::new();
5393            let mut cursor = "0".to_owned();
5394            loop {
5395                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
5396                line.extend_from_slice(args);
5397                let (next, keys) = scan_reply(&f.run(&line));
5398                out.extend(keys);
5399                cursor = next;
5400                if cursor == "0" {
5401                    break;
5402                }
5403            }
5404            out.sort();
5405            out
5406        };
5407
5408        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
5409        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
5410        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
5411        // Case insensitive, the same as Redis's own comparison.
5412        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
5413        // A type nothing can hold is not an error, it just matches nothing.
5414        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
5415        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
5416        // Both filters at once, and they are an and rather than an or.
5417        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
5418    }
5419
5420    #[test]
5421    fn a_scan_says_what_is_wrong_with_it() {
5422        let mut f = Fixture::new();
5423        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
5424        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
5425        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
5426        assert_eq!(
5427            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
5428            "-ERR syntax error\r\n"
5429        );
5430        assert_eq!(
5431            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
5432            "-ERR value is not an integer or out of range\r\n"
5433        );
5434        assert_eq!(
5435            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
5436            "-ERR syntax error\r\n"
5437        );
5438        // A cursor the client made up is a cursor. It resumes somewhere
5439        // arbitrary and answers whatever is there, which is what Redis does and
5440        // is the only behaviour that does not need the server to remember every
5441        // cursor it has handed out.
5442        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
5443    }
5444
5445    #[test]
5446    fn keys_and_randomkey_look_at_the_whole_database() {
5447        let mut f = Fixture::new();
5448        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
5449        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
5450
5451        for name in ["one", "two", "three"] {
5452            f.run(&[b"SET", name.as_bytes(), b"v"]);
5453        }
5454        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
5455        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
5456        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
5457
5458        for _ in 0..50 {
5459            let got = f.run(&[b"RANDOMKEY"]);
5460            assert!(
5461                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
5462                "got {got}"
5463            );
5464        }
5465    }
5466
5467    #[test]
5468    fn a_walk_does_not_answer_keys_that_have_expired() {
5469        let mut f = Fixture::new();
5470        f.run(&[b"SET", b"alive", b"v"]);
5471        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
5472        f.server.advance_clock_ms(2);
5473        assert_eq!(
5474            f.run(&[b"DBSIZE"]),
5475            ":2\r\n",
5476            "nothing has collected it yet"
5477        );
5478
5479        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
5480        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
5481        assert_eq!(keys, ["alive"]);
5482        for _ in 0..20 {
5483            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
5484        }
5485        // The walk collected it on the way past, which is what makes DBSIZE
5486        // here answer what Redis answers once its own cycle has been round.
5487        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
5488    }
5489
5490    #[test]
5491    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
5492        let mut f = Fixture::new();
5493        f.run(&[b"SET", b"k", b"v"]);
5494        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
5495        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
5496
5497        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
5498        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
5499        let ms = int(&f.run(&[b"PTTL", b"k"]));
5500        assert!((99_000..=100_000).contains(&ms), "got {ms}");
5501
5502        // The absolute pair, derived from the same one number the store kept.
5503        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
5504        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
5505        assert_eq!(at, (at_ms + 500) / 1000);
5506        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5507
5508        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
5509        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
5510        assert_eq!(
5511            f.run(&[b"PERSIST", b"k"]),
5512            ":0\r\n",
5513            "nothing to take off the second time"
5514        );
5515        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
5516        assert_eq!(
5517            f.run(&[b"GET", b"k"]),
5518            "$1\r\nv\r\n",
5519            "and the value went through all of that untouched"
5520        );
5521    }
5522
5523    #[test]
5524    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
5525        let mut f = Fixture::new();
5526        f.run(&[b"SET", b"str", b"v"]);
5527        f.run(&[b"SADD", b"set", b"a", b"b"]);
5528        f.run(&[b"HSET", b"hash", b"f", b"v"]);
5529
5530        for key in [b"str".as_slice(), b"set", b"hash"] {
5531            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
5532            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
5533        }
5534        // The body is not touched by any of that, which is the whole reason the
5535        // deadline lives in the record and the body lives somewhere else.
5536        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
5537        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
5538        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5539    }
5540
5541    #[test]
5542    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
5543        let mut f = Fixture::new();
5544        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
5545            f.run(&[b"SET", key, b"v"]);
5546        }
5547        // Four ways of naming a moment that has passed, and all four are a
5548        // delete answering 1 rather than an error. Zero is a moment, minus one
5549        // is a moment, and the hash field commands refuse the negative one.
5550        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
5551        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
5552        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
5553        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
5554        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5555        assert_eq!(
5556            f.run(&[b"EXPIRE", b"a", b"100"]),
5557            ":0\r\n",
5558            "and the key really went, so there is nothing to put a deadline on"
5559        );
5560    }
5561
5562    #[test]
5563    fn the_four_conditions_decide_whether_the_deadline_moves() {
5564        let mut f = Fixture::new();
5565        f.run(&[b"SET", b"k", b"v"]);
5566
5567        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
5568        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
5569        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
5570        assert_eq!(
5571            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
5572            ":1\r\n",
5573            "no deadline reads as infinitely far away, so LT passes where GT fails"
5574        );
5575
5576        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
5577        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
5578        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
5579        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
5580        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
5581        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
5582
5583        // The condition is answered before the past check, so this is a 0 and
5584        // the key survives. The other order would delete it.
5585        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
5586        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
5587        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
5588        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
5589    }
5590
5591    #[test]
5592    fn the_conditions_are_a_set_and_not_a_keyword() {
5593        let mut f = Fixture::new();
5594        f.run(&[b"SET", b"k", b"v"]);
5595
5596        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
5597        assert_eq!(
5598            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
5599            ":0\r\n",
5600            "the same keyword twice means it once, and NX now has a deadline to fail on"
5601        );
5602
5603        // XX with LT is the one pair that is not either of them on its own: LT
5604        // alone would accept a key with no deadline and this does not.
5605        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
5606        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
5607        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
5608        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
5609        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
5610        f.run(&[b"PERSIST", b"k"]);
5611        assert_eq!(
5612            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
5613            ":0\r\n",
5614            "where LT on its own would have taken it"
5615        );
5616        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
5617    }
5618
5619    #[test]
5620    fn a_key_is_gone_once_its_moment_passes() {
5621        let mut f = Fixture::new();
5622        f.run(&[b"SET", b"k", b"v"]);
5623        f.run(&[b"EXPIRE", b"k", b"100"]);
5624
5625        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
5626        f.server.set_clock_ms(at as u64 + 1);
5627        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
5628        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
5629        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5630        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5631    }
5632
5633    #[test]
5634    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
5635        let mut f = Fixture::new();
5636        f.run(&[b"SET", b"k", b"v"]);
5637        for (bad, want) in [
5638            (
5639                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
5640                "-ERR value is not an integer or out of range\r\n",
5641            ),
5642            (
5643                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
5644                "-ERR Unsupported option MAYBE\r\n",
5645            ),
5646            (
5647                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
5648                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
5649            ),
5650            (
5651                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
5652                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
5653            ),
5654            (
5655                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
5656                "-ERR GT and LT options at the same time are not compatible\r\n",
5657            ),
5658            // Seconds that overflow when multiplied into milliseconds. Every
5659            // message names the command it came from.
5660            (
5661                &[b"EXPIRE", b"k", b"9223372036854775807"],
5662                "-ERR invalid expire time in 'expire' command\r\n",
5663            ),
5664            (
5665                &[b"EXPIREAT", b"k", b"9223372036854775807"],
5666                "-ERR invalid expire time in 'expireat' command\r\n",
5667            ),
5668            (
5669                &[b"PEXPIRE", b"k", b"9223372036854775807"],
5670                "-ERR invalid expire time in 'pexpire' command\r\n",
5671            ),
5672        ] {
5673            assert_eq!(f.run(bad), want, "for {bad:?}");
5674        }
5675        assert_eq!(
5676            f.run(&[b"TTL", b"k"]),
5677            ":-1\r\n",
5678            "and none of those put a deadline on anything"
5679        );
5680
5681        // The one of the four that has no arithmetic to overflow. Redis takes
5682        // it and holds the number as given, and a record here holds forty six
5683        // bits, so it lands in the year 4199 instead. D-17.
5684        assert_eq!(
5685            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
5686            ":1\r\n"
5687        );
5688        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
5689    }
5690
5691    #[test]
5692    fn flushing_empties_this_database_or_every_one_of_them() {
5693        let mut f = Fixture::new();
5694        f.run(&[b"SELECT", b"0"]);
5695        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
5696        f.run(&[b"SELECT", b"1"]);
5697        f.run(&[b"SET", b"c", b"3"]);
5698        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
5699        // ASYNC and SYNC are both taken and neither changes anything, since the
5700        // keyspace is empty before the OK goes out either way.
5701        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
5702        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5703        // Only database one was emptied.
5704        f.run(&[b"SELECT", b"0"]);
5705        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
5706        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
5707        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5708        f.run(&[b"SELECT", b"1"]);
5709        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5710        // Anything else after the name is a syntax error, and so is a third
5711        // argument even when the second one is a word we take.
5712        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
5713        assert_eq!(
5714            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
5715            "-ERR syntax error\r\n"
5716        );
5717    }
5718
5719    #[test]
5720    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
5721        let mut f = Fixture::new();
5722        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
5723        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
5724        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
5725        // Nothing is cached, so nothing is there, one answer per hash asked
5726        // about.
5727        assert_eq!(
5728            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
5729            "*2\r\n:0\r\n:0\r\n"
5730        );
5731        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
5732        assert_eq!(
5733            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
5734            "*0\r\n"
5735        );
5736        assert_eq!(
5737            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
5738            "-ERR Library not found\r\n"
5739        );
5740
5741        // Redis's two messages here are its own, one per container, and one of
5742        // them reads like a typo.
5743        assert_eq!(
5744            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
5745            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
5746        );
5747        assert_eq!(
5748            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
5749            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
5750        );
5751        // A second argument after the mode is the generic one instead, because
5752        // the count is checked before the word is looked at. The subcommand in
5753        // the sentence is the client's own spelling and not the canonical one,
5754        // which is the same thing `unknown subcommand` does.
5755        assert_eq!(
5756            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
5757            "-ERR unknown subcommand or wrong number of arguments for 'FLUSH'. Try FUNCTION HELP.\r\n"
5758        );
5759        assert_eq!(
5760            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
5761            "-ERR Unknown argument bogus\r\n"
5762        );
5763        assert_eq!(
5764            f.run(&[b"SCRIPT", b"EXISTS"]),
5765            "-ERR wrong number of arguments for 'script|exists' command\r\n"
5766        );
5767
5768        assert_eq!(
5769            f.run(&[b"FUNCTION", b"NOPE"]),
5770            "-ERR unknown subcommand 'NOPE'. Try FUNCTION HELP.\r\n"
5771        );
5772    }
5773
5774    #[test]
5775    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5776    fn the_script_cache_holds_what_was_loaded_into_it() {
5777        let mut f = Fixture::new();
5778        // The hash is the sha1 of the body and nothing else, so it is the same
5779        // number a real server answers and a client can compute it itself.
5780        let sha = b"e0e1f9fabfc9d4800c877a703b823ac0578ff8db";
5781        assert_eq!(
5782            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
5783            "$40\r\ne0e1f9fabfc9d4800c877a703b823ac0578ff8db\r\n"
5784        );
5785        assert_eq!(f.run(&[b"SCRIPT", b"EXISTS", sha]), "*1\r\n:1\r\n");
5786        assert_eq!(f.run(&[b"EVALSHA", sha, b"0"]), ":1\r\n");
5787        // Loading is idempotent and a body that will not parse is refused
5788        // where it was written rather than where it is called.
5789        assert_eq!(
5790            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
5791            "$40\r\ne0e1f9fabfc9d4800c877a703b823ac0578ff8db\r\n"
5792        );
5793        assert!(
5794            f.run(&[b"SCRIPT", b"LOAD", b"this is not lua"])
5795                .starts_with("-ERR Error compiling script"),
5796        );
5797
5798        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
5799        assert_eq!(f.run(&[b"SCRIPT", b"EXISTS", sha]), "*1\r\n:0\r\n");
5800        assert_eq!(
5801            f.run(&[b"EVALSHA", sha, b"0"]),
5802            "-NOSCRIPT No matching script. Please use EVAL.\r\n"
5803        );
5804
5805        // Running the body puts it in the cache too, which is what makes the
5806        // load then call then fall back to load pattern a client uses work.
5807        assert_eq!(f.run(&[b"EVAL", b"return 1", b"0"]), ":1\r\n");
5808        assert_eq!(f.run(&[b"SCRIPT", b"EXISTS", sha]), "*1\r\n:1\r\n");
5809
5810        // Nothing here can run long enough to be killed, which is D-101, so
5811        // the answer is the one a real server gives when nothing is stuck.
5812        assert_eq!(
5813            f.run(&[b"SCRIPT", b"KILL"]),
5814            "-NOTBUSY No scripts in execution right now.\r\n"
5815        );
5816        assert_eq!(f.run(&[b"SCRIPT", b"DEBUG", b"NO"]), "+OK\r\n");
5817        assert_eq!(f.run(&[b"SCRIPT", b"DEBUG", b"yes"]), "+OK\r\n");
5818        assert_eq!(
5819            f.run(&[b"SCRIPT", b"DEBUG", b"maybe"]),
5820            "-ERR Use SCRIPT DEBUG YES/SYNC/NO\r\n"
5821        );
5822    }
5823
5824    #[test]
5825    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5826    fn eval_counts_its_keys_before_it_compiles_anything() {
5827        let mut f = Fixture::new();
5828        assert_eq!(
5829            f.run(&[b"EVAL", b"return 1"]),
5830            "-ERR wrong number of arguments for 'eval' command\r\n"
5831        );
5832        assert_eq!(
5833            f.run(&[b"EVAL", b"return 1", b"abc"]),
5834            "-ERR value is not an integer or out of range\r\n"
5835        );
5836        assert_eq!(
5837            f.run(&[b"EVAL", b"return 1", b"-1"]),
5838            "-ERR Number of keys can't be negative\r\n"
5839        );
5840        assert_eq!(
5841            f.run(&[b"EVAL", b"return 1", b"1"]),
5842            "-ERR Number of keys can't be greater than number of args\r\n"
5843        );
5844        // The count splits the tail, and everything past the keys is ARGV.
5845        assert_eq!(
5846            f.run(&[
5847                b"EVAL",
5848                b"return {KEYS[1],KEYS[2],ARGV[1]}",
5849                b"2",
5850                b"a",
5851                b"b",
5852                b"c"
5853            ]),
5854            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5855        );
5856        assert_eq!(
5857            f.run(&[b"EVAL", b"return #KEYS", b"0", b"a", b"b"]),
5858            ":0\r\n"
5859        );
5860        assert_eq!(
5861            f.run(&[b"EVAL", b"return #ARGV", b"0", b"a", b"b"]),
5862            ":2\r\n"
5863        );
5864    }
5865
5866    #[test]
5867    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5868    fn a_lua_value_comes_back_as_the_reply_it_maps_to() {
5869        let mut f = Fixture::new();
5870        let eval = |f: &mut Fixture, body: &[u8]| f.run(&[b"EVAL", body, b"0"]);
5871
5872        // A number is truncated toward zero rather than rounded, and the two
5873        // ends of the range saturate the way the cast does.
5874        assert_eq!(eval(&mut f, b"return 3.99"), ":3\r\n");
5875        assert_eq!(eval(&mut f, b"return -3.99"), ":-3\r\n");
5876        assert_eq!(eval(&mut f, b"return 0.5"), ":0\r\n");
5877        assert_eq!(eval(&mut f, b"return 2^63"), ":9223372036854775807\r\n");
5878        assert_eq!(eval(&mut f, b"return -2^63"), ":-9223372036854775808\r\n");
5879        assert_eq!(eval(&mut f, b"return 1/0"), ":9223372036854775807\r\n");
5880        assert_eq!(eval(&mut f, b"return 0/0"), ":0\r\n");
5881
5882        assert_eq!(eval(&mut f, b"return 'hello'"), "$5\r\nhello\r\n");
5883        assert_eq!(eval(&mut f, b"return true"), ":1\r\n");
5884        // Everything that is not there is the same nothing.
5885        assert_eq!(eval(&mut f, b"return false"), "$-1\r\n");
5886        assert_eq!(eval(&mut f, b"return nil"), "$-1\r\n");
5887        assert_eq!(eval(&mut f, b"return"), "$-1\r\n");
5888        assert_eq!(eval(&mut f, b""), "$-1\r\n");
5889
5890        // A table is an array that stops at the first hole, which is what makes
5891        // a script build a reply by appending rather than by indexing.
5892        assert_eq!(eval(&mut f, b"return {}"), "*0\r\n");
5893        assert_eq!(eval(&mut f, b"return {1,2,nil,4}"), "*2\r\n:1\r\n:2\r\n");
5894        assert_eq!(
5895            eval(&mut f, b"return {1,'a',{2}}"),
5896            "*3\r\n:1\r\n$1\r\na\r\n*1\r\n:2\r\n"
5897        );
5898
5899        // The named fields, in the order a real server looks for them.
5900        assert_eq!(eval(&mut f, b"return {ok='fine'}"), "+fine\r\n");
5901        assert_eq!(eval(&mut f, b"return {err='mine'}"), "-mine\r\n");
5902        assert_eq!(eval(&mut f, b"return {err='a', ok='b'}"), "-a\r\n");
5903        assert_eq!(eval(&mut f, b"return {ok='b', double=1.5}"), "+b\r\n");
5904        // A line break inside one of them becomes a space, because the reply is
5905        // a single line and a client that saw the break would lose the frame.
5906        assert_eq!(eval(&mut f, b"return {ok='a\\r\\nb'}"), "+a  b\r\n");
5907        // A field of the wrong type is not that kind of reply at all, and falls
5908        // through to the array walk, which finds nothing.
5909        assert_eq!(eval(&mut f, b"return {ok=1}"), "*0\r\n");
5910        assert_eq!(eval(&mut f, b"return {err={}}"), "*0\r\n");
5911    }
5912
5913    #[test]
5914    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5915    fn the_protocol_the_client_asked_for_is_the_one_a_table_answers_in() {
5916        let mut f = Fixture::new();
5917        // Under RESP2 the four typed tables have to come back as something a
5918        // client that only knows RESP2 can read.
5919        assert_eq!(
5920            f.run(&[b"EVAL", b"return {double=3.5}", b"0"]),
5921            "$3\r\n3.5\r\n"
5922        );
5923        assert_eq!(
5924            f.run(&[b"EVAL", b"return {big_number='123'}", b"0"]),
5925            "$3\r\n123\r\n"
5926        );
5927        assert_eq!(
5928            f.run(&[b"EVAL", b"return {map={a='b'}}", b"0"]),
5929            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
5930        );
5931        assert_eq!(
5932            f.run(&[b"EVAL", b"return {set={a=true}}", b"0"]),
5933            "*1\r\n$1\r\na\r\n"
5934        );
5935        assert_eq!(f.run(&[b"EVAL", b"return false", b"0"]), "$-1\r\n");
5936
5937        f.out = Out::new(Proto::Resp3);
5938        assert_eq!(f.run(&[b"EVAL", b"return {double=3.5}", b"0"]), ",3.5\r\n");
5939        assert_eq!(
5940            f.run(&[b"EVAL", b"return {big_number='123'}", b"0"]),
5941            "(123\r\n"
5942        );
5943        assert_eq!(
5944            f.run(&[b"EVAL", b"return {map={a='b'}}", b"0"]),
5945            "%1\r\n$1\r\na\r\n$1\r\nb\r\n"
5946        );
5947        assert_eq!(
5948            f.run(&[b"EVAL", b"return {set={a=true}}", b"0"]),
5949            "~1\r\n$1\r\na\r\n"
5950        );
5951        assert_eq!(f.run(&[b"EVAL", b"return false", b"0"]), "_\r\n");
5952    }
5953
5954    #[test]
5955    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5956    fn a_reply_comes_back_into_lua_as_the_value_it_maps_to() {
5957        let mut f = Fixture::new();
5958        f.run(&[b"SET", b"s", b"hello"]);
5959        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
5960        let eval = |f: &mut Fixture, body: &[u8]| f.run(&[b"EVAL", body, b"0"]);
5961
5962        assert_eq!(
5963            eval(&mut f, b"return type(redis.call('get','s'))"),
5964            "$6\r\nstring\r\n"
5965        );
5966        assert_eq!(
5967            eval(&mut f, b"return type(redis.call('llen','l'))"),
5968            "$6\r\nnumber\r\n"
5969        );
5970        assert_eq!(
5971            eval(&mut f, b"return type(redis.call('lrange','l',0,-1))"),
5972            "$5\r\ntable\r\n"
5973        );
5974        // A status is a table with one field, which is what lets a script pass
5975        // one straight back out again.
5976        assert_eq!(
5977            eval(&mut f, b"return redis.call('set','s','v')['ok']"),
5978            "$2\r\nOK\r\n"
5979        );
5980        // A missing key is false under RESP2 and nil once the script asks for
5981        // RESP3, which is the one conversion the script gets to choose.
5982        assert_eq!(
5983            eval(&mut f, b"return tostring(redis.call('get','nosuch'))"),
5984            "$5\r\nfalse\r\n"
5985        );
5986        assert_eq!(
5987            eval(
5988                &mut f,
5989                b"redis.setresp(3) return tostring(redis.call('get','nosuch'))"
5990            ),
5991            "$3\r\nnil\r\n"
5992        );
5993        // The choice does not outlive the script that made it.
5994        assert_eq!(
5995            eval(&mut f, b"return tostring(redis.call('get','nosuch'))"),
5996            "$5\r\nfalse\r\n"
5997        );
5998    }
5999
6000    #[test]
6001    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6002    fn an_error_from_a_script_names_the_line_it_came_from() {
6003        let mut f = Fixture::new();
6004        // The position is the script's own, not the prelude's, and the suffix
6005        // names the script so a client can find it in the cache.
6006        assert_eq!(
6007            f.run(&[b"EVAL", b"error('boom')", b"0"]),
6008            "-ERR user_script:1: boom script: \
6009             82903a0434f1503e152f89c03c9acd881a0e8150, on @user_script:1.\r\n"
6010        );
6011        // Level zero says the message already knows where it came from.
6012        assert_eq!(
6013            f.run(&[b"EVAL", b"error('boom', 0)", b"0"]),
6014            "-ERR boom script: 90724e16396e5864c1184910ba6d7440461cee4f, on @user_script:1.\r\n"
6015        );
6016        // A table with an err field keeps its own text and gets the suffix.
6017        assert!(
6018            f.run(&[b"EVAL", b"error({err='structured'})", b"0"])
6019                .starts_with("-structured script: "),
6020        );
6021        // A script that will not parse is refused before it runs, so there is
6022        // no script and nothing to name.
6023        assert_eq!(
6024            f.run(&[b"EVAL", b"return this is not lua", b"0"]),
6025            "-ERR Error compiling script (new function): user_script:1: '<eof>' expected near 'is'\r\n"
6026        );
6027
6028        // A table that came out of pcall is a string by the time the script
6029        // sees it, which is a real server's own wrapping and not Lua's.
6030        assert_eq!(
6031            f.run(&[
6032                b"EVAL",
6033                b"local a, b = pcall(function() error({err='z'}) end) return type(b) .. ':' .. tostring(b)",
6034                b"0"
6035            ]),
6036            "$8\r\nstring:z\r\n"
6037        );
6038        assert_eq!(
6039            f.run(&[
6040                b"EVAL",
6041                b"local a, b = pcall(function() error({a=1}) end) return type(b)",
6042                b"0"
6043            ]),
6044            "$5\r\ntable\r\n"
6045        );
6046    }
6047
6048    #[test]
6049    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6050    fn redis_call_refuses_what_it_cannot_run_and_pcall_hands_it_back() {
6051        let mut f = Fixture::new();
6052        let sentence = |f: &mut Fixture, body: &[u8]| {
6053            let reply = f.run(&[b"EVAL", body, b"0"]);
6054            reply.split(" script: ").next().unwrap().to_owned()
6055        };
6056
6057        assert_eq!(
6058            sentence(&mut f, b"return redis.call()"),
6059            "-ERR Please specify at least one argument for this redis lib call"
6060        );
6061        assert_eq!(
6062            sentence(&mut f, b"return redis.call('get', {})"),
6063            "-ERR Lua redis lib command arguments must be strings or integers"
6064        );
6065        assert_eq!(
6066            sentence(&mut f, b"return redis.call('nosuchcmd')"),
6067            "-ERR Unknown Redis command called from script"
6068        );
6069        assert_eq!(
6070            sentence(&mut f, b"return redis.call('get')"),
6071            "-ERR Wrong number of args calling Redis command from script"
6072        );
6073        // The commands that make no sense inside a script are refused by name
6074        // rather than by not being implemented, so the sentence is the same one
6075        // a real server writes for each of them.
6076        for name in [
6077            &b"return redis.call('multi')"[..],
6078            b"return redis.call('exec')",
6079            b"return redis.call('watch','k')",
6080            b"return redis.call('subscribe','c')",
6081            b"return redis.call('debug','jmap')",
6082            b"return redis.call('eval','return 1',0)",
6083            b"return redis.call('config','get','maxmemory')",
6084        ] {
6085            assert_eq!(
6086                sentence(&mut f, name),
6087                "-ERR This Redis command is not allowed from script",
6088                "for {}",
6089                String::from_utf8_lossy(name)
6090            );
6091        }
6092        // HELP is the one subcommand of a refused container that is allowed,
6093        // because it reads nothing and changes nothing.
6094        assert!(
6095            f.run(&[b"EVAL", b"return redis.call('config','help')", b"0"])
6096                .starts_with('*'),
6097        );
6098
6099        // pcall answers the same sentence as a value instead of raising it, and
6100        // the value has an err field a script can read.
6101        assert_eq!(
6102            f.run(&[
6103                b"EVAL",
6104                b"local x = redis.pcall('nosuchcmd') return x.err",
6105                b"0"
6106            ]),
6107            "$44\r\nERR Unknown Redis command called from script\r\n"
6108        );
6109        // Returning it unread raises it, because the table has an err field.
6110        assert_eq!(
6111            f.run(&[b"EVAL", b"return redis.pcall('nosuchcmd')", b"0"]),
6112            "-ERR Unknown Redis command called from script\r\n"
6113        );
6114    }
6115
6116    #[test]
6117    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6118    fn a_read_only_script_is_stopped_at_the_write_and_not_at_the_door() {
6119        let mut f = Fixture::new();
6120        f.run(&[b"SET", b"k", b"v"]);
6121        assert_eq!(
6122            f.run(&[b"EVAL_RO", b"return redis.call('get', KEYS[1])", b"1", b"k"]),
6123            "$1\r\nv\r\n"
6124        );
6125        assert!(
6126            f.run(&[
6127                b"EVAL_RO",
6128                b"return redis.call('set', KEYS[1], 'x')",
6129                b"1",
6130                b"k"
6131            ])
6132            .starts_with("-ERR Write commands are not allowed from read-only scripts."),
6133        );
6134        // The write did not happen, and the same body under EVAL does.
6135        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
6136        assert_eq!(
6137            f.run(&[
6138                b"EVAL",
6139                b"return redis.call('set', KEYS[1], 'x')",
6140                b"1",
6141                b"k"
6142            ]),
6143            "+OK\r\n"
6144        );
6145        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nx\r\n");
6146
6147        // EVALSHA_RO runs a cached body under the same rule.
6148        let sha = b"e0e1f9fabfc9d4800c877a703b823ac0578ff8db";
6149        f.run(&[b"SCRIPT", b"LOAD", b"return 1"]);
6150        assert_eq!(f.run(&[b"EVALSHA_RO", sha, b"0"]), ":1\r\n");
6151    }
6152
6153    #[test]
6154    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6155    fn a_script_cannot_leave_anything_behind_for_the_next_one() {
6156        let mut f = Fixture::new();
6157        // A plain global write and a write through a name on the redis table
6158        // both raise, with the position the script wrote them at.
6159        for body in [&b"x = 1"[..], b"pcall = 1", b"redis = 1", b"redis.call = 1"] {
6160            let reply = f.run(&[b"EVAL", body, b"0"]);
6161            assert!(
6162                reply
6163                    .starts_with("-ERR user_script:1: Attempt to modify a readonly table script: "),
6164                "{body:?} gave {reply}",
6165            );
6166        }
6167        // Walking round the guard with rawset or setmetatable raises too, and
6168        // without the position, which is where a real server raises it from.
6169        for body in [
6170            &b"rawset(redis, 'call', 1)"[..],
6171            b"rawset(_G, 'zz', 1)",
6172            b"setmetatable(_G, {})",
6173            b"setmetatable(redis, {})",
6174        ] {
6175            let reply = f.run(&[b"EVAL", body, b"0"]);
6176            assert!(
6177                reply.starts_with("-ERR Attempt to modify a readonly table script: "),
6178                "{body:?} gave {reply}",
6179            );
6180        }
6181        // Reading a name that is not there is a mistake rather than a nil, so a
6182        // misspelled global stops the script instead of doing nothing quietly.
6183        assert!(
6184            f.run(&[b"EVAL", b"return nosuchglobal", b"0"])
6185                .contains("Script attempted to access nonexistent global variable 'nosuchglobal'"),
6186        );
6187        // Reading a name that is not on the redis table is a nil, which is how
6188        // a script tests for a helper that an older server does not have.
6189        assert_eq!(
6190            f.run(&[b"EVAL", b"return tostring(redis.nosuchfield)", b"0"]),
6191            "$3\r\nnil\r\n"
6192        );
6193
6194        // The one write that lands, D-103, is taken back out before the next
6195        // script starts, so nothing a script does reaches the one after it.
6196        assert_eq!(f.run(&[b"EVAL", b"_G.pcall = 1 return 1", b"0"]), ":1\r\n");
6197        assert_eq!(
6198            f.run(&[b"EVAL", b"return type(pcall)", b"0"]),
6199            "$8\r\nfunction\r\n"
6200        );
6201        assert_eq!(
6202            f.run(&[b"EVAL", b"return type(redis.call)", b"0"]),
6203            "$8\r\nfunction\r\n"
6204        );
6205    }
6206
6207    #[test]
6208    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6209    fn a_script_can_walk_the_redis_table_it_is_not_allowed_to_write_to() {
6210        let mut f = Fixture::new();
6211        // The guard in front of the table is empty, so the three base library
6212        // readers that skip a metatable are pointed at the real table behind
6213        // it. A script counts what a real server counts.
6214        assert_eq!(
6215            f.run(&[
6216                b"EVAL",
6217                b"local n = 0 for k in pairs(redis) do n = n + 1 end return n",
6218                b"0",
6219            ]),
6220            ":23\r\n"
6221        );
6222        assert_eq!(
6223            f.run(&[
6224                b"EVAL",
6225                b"local t = {} for k in pairs(redis) do t[#t+1] = k end \
6226                  table.sort(t) return table.concat(t, ' ')",
6227                b"0",
6228            ]),
6229            "$243\r\nLOG_DEBUG LOG_NOTICE LOG_VERBOSE LOG_WARNING REDIS_VERSION \
6230             REDIS_VERSION_NUM REPL_ALL REPL_AOF REPL_NONE REPL_REPLICA REPL_SLAVE \
6231             acl_check_cmd breakpoint call debug error_reply log pcall replicate_commands \
6232             set_repl setresp sha1hex status_reply\r\n"
6233        );
6234        // The loop hands over the values as well as the names, so the twelve
6235        // helpers are callable from inside a traversal and not just findable.
6236        assert_eq!(
6237            f.run(&[
6238                b"EVAL",
6239                b"local n = 0 for k, v in pairs(redis) do \
6240                  if type(v) == 'function' then n = n + 1 end end return n",
6241                b"0",
6242            ]),
6243            ":12\r\n"
6244        );
6245        // The other two readers agree with it.
6246        assert_eq!(
6247            f.run(&[b"EVAL", b"return type(next(redis))", b"0"]),
6248            "$6\r\nstring\r\n"
6249        );
6250        assert_eq!(
6251            f.run(&[b"EVAL", b"return type(rawget(redis, 'call'))", b"0"]),
6252            "$8\r\nfunction\r\n"
6253        );
6254        assert_eq!(
6255            f.run(&[
6256                b"EVAL",
6257                b"return tostring(rawget(redis, 'nosuchfield'))",
6258                b"0",
6259            ]),
6260            "$3\r\nnil\r\n"
6261        );
6262        // Reading round the guard is the only thing that was given back. A
6263        // write still lands on the guard and still raises.
6264        for body in [&b"redis.call = 1"[..], b"rawset(redis, 'call', 1)"] {
6265            assert!(
6266                f.run(&[b"EVAL", body, b"0"])
6267                    .contains("Attempt to modify a readonly table script: "),
6268                "{body:?}",
6269            );
6270        }
6271        // A table nobody guards walks the way it always did, whether a script
6272        // made it or the standard library did.
6273        assert_eq!(
6274            f.run(&[
6275                b"EVAL",
6276                b"local t = {a=1,b=2} local n = 0 for k in pairs(t) do n = n + 1 end return n",
6277                b"0",
6278            ]),
6279            ":2\r\n"
6280        );
6281        assert_eq!(
6282            f.run(&[b"EVAL", b"return tostring(next({}))", b"0"]),
6283            "$3\r\nnil\r\n"
6284        );
6285        assert_eq!(
6286            f.run(&[
6287                b"EVAL",
6288                b"local f for k, v in pairs(string) do if k == 'sub' then f = v end end \
6289                  return type(f)",
6290                b"0",
6291            ]),
6292            "$8\r\nfunction\r\n"
6293        );
6294    }
6295
6296    #[test]
6297    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6298    fn a_script_gets_the_bit_library_a_real_server_carries() {
6299        let mut f = Fixture::new();
6300        // Every answer is a signed word, which is why the ones past two to the
6301        // thirty one come back negative.
6302        for (body, want) in [
6303            ("bit.tobit(1)", ":1\r\n"),
6304            ("bit.tobit(2^32 + 1)", ":1\r\n"),
6305            ("bit.tobit(2^31)", ":-2147483648\r\n"),
6306            ("bit.tobit(0xffffffff)", ":-1\r\n"),
6307            // The rounding is to the nearest and not toward zero.
6308            ("bit.tobit(1.5)", ":2\r\n"),
6309            ("bit.tobit(2.5)", ":2\r\n"),
6310            ("bit.bnot(0)", ":-1\r\n"),
6311            ("bit.band(0xff, 0x0f)", ":15\r\n"),
6312            ("bit.band(1, 2, 3)", ":0\r\n"),
6313            ("bit.bor(1, 2, 4)", ":7\r\n"),
6314            ("bit.bxor(0xff, 0x0f)", ":240\r\n"),
6315            // Only the low five bits of a count are read.
6316            ("bit.lshift(1, 31)", ":-2147483648\r\n"),
6317            ("bit.lshift(1, 32)", ":1\r\n"),
6318            ("bit.lshift(1, 33)", ":2\r\n"),
6319            ("bit.rshift(-1, 1)", ":2147483647\r\n"),
6320            ("bit.arshift(-1, 1)", ":-1\r\n"),
6321            ("bit.rol(0x12345678, 8)", ":878082066\r\n"),
6322            ("bit.ror(0x12345678, 8)", ":2014458966\r\n"),
6323            ("bit.bswap(0x12345678)", ":2018915346\r\n"),
6324            // A string that reads as a number is a number, which is Lua's rule
6325            // and not a courtesy of this library.
6326            ("bit.tobit('0x10')", ":16\r\n"),
6327        ] {
6328            let script = format!("return {body}");
6329            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6330        }
6331        // The digits are the low ones, a negative count asks for upper case,
6332        // and a count outside eight is brought back to it.
6333        for (body, want) in [
6334            ("bit.tohex(1)", "00000001"),
6335            ("bit.tohex(-1)", "ffffffff"),
6336            ("bit.tohex(255, 2)", "ff"),
6337            ("bit.tohex(255, -8)", "000000FF"),
6338            ("bit.tohex(0x87654321, 4)", "4321"),
6339            ("bit.tohex(1, 0)", ""),
6340            ("bit.tohex(1, 9)", "00000001"),
6341        ] {
6342            let script = format!("return {body}");
6343            assert_eq!(
6344                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6345                format!("${}\r\n{want}\r\n", want.len()),
6346                "{body}",
6347            );
6348        }
6349        // A bad argument names the position, the function and what was passed,
6350        // and the line in front of it is the script's own.
6351        for (body, want) in [
6352            (
6353                "return bit.band()",
6354                "bad argument #1 to 'band' (number expected, got no value)",
6355            ),
6356            (
6357                "return bit.band('x')",
6358                "bad argument #1 to 'band' (number expected, got string)",
6359            ),
6360            (
6361                "return bit.tobit(true)",
6362                "bad argument #1 to 'tobit' (number expected, got boolean)",
6363            ),
6364            (
6365                "return bit.lshift(1)",
6366                "bad argument #2 to 'lshift' (number expected, got no value)",
6367            ),
6368        ] {
6369            let reply = f.run(&[b"EVAL", body.as_bytes(), b"0"]);
6370            assert!(
6371                reply.starts_with(&format!("-ERR user_script:1: {want} script: ")),
6372                "{body} gave {reply}",
6373            );
6374        }
6375        // The name in the message is the one the call site used, so a call that
6376        // went through `pcall` has no name to report.
6377        assert_eq!(
6378            f.run(&[
6379                b"EVAL",
6380                b"local ok, e = pcall(bit.band, 'x') return tostring(e)",
6381                b"0",
6382            ]),
6383            "$52\r\nbad argument #1 to '?' (number expected, got string)\r\n"
6384        );
6385        // The table is readable and not writable, the same as `redis`.
6386        assert_eq!(
6387            f.run(&[
6388                b"EVAL",
6389                b"local t = {} for k in pairs(bit) do t[#t+1] = k end \
6390                  table.sort(t) return table.concat(t, ' ')",
6391                b"0",
6392            ]),
6393            "$66\r\narshift band bnot bor bswap bxor lshift rol ror rshift tobit tohex\r\n"
6394        );
6395        for body in [&b"bit.band = 1"[..], b"rawset(bit, 'zz', 1)"] {
6396            assert!(
6397                f.run(&[b"EVAL", body, b"0"])
6398                    .contains("Attempt to modify a readonly table script: "),
6399                "{body:?}",
6400            );
6401        }
6402    }
6403
6404    #[test]
6405    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6406    fn a_script_gets_the_cjson_library_a_real_server_carries() {
6407        let mut f = Fixture::new();
6408        // Encoding, including the three shapes nobody guesses right: an empty
6409        // table is an object, a number is fourteen significant digits, and a
6410        // hole in an array is a null rather than a shorter array.
6411        for (body, want) in [
6412            ("cjson.encode(nil)", "null"),
6413            ("cjson.encode(true)", "true"),
6414            ("cjson.encode(cjson.null)", "null"),
6415            ("cjson.encode(100)", "100"),
6416            ("cjson.encode(1/3)", "0.33333333333333"),
6417            ("cjson.encode(1e300)", "1e+300"),
6418            ("cjson.encode(2^53)", "9.007199254741e+15"),
6419            ("cjson.encode({})", "{}"),
6420            ("cjson.encode({1,2,3})", "[1,2,3]"),
6421            ("cjson.encode({a=1})", "{\"a\":1}"),
6422            ("cjson.encode({[1]=1,[3]=3})", "[1,null,3]"),
6423            ("cjson.encode({[0]=1})", "{\"0\":1}"),
6424            ("cjson.encode('a\\nb')", "\"a\\nb\""),
6425            // A tab and a backslash have short escapes, a vertical tab does not.
6426            ("cjson.encode('\\t\\\\')", "\"\\t\\\\\""),
6427            ("cjson.encode('\\11')", "\"\\u000b\""),
6428            // Reading and writing again is the shortest way to say the decoder
6429            // built what the encoder expected.
6430            (
6431                "cjson.encode(cjson.decode('[1,[2,{\"a\":null}]]'))",
6432                "[1,[2,{\"a\":null}]]",
6433            ),
6434            // An empty array comes back as an object, because a table with
6435            // nothing in it has nothing to say about which it was.
6436            ("cjson.encode(cjson.decode('[]'))", "{}"),
6437        ] {
6438            let script = format!("return {body}");
6439            assert_eq!(
6440                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6441                format!("${}\r\n{want}\r\n", want.len()),
6442                "{body}",
6443            );
6444        }
6445        // Decoding, where the leniency about numbers is on by default and a
6446        // null is a value of its own rather than a missing key.
6447        for (body, want) in [
6448            ("cjson.decode('[1,2,3]')[2]", ":2\r\n"),
6449            ("cjson.decode('{\"a\":41}').a + 1", ":42\r\n"),
6450            ("cjson.decode('0x10')", ":16\r\n"),
6451            ("cjson.decode('+1')", ":1\r\n"),
6452            ("cjson.decode('01')", ":1\r\n"),
6453            ("cjson.decode(1) + 1", ":2\r\n"),
6454            // A long bracket, because Lua 5.1 would eat the backslash first.
6455            ("cjson.decode([[\"\\u0041\"]]) == 'A' and 1 or 0", ":1\r\n"),
6456            ("cjson.decode('null') == cjson.null and 1 or 0", ":1\r\n"),
6457            ("cjson.decode('null') == nil and 1 or 0", ":0\r\n"),
6458        ] {
6459            let script = format!("return {body}");
6460            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6461        }
6462        // The settings, each of which answers with what it now holds.
6463        for (body, want) in [
6464            (
6465                "cjson.encode_number_precision(3) return cjson.encode(1/3)",
6466                "0.333",
6467            ),
6468            (
6469                "cjson.encode_invalid_numbers('null') return cjson.encode(1/0)",
6470                "null",
6471            ),
6472            (
6473                "cjson.encode_invalid_numbers(true) return cjson.encode(1/0)",
6474                "inf",
6475            ),
6476            (
6477                "cjson.encode_sparse_array(true) return cjson.encode({[1]=1,[100]=1})",
6478                "{\"1\":1,\"100\":1}",
6479            ),
6480            (
6481                "cjson.decode_array_with_array_mt(true) return cjson.encode(cjson.decode('[]'))",
6482                "[]",
6483            ),
6484            ("return tostring(cjson.encode_max_depth())", "1000"),
6485            ("return tostring(cjson.encode_keep_buffer(false))", "false"),
6486            ("return tostring(cjson.encode_sparse_array())", "false"),
6487            // A setting one script changed is not a setting the next one sees,
6488            // which is D-105.
6489            ("return tostring(cjson.encode_number_precision())", "14"),
6490        ] {
6491            assert_eq!(
6492                f.run(&[b"EVAL", body.as_bytes(), b"0"]),
6493                format!("${}\r\n{want}\r\n", want.len()),
6494                "{body}",
6495            );
6496        }
6497        // A failure names what stopped it and, when it was the text, where.
6498        for (body, want) in [
6499            (
6500                "return cjson.encode(1/0)",
6501                "Cannot serialise number: must not be NaN or Inf",
6502            ),
6503            (
6504                "return cjson.encode({[1]=1,[100]=1})",
6505                "Cannot serialise table: excessively sparse array",
6506            ),
6507            (
6508                "return cjson.encode({[true]=1})",
6509                "Cannot serialise boolean: table key must be a number or string",
6510            ),
6511            (
6512                "return cjson.encode(tostring)",
6513                "Cannot serialise function: type not supported",
6514            ),
6515            (
6516                "return cjson.encode()",
6517                "bad argument #1 to 'encode' (expected 1 argument)",
6518            ),
6519            (
6520                "return cjson.decode('[1,2')",
6521                "Expected comma or array end but found T_END at character 5",
6522            ),
6523            (
6524                "return cjson.decode('{\"a\" 1}')",
6525                "Expected colon but found T_NUMBER at character 6",
6526            ),
6527            (
6528                "return cjson.decode('tru')",
6529                "Expected value but found invalid token at character 1",
6530            ),
6531            (
6532                "return cjson.decode('[1] 2')",
6533                "Expected the end but found T_NUMBER at character 5",
6534            ),
6535            (
6536                "return cjson.encode_max_depth(0)",
6537                "bad argument #1 to 'encode_max_depth' (expected integer between 1 and 2147483647)",
6538            ),
6539            (
6540                "return cjson.encode_invalid_numbers('yes')",
6541                "bad argument #1 to 'encode_invalid_numbers' (invalid option 'yes')",
6542            ),
6543            (
6544                "return cjson.encode_max_depth(1, 2)",
6545                "bad argument #2 to 'encode_max_depth' (found too many arguments)",
6546            ),
6547        ] {
6548            let reply = f.run(&[b"EVAL", body.as_bytes(), b"0"]);
6549            assert!(
6550                reply.starts_with(&format!("-ERR user_script:1: {want} script: ")),
6551                "{body} gave {reply}",
6552            );
6553        }
6554        // A module of its own, with settings of its own and no guard on it,
6555        // which is what a real server hands back.
6556        assert_eq!(
6557            f.run(&[
6558                b"EVAL",
6559                b"local n = cjson.new() n.encode_number_precision(3) \
6560                  return cjson.encode(1/3) .. ' ' .. n.encode(1/3)",
6561                b"0",
6562            ]),
6563            "$22\r\n0.33333333333333 0.333\r\n"
6564        );
6565        // The table is readable and not writable, the same as `redis`.
6566        let names = "_NAME _VERSION decode decode_array_with_array_mt decode_invalid_numbers \
6567                     decode_max_depth encode encode_invalid_numbers encode_keep_buffer \
6568                     encode_max_depth encode_number_precision encode_sparse_array new null";
6569        assert_eq!(
6570            f.run(&[
6571                b"EVAL",
6572                b"local t = {} for k in pairs(cjson) do t[#t+1] = k end \
6573                  table.sort(t) return table.concat(t, ' ')",
6574                b"0",
6575            ]),
6576            format!("${}\r\n{names}\r\n", names.len())
6577        );
6578        for body in [&b"cjson.encode = 1"[..], b"rawset(cjson, 'zz', 1)"] {
6579            assert!(
6580                f.run(&[b"EVAL", body, b"0"])
6581                    .contains("Attempt to modify a readonly table script: "),
6582                "{body:?}",
6583            );
6584        }
6585    }
6586
6587    #[test]
6588    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6589    fn a_script_gets_the_struct_library_a_real_server_carries() {
6590        let mut f = Fixture::new();
6591        // Packing, where the sizes are the ones a sixty four bit build gives
6592        // and the order is the machine's own unless the format says otherwise.
6593        for (body, want) in [
6594            ("#struct.pack('i4', 1)", ":4\r\n"),
6595            ("#struct.pack('l', 1)", ":8\r\n"),
6596            ("#struct.pack('d', 1)", ":8\r\n"),
6597            ("#struct.pack('f', 1)", ":4\r\n"),
6598            ("#struct.pack('s', 'abc')", ":4\r\n"),
6599            ("#struct.pack('c3', 'abcdef')", ":3\r\n"),
6600            ("#struct.pack('x')", ":1\r\n"),
6601            ("string.byte(struct.pack('i4', 1), 1)", ":1\r\n"),
6602            ("string.byte(struct.pack('>i4', 1), 4)", ":1\r\n"),
6603            ("string.byte(struct.pack('<i4', 1), 1)", ":1\r\n"),
6604            // Past eight bytes the C shifts an unsigned long off the end, so
6605            // the rest of the bytes are zero and a negative is not carried.
6606            ("string.byte(struct.pack('i16', -1), 9)", ":0\r\n"),
6607            ("string.byte(struct.pack('i8', -1), 8)", ":255\r\n"),
6608            // A count of zero on `c` writes the whole string, `s` adds the
6609            // terminator, and `x` writes a zero byte nobody reads back.
6610            ("#struct.pack('c0', 'abcd')", ":4\r\n"),
6611            ("string.byte(struct.pack('s', 'a'), 2)", ":0\r\n"),
6612            ("string.byte(struct.pack('bxb', 1, 2), 2)", ":0\r\n"),
6613        ] {
6614            let script = format!("return {body}");
6615            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6616        }
6617        // Sizes, including the two the C is lenient about: an unknown letter
6618        // and a bare digit are both nothing at all rather than a complaint.
6619        for (body, want) in [
6620            ("struct.size('i')", ":4\r\n"),
6621            ("struct.size('l')", ":8\r\n"),
6622            ("struct.size('T')", ":8\r\n"),
6623            ("struct.size('h')", ":2\r\n"),
6624            ("struct.size('c10')", ":10\r\n"),
6625            ("struct.size('ic')", ":5\r\n"),
6626            ("struct.size('!8ic')", ":5\r\n"),
6627            ("struct.size('!4i')", ":4\r\n"),
6628            // Nothing is padded until `!` turns alignment on, and then a
6629            // double is pushed out to the next eight byte boundary.
6630            ("struct.size('bd')", ":9\r\n"),
6631            ("struct.size('!bd')", ":16\r\n"),
6632            ("struct.size('A')", ":0\r\n"),
6633            ("struct.size('7')", ":0\r\n"),
6634        ] {
6635            let script = format!("return {body}");
6636            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6637        }
6638        // Unpacking, which hands back the values and then where it stopped, so
6639        // the last number can be passed straight back in as the next offset.
6640        for (body, want) in [
6641            ("select('#', struct.unpack('i4', '\\1\\0\\0\\0'))", ":2\r\n"),
6642            ("select(1, struct.unpack('i4', '\\1\\0\\0\\0'))", ":1\r\n"),
6643            ("select(2, struct.unpack('i4', '\\1\\0\\0\\0'))", ":5\r\n"),
6644            ("select(1, struct.unpack('i1', '\\255'))", ":-1\r\n"),
6645            ("select(1, struct.unpack('I1', '\\255'))", ":255\r\n"),
6646            (
6647                "select(1, struct.unpack('i4', struct.pack('i4', -70000)))",
6648                ":-70000\r\n",
6649            ),
6650            ("select(2, struct.unpack('i1', 'abc', 2))", ":3\r\n"),
6651            // A `c0` takes its length from the value read just before it and
6652            // swallows it, so one byte says how long the next three are and
6653            // only the string and the position come back.
6654            ("select('#', struct.unpack('bc0', '\\3abcd'))", ":2\r\n"),
6655            ("select(2, struct.unpack('bc0', '\\3abcd'))", ":5\r\n"),
6656        ] {
6657            let script = format!("return {body}");
6658            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6659        }
6660        for (body, want) in [
6661            ("select(1, struct.unpack('bc0', '\\3abcd'))", "abc"),
6662            ("select(1, struct.unpack('s', 'ab\\0cd'))", "ab"),
6663            ("select(1, struct.unpack('c3', 'abcdef'))", "abc"),
6664        ] {
6665            let script = format!("return {body}");
6666            assert_eq!(
6667                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6668                format!("${}\r\n{want}\r\n", want.len()),
6669                "{body}",
6670            );
6671        }
6672        // A failure names the argument the C names, which is not always the
6673        // argument a reader would pick.
6674        for (body, want) in [
6675            (
6676                "return struct.pack()",
6677                "bad argument #1 to 'pack' (string expected, got no value)",
6678            ),
6679            // The C pushes a nil before it reads anything, so a missing value
6680            // is a nil rather than nothing at all.
6681            (
6682                "return struct.pack('i4')",
6683                "bad argument #2 to 'pack' (number expected, got nil)",
6684            ),
6685            // And it reads the string with a post increment before it checks
6686            // the length, so the number here is one past the real argument.
6687            (
6688                "return struct.pack('c6', 'abc')",
6689                "bad argument #3 to 'pack' (string too short)",
6690            ),
6691            (
6692                "return struct.pack('A', 'x')",
6693                "bad argument #1 to 'pack' (invalid format option 'A')",
6694            ),
6695            (
6696                "return struct.pack('i33', 1)",
6697                "integral size 33 is larger than limit of 32",
6698            ),
6699            (
6700                "return struct.pack('!3i', 1)",
6701                "alignment 3 is not a power of 2",
6702            ),
6703            (
6704                "return struct.unpack()",
6705                "bad argument #1 to 'unpack' (string expected, got no value)",
6706            ),
6707            (
6708                "return struct.unpack('i4')",
6709                "bad argument #2 to 'unpack' (string expected, got no value)",
6710            ),
6711            (
6712                "return struct.unpack('i4', 'ab')",
6713                "bad argument #2 to 'unpack' (data string too short)",
6714            ),
6715            (
6716                "return struct.unpack('i1', 'abc', 0)",
6717                "bad argument #3 to 'unpack' (offset must be 1 or greater)",
6718            ),
6719            (
6720                "return struct.unpack('c0', 'abc')",
6721                "format 'c0' needs a previous size",
6722            ),
6723            (
6724                "return struct.unpack('s', 'abc')",
6725                "unfinished string in data",
6726            ),
6727            (
6728                "return struct.size()",
6729                "bad argument #1 to 'size' (string expected, got no value)",
6730            ),
6731            (
6732                "return struct.size('s')",
6733                "bad argument #1 to 'size' (option 's' has no fixed size)",
6734            ),
6735            (
6736                "return struct.size('c0')",
6737                "bad argument #1 to 'size' (option 'c0' has no fixed size)",
6738            ),
6739        ] {
6740            let reply = f.run(&[b"EVAL", body.as_bytes(), b"0"]);
6741            assert!(
6742                reply.starts_with(&format!("-ERR user_script:1: {want} script: ")),
6743                "{body} gave {reply}",
6744            );
6745        }
6746        // Three members and no version, which is all the C registers.
6747        let names = "pack size unpack";
6748        assert_eq!(
6749            f.run(&[
6750                b"EVAL",
6751                b"local t = {} for k in pairs(struct) do t[#t+1] = k end \
6752                  table.sort(t) return table.concat(t, ' ')",
6753                b"0",
6754            ]),
6755            format!("${}\r\n{names}\r\n", names.len())
6756        );
6757        for body in [&b"struct.pack = 1"[..], b"rawset(struct, 'zz', 1)"] {
6758            assert!(
6759                f.run(&[b"EVAL", body, b"0"])
6760                    .contains("Attempt to modify a readonly table script: "),
6761                "{body:?}",
6762            );
6763        }
6764    }
6765
6766    #[test]
6767    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6768    fn a_script_gets_the_cmsgpack_library_a_real_server_carries() {
6769        let mut f = Fixture::new();
6770        // Every value goes out in the shortest form that holds it, and several
6771        // arguments are packed one after another into one string.
6772        let hex = "local function hx(s) return (string.gsub(s, '.', \
6773                   function(c) return string.format('%02x', string.byte(c)) end)) end ";
6774        for (body, want) in [
6775            ("cmsgpack.pack(nil)", "c0"),
6776            ("cmsgpack.pack(true)", "c3"),
6777            ("cmsgpack.pack(false)", "c2"),
6778            ("cmsgpack.pack(0)", "00"),
6779            ("cmsgpack.pack(127)", "7f"),
6780            ("cmsgpack.pack(128)", "cc80"),
6781            ("cmsgpack.pack(-1)", "ff"),
6782            ("cmsgpack.pack(-33)", "d0df"),
6783            ("cmsgpack.pack(65535)", "cdffff"),
6784            ("cmsgpack.pack(4294967296)", "cf0000000100000000"),
6785            ("cmsgpack.pack(2^53)", "cf0020000000000000"),
6786            ("cmsgpack.pack(-2^63)", "d38000000000000000"),
6787            // Past what an integer holds it is a number again, and a number
6788            // goes out narrow whenever four bytes give it back unchanged.
6789            ("cmsgpack.pack(2^64)", "ca5f800000"),
6790            ("cmsgpack.pack(1.5)", "ca3fc00000"),
6791            ("cmsgpack.pack(0.1)", "cb3fb999999999999a"),
6792            ("cmsgpack.pack('abc')", "a3616263"),
6793            ("cmsgpack.pack('')", "a0"),
6794            ("cmsgpack.pack({})", "90"),
6795            ("cmsgpack.pack({1, 2})", "920102"),
6796            ("cmsgpack.pack({a = 1})", "81a16101"),
6797            ("cmsgpack.pack(1, 'a', true)", "01a161c3"),
6798            // Sixteen levels of table are packed and the seventeenth is a nil,
6799            // which is what the C does rather than refusing the whole thing.
6800            (
6801                "(function() local t = {} local c = t \
6802                 for i = 1, 20 do c.n = {} c = c.n end return cmsgpack.pack(t) end)()",
6803                "81a16e81a16e81a16e81a16e81a16e81a16e81a16e81a16e\
6804                 81a16e81a16e81a16e81a16e81a16e81a16e81a16e81a16ec0",
6805            ),
6806        ] {
6807            let script = format!("{hex} return hx({body})");
6808            assert_eq!(
6809                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6810                format!("${}\r\n{want}\r\n", want.len()),
6811                "{body}",
6812            );
6813        }
6814        // Unpacking reads the whole stream, so a string holding three values
6815        // hands back three. The two that take an offset put where they got to
6816        // in front of the values, and answer minus one when nothing is left.
6817        for (body, want) in [
6818            ("cmsgpack.unpack(cmsgpack.pack(42))", 42),
6819            ("select('#', cmsgpack.unpack('\\1\\2\\3'))", 3),
6820            ("select(3, cmsgpack.unpack('\\1\\2\\3'))", 3),
6821            ("select('#', cmsgpack.unpack(''))", 0),
6822            ("select('#', cmsgpack.unpack_one('\\1\\2\\3'))", 2),
6823            ("select(1, cmsgpack.unpack_one('\\1\\2\\3'))", 1),
6824            ("select(2, cmsgpack.unpack_one('\\1\\2\\3'))", 1),
6825            ("select(1, cmsgpack.unpack_one('\\1\\2\\3', 2))", -1),
6826            ("select(1, cmsgpack.unpack_one('\\1'))", -1),
6827            ("select(1, cmsgpack.unpack_one('', 0))", -1),
6828            ("select('#', cmsgpack.unpack_limit('\\1\\2\\3', 2))", 3),
6829            ("select(1, cmsgpack.unpack_limit('\\1\\2\\3', 2))", 2),
6830            // A limit of nothing at all takes the read everything path, which
6831            // has no offset in front of it.
6832            ("select('#', cmsgpack.unpack_limit('\\1\\2\\3', 0, 0))", 3),
6833            ("cmsgpack.unpack(cmsgpack.pack({1, 2, 3}))[2]", 2),
6834        ] {
6835            let script = format!("return {body}");
6836            assert_eq!(
6837                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6838                format!(":{want}\r\n"),
6839                "{body}",
6840            );
6841        }
6842        for (body, want) in [
6843            ("cmsgpack.unpack(cmsgpack.pack({a = 'b'})).a", "b"),
6844            ("tostring(cmsgpack.unpack(cmsgpack.pack(1.5)))", "1.5"),
6845            ("tostring(cmsgpack.unpack(cmsgpack.pack(nil)))", "nil"),
6846            (
6847                "tostring(cmsgpack.unpack(string.char(0xcb, 0x7f, 0xf0, 0, 0, 0, 0, 0, 0)))",
6848                "inf",
6849            ),
6850            ("cmsgpack._NAME", "cmsgpack"),
6851            ("cmsgpack._VERSION", "lua-cmsgpack 0.4.0"),
6852            (
6853                "cmsgpack._COPYRIGHT",
6854                "Copyright (C) 2012, Salvatore Sanfilippo",
6855            ),
6856            (
6857                "cmsgpack._DESCRIPTION",
6858                "MessagePack C implementation for Lua",
6859            ),
6860        ] {
6861            let script = format!("return {body}");
6862            assert_eq!(
6863                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6864                format!("${}\r\n{want}\r\n", want.len()),
6865                "{body}",
6866            );
6867        }
6868        for (body, want) in [
6869            // The C counts the arguments before it reads any of them, so the
6870            // one it names when there are none is the one before the first.
6871            (
6872                "return cmsgpack.pack()",
6873                "bad argument #0 to 'pack' (MessagePack pack needs input.)",
6874            ),
6875            (
6876                "return cmsgpack.unpack()",
6877                "bad argument #1 to 'unpack' (string expected, got no value)",
6878            ),
6879            (
6880                "return cmsgpack.unpack(string.char(193))",
6881                "Bad data format in input.",
6882            ),
6883            (
6884                "return cmsgpack.unpack(string.char(204))",
6885                "Missing bytes in input.",
6886            ),
6887            (
6888                "return cmsgpack.unpack(string.char(146, 1))",
6889                "Missing bytes in input.",
6890            ),
6891            (
6892                "return cmsgpack.unpack_one('\\1', 5)",
6893                "Start offset 5 greater than input length 1.",
6894            ),
6895            (
6896                "return cmsgpack.unpack_limit('\\1\\2', 1, 5)",
6897                "Start offset 5 greater than input length 2.",
6898            ),
6899            // The second number here is the length of the input rather than
6900            // the limit, which is a mixed up argument in the C kept on purpose.
6901            (
6902                "return cmsgpack.unpack_one('\\1', -1)",
6903                "Invalid request to unpack with offset of -1 and limit of 1.",
6904            ),
6905            (
6906                "return cmsgpack.unpack_limit('\\1', -1, 0)",
6907                "Invalid request to unpack with offset of 0 and limit of 1.",
6908            ),
6909        ] {
6910            let reply = f.run(&[b"EVAL", body.as_bytes(), b"0"]);
6911            assert!(
6912                reply.starts_with(&format!("-ERR user_script:1: {want} script: ")),
6913                "{body} gave {reply}",
6914            );
6915        }
6916        // Four calls and the four names the C sets on the table beside them.
6917        let names = "_COPYRIGHT _DESCRIPTION _NAME _VERSION pack unpack unpack_limit unpack_one";
6918        assert_eq!(
6919            f.run(&[
6920                b"EVAL",
6921                b"local t = {} for k in pairs(cmsgpack) do t[#t+1] = k end \
6922                  table.sort(t) return table.concat(t, ' ')",
6923                b"0",
6924            ]),
6925            format!("${}\r\n{names}\r\n", names.len())
6926        );
6927        for body in [&b"cmsgpack.pack = 1"[..], b"rawset(cmsgpack, 'zz', 1)"] {
6928            assert!(
6929                f.run(&[b"EVAL", body, b"0"])
6930                    .contains("Attempt to modify a readonly table script: "),
6931                "{body:?}",
6932            );
6933        }
6934        // A library is a table like any other from a script's side, so packing
6935        // one walks its members rather than finding the guard in front empty.
6936        assert_eq!(
6937            f.run(&[
6938                b"EVAL",
6939                b"return cmsgpack.unpack(cmsgpack.pack(cmsgpack))._NAME",
6940                b"0",
6941            ]),
6942            "$8\r\ncmsgpack\r\n"
6943        );
6944    }
6945
6946    /// The library used by most of the function tests below.
6947    ///
6948    /// Written out once because every one of them wants a library that has
6949    /// something to call, and because the line numbers in the failures a couple
6950    /// of them check are line numbers in this.
6951    const LIB: &[u8] = b"#!lua name=mylib\n\
6952        local counter = 0\n\
6953        redis.register_function{function_name = 'ping', description = 'says pong',\n\
6954        callback = function(keys, args) return 'pong' end, flags = {'no-writes'}}\n\
6955        redis.register_function('count', function() counter = counter + 1 return counter end)\n\
6956        redis.register_function('echo', function(keys, args) return {keys, args} end)\n\
6957        redis.register_function('setit', function(keys, args) \
6958        return redis.call('SET', keys[1], args[1]) end)\n\
6959        redis.register_function('raise', function() error('boom') end)\n";
6960
6961    /// A second library, for the tests that need two of them.
6962    const OTHER: &[u8] = b"#!lua name=other\n\
6963        redis.register_function('twice', function(keys, args) return 2 end)\n";
6964
6965    #[test]
6966    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6967    fn a_library_is_loaded_once_and_called_by_name_forever_after() {
6968        let mut f = Fixture::new();
6969        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
6970        assert_eq!(f.run(&[b"FCALL", b"ping", b"0"]), "$4\r\npong\r\n");
6971        // The dictionary FCALL looks in is one for the whole server and it does
6972        // not care about case, which is why this finds the same function.
6973        assert_eq!(f.run(&[b"FCALL", b"PiNg", b"0"]), "$4\r\npong\r\n");
6974        // Keys and arguments arrive as the two arguments of the callback rather
6975        // than as globals, and a function that reads KEYS is reading a name
6976        // that is not there.
6977        assert_eq!(
6978            f.run(&[b"FCALL", b"echo", b"1", b"k", b"a", b"b"]),
6979            "*2\r\n*1\r\n$1\r\nk\r\n*2\r\n$1\r\na\r\n$1\r\nb\r\n"
6980        );
6981        assert_eq!(f.run(&[b"FCALL", b"setit", b"1", b"s", b"v"]), "+OK\r\n");
6982        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
6983        // A library's own local outlives the call that made it, which is the
6984        // whole reason a library is not a script.
6985        assert_eq!(f.run(&[b"FCALL", b"count", b"0"]), ":1\r\n");
6986        assert_eq!(f.run(&[b"FCALL", b"count", b"0"]), ":2\r\n");
6987        // The name a failure ends with is the function's, where a script's is
6988        // its digest, and the line is a line in the library.
6989        assert_eq!(
6990            f.run(&[b"FCALL", b"raise", b"0"]),
6991            "-ERR user_function:8: boom script: raise, on @user_function:8.\r\n"
6992        );
6993        // Deleting is by the exact name, so the upper case spelling that found
6994        // the function a moment ago does not find the library.
6995        assert_eq!(
6996            f.run(&[b"FUNCTION", b"DELETE", b"MYLIB"]),
6997            "-ERR Library not found\r\n"
6998        );
6999        assert_eq!(f.run(&[b"FUNCTION", b"DELETE", b"mylib"]), "+OK\r\n");
7000        assert_eq!(
7001            f.run(&[b"FCALL", b"ping", b"0"]),
7002            "-ERR Function not found\r\n"
7003        );
7004    }
7005
7006    #[test]
7007    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7008    fn a_library_that_is_wrong_says_which_way_it_is_wrong() {
7009        let mut f = Fixture::new();
7010        for (code, want) in [
7011            (&b"return 1"[..], "ERR Missing library metadata"),
7012            (b"#!lua name=x", "ERR Invalid library metadata"),
7013            (b"#!\n", "ERR Library name was not given"),
7014            (b"#!lua\nx", "ERR Library name was not given"),
7015            (
7016                b"#!lua name=a name=b\nx",
7017                "ERR Invalid metadata value, name argument was given multiple times",
7018            ),
7019            (
7020                b"#!lua nome=a\nx",
7021                "ERR Invalid metadata value given: nome=a",
7022            ),
7023            (b"#!lua name=\"q\nx", "ERR Invalid library metadata"),
7024            (
7025                b"#!lua name=a-b\nx",
7026                "ERR Library names can only contain letters, numbers, or underscores(_) \
7027                 and must be at least one character long",
7028            ),
7029            (b"#!zz name=x\nx", "ERR Engine 'zz' not found"),
7030            (
7031                b"#!lua name=c\nthis is not lua",
7032                "ERR Error compiling function: user_function:2: '=' expected near 'is'",
7033            ),
7034            // Nothing at all is on the global table during a load except one
7035            // table with eight names on it, so `error` is as absent as anything
7036            // a library misspelled would be.
7037            (
7038                b"#!lua name=r\nerror('boom')",
7039                "ERR Error registering functions: ERR user_function:2: \
7040                 Script attempted to access nonexistent global variable 'error'",
7041            ),
7042            // And `redis` is there but `redis.call` is not, so the name the
7043            // complaint gives is `call` and not `redis`.
7044            (
7045                b"#!lua name=r\nredis.call('PING')",
7046                "ERR Error registering functions: ERR user_function:2: \
7047                 Script attempted to access nonexistent global variable 'call'",
7048            ),
7049            (
7050                b"#!lua name=r\nx = 1",
7051                "ERR Error registering functions: ERR user_function:2: \
7052                 Attempt to modify a readonly table",
7053            ),
7054            (b"#!lua name=n\nlocal x = 1", "ERR No functions registered"),
7055        ] {
7056            assert_eq!(
7057                f.run(&[b"FUNCTION", b"LOAD", code]),
7058                format!("-{want}\r\n"),
7059                "{}",
7060                String::from_utf8_lossy(code),
7061            );
7062        }
7063    }
7064
7065    #[test]
7066    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7067    fn register_function_turns_away_every_call_it_cannot_make_sense_of() {
7068        let mut f = Fixture::new();
7069        for (call, want) in [
7070            (
7071                &b"redis.register_function()"[..],
7072                "wrong number of arguments to redis.register_function",
7073            ),
7074            (
7075                b"redis.register_function('a', function() end, 1)",
7076                "wrong number of arguments to redis.register_function",
7077            ),
7078            (
7079                b"redis.register_function('a')",
7080                "calling redis.register_function with a single argument is only \
7081                 applicable to Lua table (representing named arguments).",
7082            ),
7083            (
7084                b"redis.register_function({foo = 'a'})",
7085                "unknown argument given to redis.register_function",
7086            ),
7087            (
7088                b"redis.register_function({callback = function() end})",
7089                "redis.register_function must get a function name argument",
7090            ),
7091            (
7092                b"redis.register_function({function_name = 'a'})",
7093                "redis.register_function must get a callback argument",
7094            ),
7095            (
7096                b"redis.register_function({function_name = {}, callback = function() end})",
7097                "function_name argument given to redis.register_function must be a string",
7098            ),
7099            (
7100                b"redis.register_function({function_name = 'a', description = {}, \
7101                  callback = function() end})",
7102                "description argument given to redis.register_function must be a string",
7103            ),
7104            (
7105                b"redis.register_function({function_name = 'a', callback = 1})",
7106                "callback argument given to redis.register_function must be a function",
7107            ),
7108            (
7109                b"redis.register_function({function_name = 'a', callback = function() end, \
7110                  flags = 1})",
7111                "flags argument to redis.register_function must be a table \
7112                 representing function flags",
7113            ),
7114            (
7115                b"redis.register_function({function_name = 'a', callback = function() end, \
7116                  flags = {'zz'}})",
7117                "unknown flag given",
7118            ),
7119            (
7120                b"redis.register_function({}, function() end)",
7121                "first argument to redis.register_function must be a string",
7122            ),
7123            (
7124                b"redis.register_function('a', 1)",
7125                "second argument to redis.register_function must be a function",
7126            ),
7127            (
7128                b"redis.register_function('a-b', function() end)",
7129                "Library names can only contain letters, numbers, or underscores(_) \
7130                 and must be at least one character long",
7131            ),
7132            (
7133                b"redis.register_function('d', function() end) \
7134                  redis.register_function('d', function() end)",
7135                "Function already exists in the library",
7136            ),
7137        ] {
7138            let mut code = b"#!lua name=e\n".to_vec();
7139            code.extend_from_slice(call);
7140            // Two `ERR` in a row on purpose. The sentence comes back as a table
7141            // with the code already on it, which is what keeps the position off
7142            // the front of it, and then the code goes on the line as well.
7143            assert_eq!(
7144                f.run(&[b"FUNCTION", b"LOAD", &code]),
7145                format!("-ERR Error registering functions: ERR {want}\r\n"),
7146                "{}",
7147                String::from_utf8_lossy(call),
7148            );
7149        }
7150        // A number is a name, because the C reads an argument that should be a
7151        // string through a helper that takes a number and prints it.
7152        assert_eq!(
7153            f.run(&[
7154                b"FUNCTION",
7155                b"LOAD",
7156                b"#!lua name=n\nredis.register_function(12, function() return 1 end)",
7157            ]),
7158            "$1\r\nn\r\n"
7159        );
7160        assert_eq!(f.run(&[b"FCALL", b"12", b"0"]), ":1\r\n");
7161        // The dictionary inside one library is case sensitive where the one
7162        // across libraries is not, so these are two functions.
7163        assert_eq!(
7164            f.run(&[
7165                b"FUNCTION",
7166                b"LOAD",
7167                b"#!lua name=c\nredis.register_function('d', function() return 1 end) \
7168                  redis.register_function('D', function() return 2 end)",
7169            ]),
7170            "$1\r\nc\r\n"
7171        );
7172    }
7173
7174    #[test]
7175    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7176    fn a_library_cannot_take_a_name_another_library_already_has() {
7177        let mut f = Fixture::new();
7178        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7179        assert_eq!(
7180            f.run(&[b"FUNCTION", b"LOAD", LIB]),
7181            "-ERR Library 'mylib' already exists\r\n"
7182        );
7183        // A different library that registers a name the first one already has,
7184        // which is checked without regard to case because the dictionary it is
7185        // checked against is.
7186        assert_eq!(
7187            f.run(&[
7188                b"FUNCTION",
7189                b"LOAD",
7190                b"#!lua name=other\nredis.register_function('PING', function() return 1 end)",
7191            ]),
7192            "-ERR Function PING already exists\r\n"
7193        );
7194        // REPLACE reloads a library over itself, and the collision check leaves
7195        // the library being replaced out or nothing could ever be reloaded.
7196        assert_eq!(
7197            f.run(&[b"FUNCTION", b"LOAD", b"REPLACE", LIB]),
7198            "$5\r\nmylib\r\n"
7199        );
7200        // The counter went back to zero with the reload, since the library is a
7201        // new one and its locals are new with it.
7202        assert_eq!(f.run(&[b"FCALL", b"count", b"0"]), ":1\r\n");
7203        assert_eq!(
7204            f.run(&[b"FUNCTION", b"LOAD", b"NOPE", LIB]),
7205            "-ERR Unknown option given: NOPE\r\n"
7206        );
7207        // The loop that reads the options stops one short of the end, so the
7208        // last argument is the code whatever it looks like.
7209        assert_eq!(
7210            f.run(&[b"FUNCTION", b"LOAD", b"REPLACE"]),
7211            "-ERR Missing library metadata\r\n"
7212        );
7213        assert_eq!(
7214            f.run(&[b"FUNCTION", b"LOAD"]),
7215            "-ERR wrong number of arguments for 'function|load' command\r\n"
7216        );
7217    }
7218
7219    #[test]
7220    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7221    fn fcall_checks_the_name_before_it_looks_at_anything_else() {
7222        let mut f = Fixture::new();
7223        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7224        for (args, want) in [
7225            (&[&b"nosuch"[..], b"x"][..], "ERR Function not found"),
7226            (&[b"ping", b"x"], "ERR Bad number of keys provided"),
7227            (&[b"ping", b"1.5"], "ERR Bad number of keys provided"),
7228            (&[b"ping", b"+1"], "ERR Bad number of keys provided"),
7229            (
7230                &[b"ping", b"99999999999999999999"],
7231                "ERR Bad number of keys provided",
7232            ),
7233            (
7234                &[b"ping", b"3", b"a"],
7235                "ERR Number of keys can't be greater than number of args",
7236            ),
7237            (&[b"ping", b"-1"], "ERR Number of keys can't be negative"),
7238        ] {
7239            let mut wire: Vec<&[u8]> = vec![b"FCALL"];
7240            wire.extend_from_slice(args);
7241            assert_eq!(f.run(&wire), format!("-{want}\r\n"), "{args:?}");
7242        }
7243        // The read-only spelling refuses a function the library did not mark
7244        // no-writes, and it refuses it before anything runs.
7245        assert_eq!(
7246            f.run(&[b"FCALL_RO", b"setit", b"1", b"s", b"v"]),
7247            "-ERR Can not execute a script with write flag using *_ro command.\r\n"
7248        );
7249        assert_eq!(f.run(&[b"FCALL_RO", b"ping", b"0"]), "$4\r\npong\r\n");
7250        assert_eq!(
7251            f.run(&[b"FCALL_RO", b"nosuch", b"0"]),
7252            "-ERR Function not found\r\n"
7253        );
7254        // And a function that was marked no-writes is held to it whichever
7255        // spelling called it.
7256        assert_eq!(
7257            f.run(&[
7258                b"FUNCTION",
7259                b"LOAD",
7260                b"#!lua name=w\nredis.register_function{function_name = 'w', \
7261                  flags = {'no-writes'}, callback = function(keys) \
7262                  return redis.call('SET', keys[1], 'x') end}",
7263            ]),
7264            "$1\r\nw\r\n"
7265        );
7266        assert!(
7267            f.run(&[b"FCALL", b"w", b"1", b"k"])
7268                .starts_with("-ERR Write commands are not allowed from read-only scripts."),
7269        );
7270    }
7271
7272    #[test]
7273    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7274    fn a_function_gets_the_globals_a_script_gets_minus_the_ones_only_eval_has() {
7275        let mut f = Fixture::new();
7276        // The three names on the `redis` table that only mean something inside
7277        // EVAL are not there, and neither is the error handler EVAL installs.
7278        let names = "LOG_DEBUG LOG_NOTICE LOG_VERBOSE LOG_WARNING REDIS_VERSION \
7279                     REDIS_VERSION_NUM REPL_ALL REPL_AOF REPL_NONE REPL_REPLICA REPL_SLAVE \
7280                     acl_check_cmd call error_reply log pcall set_repl setresp sha1hex \
7281                     status_reply";
7282        let globals = "_G _VERSION assert bit cjson cmsgpack collectgarbage coroutine error \
7283                       gcinfo getmetatable ipairs load loadstring math next os pairs pcall \
7284                       rawequal rawget rawset redis select setmetatable string struct table \
7285                       tonumber tostring type unpack xpcall";
7286        assert_eq!(
7287            f.run(&[
7288                b"FUNCTION",
7289                b"LOAD",
7290                b"#!lua name=g\n\
7291                  local function sorted(t) local o = {} for k in pairs(t) do o[#o+1] = k end \
7292                  table.sort(o) return table.concat(o, ' ') end\n\
7293                  redis.register_function('names', function() return sorted(redis) end)\n\
7294                  redis.register_function('globals', function() return sorted(_G) end)\n\
7295                  redis.register_function('keysg', function() return KEYS[1] end)\n\
7296                  redis.register_function('zzz', function() return tostring(redis.zzz) end)\n\
7297                  redis.register_function('wr', function() rawset(_G, 'x', 1) end)\n\
7298                  redis.register_function('gwr', function() _G.pcall = 1 end)\n",
7299            ]),
7300            "$1\r\ng\r\n"
7301        );
7302        assert_eq!(
7303            f.run(&[b"FCALL", b"names", b"0"]),
7304            format!("${}\r\n{names}\r\n", names.len())
7305        );
7306        assert_eq!(
7307            f.run(&[b"FCALL", b"globals", b"0"]),
7308            format!("${}\r\n{globals}\r\n", globals.len())
7309        );
7310        // No `KEYS`, and reading a global that is not there is a mistake rather
7311        // than a nil, so this is the sandbox's own complaint.
7312        assert!(
7313            f.run(&[b"FCALL", b"keysg", b"1", b"k"])
7314                .contains("nonexistent global variable 'KEYS'"),
7315        );
7316        // The `redis` table has no error metatable on it, unlike the global
7317        // table, so a name that is not on it is a nil and not a complaint.
7318        assert_eq!(f.run(&[b"FCALL", b"zzz", b"0"]), "$3\r\nnil\r\n");
7319        // The global table cannot be written to either way round, which is a
7320        // stricter rule than the one a script runs under.
7321        for name in [&b"wr"[..], b"gwr"] {
7322            assert!(
7323                f.run(&[b"FCALL", name, b"0"])
7324                    .contains("Attempt to modify a readonly table"),
7325                "{}",
7326                String::from_utf8_lossy(name),
7327            );
7328        }
7329    }
7330
7331    #[test]
7332    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7333    fn function_list_says_what_every_library_registered() {
7334        let mut f = Fixture::new();
7335        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7336        // One map per library on RESP3, and the functions inside it in the
7337        // order the library registered them, which is D-109.
7338        f.out = Out::new(Proto::Resp3);
7339        let listed = f.run(&[b"FUNCTION", b"LIST"]);
7340        assert!(listed.starts_with("*1\r\n%3\r\n$12\r\nlibrary_name\r\n$5\r\nmylib\r\n"));
7341        assert!(listed.contains("$6\r\nengine\r\n$3\r\nLUA\r\n"));
7342        assert!(listed.contains(
7343            "%3\r\n$4\r\nname\r\n$4\r\nping\r\n\
7344             $11\r\ndescription\r\n$9\r\nsays pong\r\n$5\r\nflags\r\n~1\r\n+no-writes\r\n"
7345        ));
7346        // A function with no description gets a null rather than an empty
7347        // string, and no flags is an empty set rather than a missing field.
7348        assert!(listed.contains(
7349            "$4\r\nname\r\n$5\r\ncount\r\n$11\r\ndescription\r\n_\r\n$5\r\nflags\r\n~0\r\n"
7350        ));
7351        assert!(!listed.contains("library_code"));
7352        assert!(
7353            f.run(&[b"FUNCTION", b"LIST", b"WITHCODE"])
7354                .contains("library_code")
7355        );
7356        // The pattern is matched without regard to case, which is a third rule
7357        // again next to the two the two dictionaries use.
7358        assert!(
7359            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"MY*"])
7360                .starts_with("*1\r\n")
7361        );
7362        assert_eq!(
7363            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"zz*"]),
7364            "*0\r\n"
7365        );
7366        // On RESP2 the same reply is a flat array of six, which is what `map`
7367        // means on a protocol that has no map.
7368        f.out = Out::new(Proto::Resp2);
7369        assert!(f.run(&[b"FUNCTION", b"LIST"]).starts_with("*1\r\n*6\r\n"));
7370        for (args, want) in [
7371            (&[&b"ZZ"[..]][..], "ERR Unknown argument ZZ"),
7372            (&[b"WITHCODE", b"WITHCODE"], "ERR Unknown argument WITHCODE"),
7373            (
7374                &[b"LIBRARYNAME", b"a", b"LIBRARYNAME", b"b"],
7375                "ERR Unknown argument LIBRARYNAME",
7376            ),
7377            (&[b"LIBRARYNAME"], "ERR library name argument was not given"),
7378        ] {
7379            let mut wire: Vec<&[u8]> = vec![b"FUNCTION", b"LIST"];
7380            wire.extend_from_slice(args);
7381            assert_eq!(f.run(&wire), format!("-{want}\r\n"), "{args:?}");
7382        }
7383    }
7384
7385    #[test]
7386    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7387    fn function_stats_counts_what_is_loaded_and_says_nothing_is_running() {
7388        let mut f = Fixture::new();
7389        f.out = Out::new(Proto::Resp3);
7390        assert_eq!(
7391            f.run(&[b"FUNCTION", b"STATS"]),
7392            "%2\r\n$14\r\nrunning_script\r\n_\r\n$7\r\nengines\r\n%1\r\n$3\r\nLUA\r\n\
7393             %2\r\n$15\r\nlibraries_count\r\n:0\r\n$15\r\nfunctions_count\r\n:0\r\n"
7394        );
7395        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7396        assert!(
7397            f.run(&[b"FUNCTION", b"STATS"])
7398                .ends_with("libraries_count\r\n:1\r\n$15\r\nfunctions_count\r\n:5\r\n"),
7399        );
7400        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH"]), "+OK\r\n");
7401        assert!(
7402            f.run(&[b"FUNCTION", b"STATS"])
7403                .ends_with(":0\r\n$15\r\nfunctions_count\r\n:0\r\n")
7404        );
7405    }
7406
7407    #[test]
7408    fn every_function_subcommand_complains_about_its_own_arity() {
7409        let mut f = Fixture::new();
7410        for (args, want) in [
7411            (
7412                &[&b"STATS"[..], b"X"][..],
7413                "ERR wrong number of arguments for 'function|stats' command",
7414            ),
7415            (
7416                &[b"KILL", b"X"],
7417                "ERR wrong number of arguments for 'function|kill' command",
7418            ),
7419            (
7420                &[b"HELP", b"X"],
7421                "ERR wrong number of arguments for 'function|help' command",
7422            ),
7423            (
7424                &[b"DELETE"],
7425                "ERR wrong number of arguments for 'function|delete' command",
7426            ),
7427            (
7428                &[b"DELETE", b"a", b"b"],
7429                "ERR wrong number of arguments for 'function|delete' command",
7430            ),
7431            (
7432                &[b"DUMP", b"X"],
7433                "ERR wrong number of arguments for 'function|dump' command",
7434            ),
7435            (
7436                &[b"RESTORE"],
7437                "ERR wrong number of arguments for 'function|restore' command",
7438            ),
7439            // RESTORE is the other one that falls through to the generic
7440            // sentence, and for the same reason FLUSH does.
7441            (
7442                &[b"RESTORE", b"a", b"FLUSH", b"X"],
7443                "ERR unknown subcommand or wrong number of arguments for 'RESTORE'. \
7444                 Try FUNCTION HELP.",
7445            ),
7446            (
7447                &[b"RESTORE", b"a", b"ZZ"],
7448                "ERR Wrong restore policy given, value should be either FLUSH, APPEND \
7449                 or REPLACE.",
7450            ),
7451            // FLUSH is the one that does not, because it checks the count
7452            // itself before it looks at the argument.
7453            (
7454                &[b"FLUSH", b"SYNC", b"X"],
7455                "ERR unknown subcommand or wrong number of arguments for 'FLUSH'. \
7456                 Try FUNCTION HELP.",
7457            ),
7458            (
7459                &[b"FLUSH", b"ZZ"],
7460                "ERR FUNCTION FLUSH only supports SYNC|ASYNC option",
7461            ),
7462            (&[b"ZZ"], "ERR unknown subcommand 'ZZ'. Try FUNCTION HELP."),
7463        ] {
7464            let mut wire: Vec<&[u8]> = vec![b"FUNCTION"];
7465            wire.extend_from_slice(args);
7466            assert_eq!(f.run(&wire), format!("-{want}\r\n"), "{args:?}");
7467        }
7468        assert_eq!(
7469            f.run(&[b"FUNCTION"]),
7470            "-ERR wrong number of arguments for 'function' command\r\n"
7471        );
7472        assert_eq!(
7473            f.run(&[b"FUNCTION", b"KILL"]),
7474            "-NOTBUSY No scripts in execution right now.\r\n"
7475        );
7476    }
7477
7478    /// The two ends of the same pipe, so they are tested as one.
7479    ///
7480    /// An empty server dumps ten bytes rather than nothing, because the footer
7481    /// is there whether or not a library is in front of it, and restoring those
7482    /// ten bytes is a working no op.
7483    #[test]
7484    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7485    fn a_library_survives_a_dump_and_a_restore() {
7486        let mut f = Fixture::new();
7487        let empty = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7488        assert_eq!(empty.len(), 10);
7489        assert_eq!(f.run(&[b"FUNCTION", b"RESTORE", &empty]), "+OK\r\n");
7490
7491        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7492        let full = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7493        assert!(full.len() > empty.len());
7494
7495        // The default policy is APPEND, so restoring onto the library the
7496        // payload came from is a name collision and not a quiet replacement.
7497        assert_eq!(
7498            f.run(&[b"FUNCTION", b"RESTORE", &full]),
7499            "-ERR Library mylib already exists\r\n"
7500        );
7501        assert_eq!(
7502            f.run(&[b"FUNCTION", b"RESTORE", &full, b"REPLACE"]),
7503            "+OK\r\n"
7504        );
7505        assert_eq!(
7506            f.run(&[b"FUNCTION", b"RESTORE", &full, b"FLUSH"]),
7507            "+OK\r\n"
7508        );
7509        // Whichever way it went back, the functions in it still run.
7510        assert_eq!(f.run(&[b"FCALL", b"ping", b"0"]), "$4\r\npong\r\n");
7511
7512        // FLUSH keeps only what the payload held, so a library that was there
7513        // and is not in the payload is gone.
7514        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", OTHER]), "$5\r\nother\r\n");
7515        assert_eq!(
7516            f.run(&[b"FUNCTION", b"RESTORE", &full, b"FLUSH"]),
7517            "+OK\r\n"
7518        );
7519        assert_eq!(
7520            f.run(&[b"FUNCTION", b"DELETE", b"other"]),
7521            "-ERR Library not found\r\n"
7522        );
7523    }
7524
7525    /// A payload that is going to be refused has to leave the server alone.
7526    ///
7527    /// Every one of these is refused for a different reason and at a different
7528    /// depth, from bytes that are not a payload at all down to a library that
7529    /// compiles and then collides, and the library that was already there has to
7530    /// still be there afterwards in every case.
7531    #[test]
7532    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7533    fn a_restore_that_fails_changes_nothing() {
7534        let mut f = Fixture::new();
7535        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7536        let good = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7537
7538        // Put the footer back on, so that each of these is refused for the
7539        // reason it is meant to be testing rather than for a checksum the edit
7540        // broke on the way.
7541        let reseal = |body: &[u8], version: u16| {
7542            let mut out = body.to_vec();
7543            out.extend_from_slice(&version.to_le_bytes());
7544            let crc = yo_common::crc::crc64(0, &out);
7545            out.extend_from_slice(&crc.to_le_bytes());
7546            out
7547        };
7548        let body = &good[..good.len() - 10];
7549
7550        let mut torn = good.clone();
7551        let n = torn.len();
7552        torn[n - 1] ^= 0xff;
7553        let future = reseal(body, 999);
7554        // The opcode in front of the one library, changed to the one the 7.0
7555        // release candidates wrote and then to one that is not a library at all.
7556        let mut pre_ga = body.to_vec();
7557        pre_ga[0] = 246;
7558        let pre_ga = reseal(&pre_ga, yo_kv::rdb::VERSION);
7559        let mut other = body.to_vec();
7560        other[0] = 0;
7561        let other = reseal(&other, yo_kv::rdb::VERSION);
7562        // A library whose length says there is more of it than there is.
7563        let mut cut = body.to_vec();
7564        cut.truncate(body.len() - 1);
7565        let cut = reseal(&cut, yo_kv::rdb::VERSION);
7566
7567        for (bytes, want) in [
7568            (vec![], "ERR DUMP payload version or checksum are wrong"),
7569            (
7570                b"0123456789".to_vec(),
7571                "ERR DUMP payload version or checksum are wrong",
7572            ),
7573            (torn, "ERR DUMP payload version or checksum are wrong"),
7574            (future, "ERR DUMP payload version or checksum are wrong"),
7575            (pre_ga, "ERR Pre-GA function format not supported"),
7576            (other, "ERR given type is not a function"),
7577            (cut, "ERR Failed loading library payload"),
7578        ] {
7579            assert_eq!(
7580                f.run(&[b"FUNCTION", b"RESTORE", &bytes]),
7581                format!("-{want}\r\n")
7582            );
7583        }
7584
7585        // Still exactly the one library, and it still runs.
7586        assert_eq!(f.run(&[b"FCALL", b"ping", b"0"]), "$4\r\npong\r\n");
7587        let again = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7588        assert_eq!(again, good);
7589    }
7590
7591    /// A REPLACE takes a library's name off another library and still refuses to
7592    /// take a function name off one it is leaving alone.
7593    #[test]
7594    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7595    fn a_restore_will_not_take_a_function_name_off_a_library_it_keeps() {
7596        let mut f = Fixture::new();
7597        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7598        let full = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7599        // A second library registering the name the payload's library uses.
7600        let clash =
7601            b"#!lua name=cl\nredis.register_function('ping', function() return 'other' end)"
7602                .as_slice();
7603        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH"]), "+OK\r\n");
7604        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", clash]), "$2\r\ncl\r\n");
7605        assert_eq!(
7606            f.run(&[b"FUNCTION", b"RESTORE", &full, b"REPLACE"]),
7607            "-ERR Function ping already exists\r\n"
7608        );
7609        // Untouched, so the name still belongs to the library that had it.
7610        assert_eq!(f.run(&[b"FCALL", b"ping", b"0"]), "$5\r\nother\r\n");
7611    }
7612
7613    #[test]
7614    fn command_getkeys_reads_the_key_count_out_of_a_script_call() {
7615        let mut f = Fixture::new();
7616        assert_eq!(
7617            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1", b"1", b"k"]),
7618            "*1\r\n$1\r\nk\r\n"
7619        );
7620        assert_eq!(
7621            f.run(&[
7622                b"COMMAND", b"GETKEYS", b"EVALSHA", b"abc", b"2", b"k1", b"k2"
7623            ]),
7624            "*2\r\n$2\r\nk1\r\n$2\r\nk2\r\n"
7625        );
7626        // None is a real answer for a script and the arguments past the count
7627        // are not keys, so they are not listed.
7628        assert_eq!(
7629            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL_RO", b"return 1", b"0", b"a"]),
7630            "*0\r\n"
7631        );
7632        // A count that makes no sense finds no keys rather than being an error,
7633        // which is what a real server's key spec does with it.
7634        assert_eq!(
7635            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1", b"3", b"k"]),
7636            "*0\r\n"
7637        );
7638        assert_eq!(
7639            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1", b"-1"]),
7640            "*0\r\n"
7641        );
7642        assert_eq!(
7643            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1", b"abc"]),
7644            "*0\r\n"
7645        );
7646        // The count itself has to be there, and that is an arity question.
7647        assert_eq!(
7648            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1"]),
7649            "-ERR Invalid number of arguments specified for command\r\n"
7650        );
7651    }
7652
7653    #[test]
7654    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7655    fn the_helpers_on_the_redis_table_answer_the_way_they_are_documented() {
7656        let mut f = Fixture::new();
7657        let eval = |f: &mut Fixture, body: &[u8]| f.run(&[b"EVAL", body, b"0"]);
7658
7659        assert_eq!(
7660            eval(&mut f, b"return redis.sha1hex('')"),
7661            "$40\r\nda39a3ee5e6b4b0d3255bfef95601890afd80709\r\n"
7662        );
7663        assert_eq!(
7664            eval(&mut f, b"return redis.sha1hex('return 1')"),
7665            "$40\r\ne0e1f9fabfc9d4800c877a703b823ac0578ff8db\r\n"
7666        );
7667        // A message with no space in it gets the generic code in front, and one
7668        // that already looks like a coded error is left alone.
7669        assert_eq!(
7670            eval(&mut f, b"return redis.error_reply('boom')"),
7671            "-ERR boom\r\n"
7672        );
7673        assert_eq!(
7674            eval(&mut f, b"return redis.error_reply('WRONGTYPE nope')"),
7675            "-WRONGTYPE nope\r\n"
7676        );
7677        assert_eq!(
7678            eval(&mut f, b"return redis.status_reply('fine')"),
7679            "+fine\r\n"
7680        );
7681        // Neither of them raises when it is called wrongly, they answer a value
7682        // that is an error, which is a difference a script can see.
7683        assert_eq!(
7684            eval(&mut f, b"return redis.error_reply(1)"),
7685            "-ERR wrong number or type of arguments\r\n"
7686        );
7687        assert_eq!(
7688            eval(&mut f, b"local x = redis.status_reply() return x.err"),
7689            "$37\r\nERR wrong number or type of arguments\r\n"
7690        );
7691
7692        // The constants a script branches on.
7693        assert_eq!(
7694            eval(
7695                &mut f,
7696                b"return redis.LOG_DEBUG .. redis.LOG_VERBOSE .. redis.LOG_NOTICE .. redis.LOG_WARNING"
7697            ),
7698            "$4\r\n0123\r\n"
7699        );
7700        assert_eq!(
7701            eval(
7702                &mut f,
7703                b"return redis.REPL_NONE .. redis.REPL_AOF .. redis.REPL_SLAVE .. redis.REPL_REPLICA .. redis.REPL_ALL"
7704            ),
7705            "$5\r\n01223\r\n"
7706        );
7707        // The calls that exist so an old script keeps working.
7708        assert_eq!(eval(&mut f, b"return redis.replicate_commands()"), ":1\r\n");
7709        assert_eq!(
7710            eval(&mut f, b"redis.set_repl(redis.REPL_ALL) return 1"),
7711            ":1\r\n"
7712        );
7713        assert_eq!(
7714            eval(&mut f, b"redis.log(redis.LOG_WARNING, 'x') return 1"),
7715            ":1\r\n"
7716        );
7717        assert_eq!(
7718            eval(&mut f, b"return redis.acl_check_cmd('get', 'k')"),
7719            ":1\r\n"
7720        );
7721        // Each of those checks its arguments the way a real server does.
7722        assert!(eval(&mut f, b"redis.setresp(4)").contains("RESP version must be 2 or 3."),);
7723        assert!(eval(&mut f, b"redis.set_repl(9)").contains("Invalid replication flags."));
7724        assert!(
7725            eval(&mut f, b"redis.log('x', 'y')")
7726                .contains("First argument must be a number (log level)."),
7727        );
7728        assert!(
7729            eval(&mut f, b"return redis.acl_check_cmd('nosuchcmd')")
7730                .contains("Invalid command passed to redis.acl_check_cmd()"),
7731        );
7732        assert!(
7733            eval(&mut f, b"return redis.acl_check_cmd('get')")
7734                .contains("Wrong number of args for redis.acl_check_cmd()"),
7735        );
7736    }
7737
7738    #[test]
7739    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
7740        let mut f = Fixture::new();
7741        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
7742        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
7743        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
7744        // Read back as a string it is still an integer, written out as digits
7745        // only because somebody asked for them.
7746        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
7747        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
7748        // A counter that is not a number is the error the store raises and this
7749        // layer only spells, which is the whole point of the split.
7750        f.run(&[b"SET", b"k", b"hello"]);
7751        assert_eq!(
7752            f.run(&[b"INCR", b"k"]),
7753            "-ERR value is not an integer or out of range\r\n"
7754        );
7755        assert_eq!(
7756            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
7757            "-ERR increment would produce NaN or Infinity\r\n"
7758        );
7759    }
7760
7761    /// Every one of these was read off a running 8.8. They are the answers a
7762    /// client library's own test suite checks, and the shapes are not
7763    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
7764    /// integer, `INCREX` is a pair.
7765    #[test]
7766    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
7767        let mut f = Fixture::new();
7768        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
7769        // The same digest a real 8.8 answers for the same five bytes, which is
7770        // what makes `IFDEQ` usable against a mixed deployment.
7771        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
7772        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
7773        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
7774        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
7775        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
7776        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
7777        assert_eq!(
7778            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
7779            "*2\r\n:1\r\n:0\r\n",
7780            "a refused increment reports the value it left alone and applied nothing"
7781        );
7782        assert_eq!(
7783            f.run(&[
7784                b"INCREX",
7785                b"n",
7786                b"BYINT",
7787                b"5",
7788                b"UBOUND",
7789                b"3",
7790                b"SATURATE"
7791            ]),
7792            "*2\r\n:3\r\n:2\r\n"
7793        );
7794        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
7795        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
7796    }
7797
7798    #[test]
7799    fn the_same_answers_come_out_in_resp3_spelling() {
7800        let mut f = Fixture::new();
7801        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
7802        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
7803        // A float counter is a double on RESP3 and the digits in a bulk string
7804        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
7805        assert_eq!(
7806            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
7807            "*2\r\n,1.5\r\n,1.5\r\n"
7808        );
7809        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
7810        // `RESET` puts the protocol back, which is the part that is easy to
7811        // miss and leaves a pooled connection speaking the wrong one.
7812        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
7813        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
7814    }
7815
7816    #[test]
7817    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
7818        let mut f = Fixture::new();
7819        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
7820        assert_eq!(flow, Flow::Continue);
7821        assert_eq!(
7822            reply,
7823            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
7824        );
7825        // A name with a line ending in it cannot write its own frame into the
7826        // stream, which is the reason the error writer maps them to spaces.
7827        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
7828        assert_eq!(reply.matches("\r\n").count(), 1);
7829    }
7830
7831    #[test]
7832    fn arity_is_checked_before_the_command_is() {
7833        let mut f = Fixture::new();
7834        assert_eq!(
7835            f.run(&[b"GET"]),
7836            "-ERR wrong number of arguments for 'get' command\r\n"
7837        );
7838        assert_eq!(
7839            f.run(&[b"MSET", b"k"]),
7840            "-ERR wrong number of arguments for 'mset' command\r\n"
7841        );
7842        // The table says `PING` takes one or more and a real server then
7843        // refuses three, which is the sort of thing that only shows up against
7844        // the real thing.
7845        assert_eq!(
7846            f.run(&[b"PING", b"a", b"b"]),
7847            "-ERR wrong number of arguments for 'ping' command\r\n"
7848        );
7849        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
7850        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
7851        // `DELEX` takes two or four and nothing between.
7852        assert_eq!(
7853            f.run(&[b"DELEX", b"k", b"IFEQ"]),
7854            "-ERR wrong number of arguments for 'delex' command\r\n"
7855        );
7856    }
7857
7858    /// The option rules, all of them measured against 8.8 rather than read off
7859    /// the documentation. The surprising one is that `SET` accepts the same
7860    /// keyword twice and `INCREX` does not.
7861    #[test]
7862    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
7863        let mut f = Fixture::new();
7864        let syntax = "-ERR syntax error\r\n";
7865        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
7866        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
7867        assert_eq!(
7868            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
7869            syntax
7870        );
7871        assert_eq!(
7872            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
7873            syntax
7874        );
7875        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
7876        // Twice is fine, and the last one wins.
7877        assert_eq!(
7878            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
7879            "+OK\r\n"
7880        );
7881        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
7882        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
7883        // `INCREX` refuses what `SET` allows.
7884        assert_eq!(
7885            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
7886            syntax
7887        );
7888        assert_eq!(
7889            f.run(&[b"INCREX", b"n", b"ENX"]),
7890            "-ERR ENX flag requires an expiration\r\n"
7891        );
7892        assert_eq!(
7893            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
7894            "-ERR UBOUND is not an integer or out of range\r\n"
7895        );
7896        assert_eq!(
7897            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
7898            "-ERR LBOUND can't be greater than UBOUND\r\n"
7899        );
7900        assert_eq!(
7901            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
7902            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
7903        );
7904    }
7905
7906    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
7907    /// key that is not there, which answers null without ever looking at the
7908    /// expiration it was given.
7909    #[test]
7910    fn the_expiry_rules_are_redis_own() {
7911        let mut f = Fixture::new();
7912        let bad = "-ERR invalid expire time in 'set' command\r\n";
7913        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
7914        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
7915        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
7916        assert_eq!(
7917            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
7918            bad
7919        );
7920        assert_eq!(
7921            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
7922            "-ERR value is not an integer or out of range\r\n"
7923        );
7924        assert_eq!(
7925            f.run(&[b"SETEX", b"k", b"0", b"v"]),
7926            "-ERR invalid expire time in 'setex' command\r\n"
7927        );
7928        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
7929        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
7930        assert_eq!(
7931            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
7932            "-ERR syntax error\r\n",
7933            "the option list is still checked before the key is looked up"
7934        );
7935        // A deadline in the past is accepted and the key goes with it.
7936        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
7937        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
7938        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
7939    }
7940
7941    #[test]
7942    fn mset_takes_its_pairs_from_the_read_buffer() {
7943        let mut f = Fixture::new();
7944        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
7945        assert_eq!(
7946            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
7947            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
7948        );
7949        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
7950        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
7951        assert_eq!(
7952            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
7953            "-ERR wrong number of key-value pairs\r\n"
7954        );
7955        assert_eq!(
7956            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
7957            "-ERR invalid numkeys value\r\n"
7958        );
7959        assert_eq!(
7960            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
7961            "-ERR invalid numkeys value\r\n"
7962        );
7963    }
7964
7965    #[test]
7966    fn lcs_answers_the_length_the_string_and_the_runs() {
7967        let mut f = Fixture::new();
7968        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
7969        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
7970        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
7971        assert_eq!(
7972            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
7973            "*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"
7974        );
7975        // Without `IDX` the two options that only mean something with it are
7976        // accepted and ignored, which is what a real server does.
7977        assert_eq!(
7978            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
7979            "$6\r\nmytext\r\n"
7980        );
7981    }
7982
7983    #[test]
7984    fn select_moves_the_connection_and_the_databases_stay_apart() {
7985        let mut f = Fixture::new();
7986        f.run(&[b"SET", b"k", b"zero"]);
7987        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
7988        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
7989        f.run(&[b"SET", b"k", b"four"]);
7990        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
7991        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
7992        assert_eq!(
7993            f.run(&[b"SELECT", b"99"]),
7994            "-ERR DB index is out of range\r\n"
7995        );
7996        assert_eq!(
7997            f.run(&[b"SELECT", b"-1"]),
7998            "-ERR DB index is out of range\r\n"
7999        );
8000        assert_eq!(
8001            f.run(&[b"SELECT", b"abc"]),
8002            "-ERR value is not an integer or out of range\r\n"
8003        );
8004        // `RESET` brings it back to zero.
8005        f.run(&[b"SELECT", b"4"]);
8006        f.run(&[b"RESET"]);
8007        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
8008    }
8009
8010    #[test]
8011    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
8012        let mut f = Fixture::new();
8013        let reply = f.run(&[b"HELLO"]);
8014        assert!(reply.starts_with("*14\r\n"), "{reply}");
8015        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
8016        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
8017        assert!(
8018            reply.contains(":7\r\n"),
8019            "the connection id is in there: {reply}"
8020        );
8021        assert_eq!(
8022            f.run(&[b"HELLO", b"4"]),
8023            "-NOPROTO unsupported protocol version\r\n"
8024        );
8025        assert_eq!(
8026            f.run(&[b"HELLO", b"abc"]),
8027            "-ERR Protocol version is not an integer or out of range\r\n"
8028        );
8029        assert_eq!(
8030            f.run(&[b"HELLO", b"3", b"SETNAME"]),
8031            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
8032        );
8033        assert!(
8034            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
8035                .starts_with("%7\r\n")
8036        );
8037        assert_eq!(f.session.name(), b"bob");
8038        f.run(&[b"RESET"]);
8039        assert_eq!(f.session.name(), b"");
8040    }
8041
8042    #[test]
8043    fn command_describes_this_server_in_the_shape_a_driver_reads() {
8044        let mut f = Fixture::new();
8045        let count = format!(":{}\r\n", COMMANDS.len());
8046        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
8047        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
8048        assert_eq!(
8049            info,
8050            "*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\
8051             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*1\r\n*6\r\n\
8052             $5\r\nflags\r\n*2\r\n+RO\r\n+access\r\n\
8053             $12\r\nbegin_search\r\n*4\r\n$4\r\ntype\r\n$5\r\nindex\r\n$4\r\nspec\r\n\
8054             *2\r\n$5\r\nindex\r\n:1\r\n\
8055             $9\r\nfind_keys\r\n*4\r\n$4\r\ntype\r\n$5\r\nrange\r\n$4\r\nspec\r\n\
8056             *6\r\n$7\r\nlastkey\r\n:0\r\n$7\r\nkeystep\r\n:1\r\n$5\r\nlimit\r\n:0\r\n\
8057             *0\r\n"
8058        );
8059        // A null in the list, and the plain one: `$-1` and not `*-1`.
8060        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
8061        assert_eq!(
8062            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
8063            "*1\r\n$8\r\ngetrange\r\n"
8064        );
8065        assert_eq!(
8066            f.run(&[b"COMMAND", b"NOPE"]),
8067            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
8068        );
8069    }
8070
8071    /// A cluster aware client asks this question and then routes on the
8072    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
8073    /// that matters.
8074    #[test]
8075    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
8076        let mut f = Fixture::new();
8077        assert_eq!(
8078            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
8079            "*1\r\n$1\r\nk\r\n"
8080        );
8081        assert_eq!(
8082            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
8083            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
8084        );
8085        assert_eq!(
8086            f.run(&[
8087                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
8088            ]),
8089            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
8090        );
8091        assert_eq!(
8092            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
8093            "-ERR The command has no key arguments\r\n"
8094        );
8095        assert_eq!(
8096            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
8097            "-ERR Invalid number of arguments specified for command\r\n"
8098        );
8099    }
8100
8101    #[test]
8102    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
8103        let mut f = Fixture::new();
8104        assert_eq!(
8105            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
8106            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
8107        );
8108        // A pattern matches more than one, and a setting two patterns both ask
8109        // for is still sent once.
8110        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
8111        assert!(both.starts_with("*6\r\n"), "{both}");
8112        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
8113        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
8114        assert_eq!(
8115            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
8116            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
8117        );
8118        assert_eq!(
8119            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
8120            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
8121        );
8122        assert_eq!(
8123            f.run(&[b"CONFIG", b"GET"]),
8124            "-ERR wrong number of arguments for 'config|get' command\r\n"
8125        );
8126        // Too few arguments and an odd number of them are different
8127        // complaints, which is the sort of thing only the real server tells
8128        // you.
8129        assert_eq!(
8130            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
8131            "-ERR wrong number of arguments for 'config|set' command\r\n"
8132        );
8133        assert_eq!(
8134            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
8135            "-ERR syntax error\r\n"
8136        );
8137        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
8138        assert_eq!(
8139            f.run(&[b"CONFIG", b"REWRITE"]),
8140            "-ERR The server is running without a config file\r\n"
8141        );
8142    }
8143
8144    /// A name is matched without regard to case, in both of the two ways a name
8145    /// can be given. This was case sensitive and a real server is not, so
8146    /// `CONFIG GET MAXMEMORY` answered nothing at all.
8147    ///
8148    /// And the name it answers under is the one the client spelled when they
8149    /// spelled it out, and its own when they gave a pattern, which is the shape
8150    /// of upstream's code rather than a decision it made.
8151    #[test]
8152    fn a_setting_is_found_whatever_case_it_is_asked_for_in() {
8153        let mut f = Fixture::new();
8154        assert_eq!(
8155            f.run(&[b"CONFIG", b"GET", b"MAXMEMORY"]),
8156            "*2\r\n$9\r\nMAXMEMORY\r\n$1\r\n0\r\n"
8157        );
8158        let starred = f.run(&[b"CONFIG", b"GET", b"MAXMEM*"]);
8159        assert!(starred.starts_with("*6\r\n"), "{starred}");
8160        assert!(starred.contains("maxmemory"), "{starred}");
8161        assert!(!starred.contains("MAXMEM"), "{starred}");
8162        // The first argument that matches decides, because upstream has the
8163        // setting in its match table by the time it looks at the second.
8164        assert_eq!(
8165            f.run(&[b"CONFIG", b"GET", b"MAXMEMORY", b"maxmemory"]),
8166            "*2\r\n$9\r\nMAXMEMORY\r\n$1\r\n0\r\n"
8167        );
8168        let both = f.run(&[b"CONFIG", b"GET", b"maxmem*", b"MAXMEMORY"]);
8169        assert!(both.starts_with("*6\r\n"), "{both}");
8170        assert!(!both.contains("MAXMEMORY"), "{both}");
8171    }
8172
8173    /// The four settings a slot migration runs under, two of which a pattern
8174    /// finds and two of which only their own name does.
8175    #[test]
8176    fn the_migration_settings_read_and_write_like_the_reference() {
8177        let mut f = Fixture::new();
8178        let group = f.run(&[b"CONFIG", b"GET", b"cluster-slot-migration-*"]);
8179        assert!(group.starts_with("*4\r\n"), "{group}");
8180        assert!(group.contains("handoff-max-lag-bytes"), "{group}");
8181        assert!(group.contains("write-pause-timeout"), "{group}");
8182        assert!(!group.contains("max-archived-tasks"), "{group}");
8183        assert!(!group.contains("sync-buffer-drain-timeout"), "{group}");
8184        // Hidden means a pattern does not find it, not that it is not there.
8185        assert_eq!(
8186            f.run(&[
8187                b"CONFIG",
8188                b"GET",
8189                b"cluster-slot-migration-max-archived-tasks"
8190            ]),
8191            "*2\r\n$41\r\ncluster-slot-migration-max-archived-tasks\r\n$2\r\n32\r\n"
8192        );
8193        // The one that counts bytes takes a unit and reads back as a plain
8194        // number of bytes, the same way `maxmemory` does.
8195        assert_eq!(
8196            f.run(&[
8197                b"CONFIG",
8198                b"SET",
8199                b"cluster-slot-migration-handoff-max-lag-bytes",
8200                b"2mb"
8201            ]),
8202            "+OK\r\n"
8203        );
8204        assert_eq!(
8205            f.run(&[
8206                b"CONFIG",
8207                b"GET",
8208                b"cluster-slot-migration-handoff-max-lag-bytes"
8209            ]),
8210            "*2\r\n$44\r\ncluster-slot-migration-handoff-max-lag-bytes\r\n$7\r\n2097152\r\n"
8211        );
8212        // And the three that count something else do not take one.
8213        assert_eq!(
8214            f.run(&[
8215                b"CONFIG",
8216                b"SET",
8217                b"cluster-slot-migration-write-pause-timeout",
8218                b"10s"
8219            ]),
8220            "-ERR CONFIG SET failed (possibly related to argument 'cluster-slot-migration-write-pause-timeout') - argument couldn't be parsed into an integer\r\n"
8221        );
8222        assert_eq!(
8223            f.run(&[
8224                b"CONFIG",
8225                b"SET",
8226                b"cluster-slot-migration-handoff-max-lag-bytes",
8227                b"-1"
8228            ]),
8229            "-ERR CONFIG SET failed (possibly related to argument 'cluster-slot-migration-handoff-max-lag-bytes') - argument must be a memory value\r\n"
8230        );
8231        assert_eq!(
8232            f.run(&[
8233                b"CONFIG",
8234                b"SET",
8235                b"cluster-slot-migration-write-pause-timeout",
8236                b"-1"
8237            ]),
8238            "-ERR CONFIG SET failed (possibly related to argument 'cluster-slot-migration-write-pause-timeout') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
8239        );
8240        // The archived count is the only one with a ceiling, because it is an
8241        // int on the other side and the rest are a long long.
8242        assert_eq!(
8243            f.run(&[
8244                b"CONFIG",
8245                b"SET",
8246                b"cluster-slot-migration-max-archived-tasks",
8247                b"0"
8248            ]),
8249            "-ERR CONFIG SET failed (possibly related to argument 'cluster-slot-migration-max-archived-tasks') - argument must be between 1 and 2147483647 inclusive\r\n"
8250        );
8251        assert_eq!(
8252            f.run(&[
8253                b"CONFIG",
8254                b"SET",
8255                b"cluster-slot-migration-max-archived-tasks",
8256                b"2147483648"
8257            ]),
8258            "-ERR CONFIG SET failed (possibly related to argument 'cluster-slot-migration-max-archived-tasks') - argument must be between 1 and 2147483647 inclusive\r\n"
8259        );
8260    }
8261
8262    #[test]
8263    fn the_eviction_policy_reads_back_what_was_written_to_it() {
8264        let mut f = Fixture::new();
8265        assert_eq!(
8266            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
8267            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
8268        );
8269        assert_eq!(
8270            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
8271            "+OK\r\n",
8272            "the name is matched without regard to case, like every other one"
8273        );
8274        assert_eq!(
8275            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
8276            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
8277        );
8278        // And INFO agrees with CONFIG, which it did not when it was a literal.
8279        assert!(
8280            f.run(&[b"INFO", b"memory"])
8281                .contains("maxmemory_policy:allkeys-lfu"),
8282            "INFO and CONFIG disagree about the policy"
8283        );
8284        // The refusal names every legal value in the order the real server's
8285        // enum table lists them, because a client comparing the message compares
8286        // the whole string.
8287        assert_eq!(
8288            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
8289            "-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"
8290        );
8291        // A bad pair leaves the good one in the same command alone, and the
8292        // policy is checked by the same pass that checks the numbers.
8293        assert_eq!(
8294            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
8295            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
8296        );
8297        f.run(&[
8298            b"CONFIG",
8299            b"SET",
8300            b"hash-max-listpack-entries",
8301            b"7",
8302            b"maxmemory-policy",
8303            b"nonsense",
8304        ]);
8305        assert_eq!(
8306            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
8307            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
8308        );
8309    }
8310
8311    #[test]
8312    fn the_three_eviction_numbers_read_back_too() {
8313        let mut f = Fixture::new();
8314        for (name, default, set) in [
8315            ("maxmemory-samples", "5", "12"),
8316            ("lfu-log-factor", "10", "3"),
8317            ("lfu-decay-time", "1", "60"),
8318        ] {
8319            let get = || {
8320                format!(
8321                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
8322                    name.len(),
8323                    default.len()
8324                )
8325            };
8326            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
8327            assert_eq!(
8328                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
8329                "+OK\r\n"
8330            );
8331            assert_eq!(
8332                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
8333                format!(
8334                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
8335                    name.len(),
8336                    set.len()
8337                )
8338            );
8339            // A number that is not a number is refused with the same sentence
8340            // every other number gets, which names the setting the client typed.
8341            assert_eq!(
8342                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
8343                format!(
8344                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
8345                )
8346            );
8347        }
8348    }
8349
8350    #[test]
8351    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
8352        let mut f = Fixture::new();
8353        assert_eq!(
8354            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
8355            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
8356            "no limit is the default"
8357        );
8358        // The pairing is Redis's and it is a trap: the bare letter is a power of
8359        // ten and the one with the b is a power of two.
8360        for (typed, bytes) in [
8361            (&b"1024"[..], "1024"),
8362            (b"1k", "1000"),
8363            (b"1kb", "1024"),
8364            (b"1M", "1000000"),
8365            (b"1Mb", "1048576"),
8366            (b"1gb", "1073741824"),
8367            (b"100mb", "104857600"),
8368        ] {
8369            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
8370            assert_eq!(
8371                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
8372                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
8373                "set {}",
8374                String::from_utf8_lossy(typed)
8375            );
8376        }
8377        assert!(
8378            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
8379            "the report agrees with the setting"
8380        );
8381
8382        // A unit nobody has heard of, and a negative number, which is not a very
8383        // large one however it is spelled.
8384        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
8385            assert_eq!(
8386                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
8387                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
8388                "refused {}",
8389                String::from_utf8_lossy(bad)
8390            );
8391        }
8392        assert!(
8393            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
8394            "and the refusal left the old one alone"
8395        );
8396    }
8397
8398    #[test]
8399    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
8400        let mut f = Fixture::new();
8401        f.run(&[b"SET", b"here", b"already"]);
8402        // A byte, which is under what an empty server holds, so nothing this
8403        // command could do would get it under. The default policy is
8404        // `noeviction`, so nothing is what it does.
8405        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
8406        assert_eq!(
8407            f.run(&[b"SET", b"k", b"v"]),
8408            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
8409        );
8410        assert_eq!(
8411            f.run(&[b"LPUSH", b"l", b"v"]),
8412            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
8413        );
8414        // Reading is allowed, and so is the one thing that would help.
8415        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
8416        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
8417        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
8418
8419        // Taking the limit away lets the write through again.
8420        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
8421        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
8422    }
8423
8424    /// Not under Miri, for the reason in `filled`: what it is watching is a
8425    /// whole two megabyte segment going back, so the megabytes are the claim
8426    /// and there is no smaller version of it that says the same thing.
8427    #[cfg_attr(miri, ignore = "several megabytes of arena, see `filled`")]
8428    #[test]
8429    fn an_allkeys_policy_makes_room_instead_of_refusing() {
8430        let mut f = Fixture::new();
8431        let val = vec![b'v'; 256];
8432        for i in 0..24000u32 {
8433            let k = format!("key:{i:08}");
8434            f.run(&[b"SET", k.as_bytes(), &val]);
8435        }
8436        let full = f.server.memory_bytes();
8437        assert!(
8438            full > 3 * 1024 * 1024,
8439            "the arena is several segments: {full}"
8440        );
8441
8442        // Two megabytes under what it is holding, which is one segment's worth,
8443        // so getting there means giving a whole segment back and not just
8444        // dropping a few records.
8445        let limit = full - 2 * 1024 * 1024;
8446        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
8447        f.run(&[
8448            b"CONFIG",
8449            b"SET",
8450            b"maxmemory",
8451            limit.to_string().as_bytes(),
8452        ]);
8453
8454        // Writes keep working the whole way down. The budget means one command
8455        // does not do it all, so this runs until the server has settled and
8456        // checks that nothing was refused on the way.
8457        for i in 0..2000u32 {
8458            let k = format!("new:{i:08}");
8459            assert_eq!(
8460                f.run(&[b"SET", k.as_bytes(), &val]),
8461                "+OK\r\n",
8462                "write {i} was refused"
8463            );
8464            f.server.refresh_memory();
8465            if f.server.memory_bytes() <= limit {
8466                break;
8467            }
8468        }
8469        assert!(
8470            f.server.memory_bytes() <= limit,
8471            "it never got under: {} against {limit}",
8472            f.server.memory_bytes()
8473        );
8474        let info = f.run(&[b"INFO", b"stats"]);
8475        assert!(!info.contains("evicted_keys:0"), "{info}");
8476        assert!(
8477            f.run(&[b"DBSIZE"]) != ":0\r\n",
8478            "and it did not empty the database to get there"
8479        );
8480    }
8481
8482    /// Not under Miri. Every round is eleven commands over six collections
8483    /// holding two hundred byte values, which is a third of a second each
8484    /// interpreted, and the rounds cannot come down far: one in seven takes an
8485    /// entry back out, so under about a hundred and seventy of them the
8486    /// collections never reach the hundred and twenty eight entries where the
8487    /// small representations give up and become the big ones, and a
8488    /// representation changing under the running total is one of the five
8489    /// things this is here to watch. What is left is an hour, for an accounting
8490    /// claim rather than a safety one, and the commands it sends are sent a few
8491    /// at a time by the tests around it.
8492    #[cfg_attr(miri, ignore = "an hour of commands, and they cannot come down")]
8493    #[test]
8494    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
8495        // The limit is judged against a number kept as the collections move,
8496        // rather than found by asking all of them, and the two have to be the
8497        // same number or the limit is enforced against a fiction. This does the
8498        // things that move it, which is growing a collection, shrinking one,
8499        // changing its representation, deleting it and reusing its slot, across
8500        // all five types, and checks the two against each other as it goes.
8501        let mut f = Fixture::new();
8502        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
8503        let big = vec![b'v'; 200];
8504
8505        for i in 0..400u32 {
8506            let n = i.to_string();
8507            let n = n.as_bytes();
8508            f.run(&[b"SADD", b"s", n]);
8509            f.run(&[b"SADD", b"s2", &big]);
8510            f.run(&[b"HSET", b"h", n, &big]);
8511            f.run(&[b"RPUSH", b"l", &big]);
8512            f.run(&[b"ZADD", b"z", n, n]);
8513            f.run(&[b"ARSET", b"a", n, &big]);
8514            if i % 7 == 0 {
8515                f.run(&[b"SREM", b"s", n]);
8516                f.run(&[b"HDEL", b"h", n]);
8517                f.run(&[b"LPOP", b"l"]);
8518                f.run(&[b"ZREM", b"z", n]);
8519                f.run(&[b"ARDEL", b"a", n]);
8520            }
8521            if i % 53 == 0 {
8522                // Every type deleted and made again, so a slot goes on the free
8523                // list and comes back holding something else.
8524                f.run(&[b"DEL", b"s2"]);
8525            }
8526            assert_eq!(
8527                f.server.settled_memory(),
8528                f.server.memory_bytes(),
8529                "after round {i}"
8530            );
8531        }
8532
8533        // The run has to have built something, or the two numbers agreeing is
8534        // two zeroes agreeing.
8535        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
8536        assert!(
8537            f.server.memory_bytes() > 512 * 1024,
8538            "{}",
8539            f.server.memory_bytes()
8540        );
8541
8542        // And it survives the collections going away entirely.
8543        f.run(&[b"FLUSHALL"]);
8544        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
8545    }
8546
8547    #[test]
8548    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
8549        // A server with no limit does not keep the running total, so setting a
8550        // limit on a database that is already full has to start it from a walk.
8551        // If it did not, the first reading would be zero and the server would
8552        // think it had all the room in the world.
8553        let mut f = Fixture::new();
8554        for i in 0..200u32 {
8555            let n = i.to_string();
8556            f.run(&[b"SADD", b"s", n.as_bytes()]);
8557            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
8558        }
8559        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
8560        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
8561
8562        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
8563        for i in 200..400u32 {
8564            let n = i.to_string();
8565            f.run(&[b"SADD", b"s", n.as_bytes()]);
8566        }
8567        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
8568        assert_eq!(
8569            f.server.settled_memory(),
8570            f.server.memory_bytes(),
8571            "the writes it was not watching are in the number it started from"
8572        );
8573    }
8574
8575    /// A reading adds up sixteen databases and only weighs the ones that moved,
8576    /// so the ones it did not weigh have to be in the total at what they were
8577    /// holding when it last did.
8578    ///
8579    /// The walk is the number this is checked against, because the walk is what
8580    /// the server actually holds. Getting this wrong in the direction that
8581    /// forgets a database is a limit enforced against a fiction, and in the other
8582    /// direction it is a server evicting keys to get under a number it is already
8583    /// under.
8584    #[test]
8585    fn a_database_nobody_has_touched_is_still_in_the_total() {
8586        let mut f = Fixture::new();
8587        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
8588        f.run(&[b"SELECT", b"3"]);
8589        for i in 0..200u32 {
8590            let n = i.to_string();
8591            f.run(&[b"SADD", b"s", n.as_bytes()]);
8592        }
8593        f.run(&[b"SELECT", b"0"]);
8594        for i in 0..200u32 {
8595            let k = format!("key:{i}");
8596            f.run(&[b"SET", k.as_bytes(), b"value"]);
8597        }
8598
8599        // More readings in a row than there are databases, with nothing running
8600        // in between, so every one of them after the first is answering mostly
8601        // out of what it remembers.
8602        for turn in 0..DATABASES * 2 {
8603            assert_eq!(
8604                f.server.settled_memory(),
8605                f.server.memory_bytes(),
8606                "reading {turn}"
8607            );
8608        }
8609
8610        // And a database that empties while it is not the selected one is not
8611        // still counted at what it used to hold.
8612        f.run(&[b"SELECT", b"3"]);
8613        f.run(&[b"FLUSHDB"]);
8614        f.run(&[b"SELECT", b"0"]);
8615        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
8616    }
8617
8618    /// One database is weighed again on every reading whatever the marks say, so
8619    /// a change nothing marked is out of date for a while rather than for good.
8620    ///
8621    /// The marks are thrown away by hand here, which is what a path that changed
8622    /// a database and did not say so would leave behind. Sixteen readings is the
8623    /// worst case, because the cursor moves one database a reading.
8624    #[test]
8625    fn a_change_nothing_marked_is_found_within_a_turn_of_the_databases() {
8626        let mut f = Fixture::new();
8627        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
8628        f.server.settled_memory();
8629        f.run(&[b"SELECT", b"7"]);
8630        for i in 0..200u32 {
8631            let n = i.to_string();
8632            f.run(&[b"SADD", b"s", n.as_bytes()]);
8633        }
8634        f.server.mine().to_weigh();
8635
8636        let walk = f.server.memory_bytes();
8637        let mut readings = 0;
8638        while f.server.settled_memory() != walk {
8639            readings += 1;
8640            assert!(readings < DATABASES, "still out of date after {readings}");
8641        }
8642    }
8643
8644    /// The reading is taken once a millisecond and not once a batch, and the
8645    /// gate is what says so.
8646    ///
8647    /// A batch is a hundred nanoseconds, so the difference between the two is
8648    /// four orders of magnitude of walking the databases to be told the same
8649    /// number back.
8650    #[test]
8651    fn a_thread_takes_one_memory_reading_a_millisecond() {
8652        let f = Fixture::new();
8653        let mine = f.server.mine();
8654        assert!(mine.measuring(7));
8655        assert!(!mine.measuring(7));
8656        assert!(!mine.measuring(7));
8657        assert!(mine.measuring(8));
8658        assert!(!mine.measuring(8));
8659    }
8660
8661    #[test]
8662    fn evicted_keys_and_expired_keys_are_different_numbers() {
8663        let mut f = Fixture::new();
8664        // Nothing has been evicted and nothing can be under the default policy,
8665        // so this stays at zero while the other one moves.
8666        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
8667        f.server.advance_clock_ms(20);
8668        f.run(&[b"GET", b"gone"]);
8669        let info = f.run(&[b"INFO", b"stats"]);
8670        assert!(info.contains("expired_keys:1"), "{info}");
8671        assert!(info.contains("evicted_keys:0"), "{info}");
8672    }
8673
8674    #[test]
8675    fn the_two_counters_count_the_reads_and_nothing_else() {
8676        let mut f = Fixture::new();
8677        f.run(&[b"SET", b"k", b"v"]);
8678        f.run(&[b"GET", b"k"]);
8679        f.run(&[b"GET", b"nope"]);
8680        f.run(&[b"EXISTS", b"k", b"nope"]);
8681        // The write at the top is not in either number, and the three reads
8682        // under it are, once for each key each of them names.
8683        let info = f.run(&[b"INFO", b"stats"]);
8684        assert!(info.contains("keyspace_hits:2"), "{info}");
8685        assert!(info.contains("keyspace_misses:2"), "{info}");
8686
8687        f.run(&[b"CONFIG", b"RESETSTAT"]);
8688        let info = f.run(&[b"INFO", b"stats"]);
8689        assert!(info.contains("keyspace_hits:0"), "{info}");
8690        assert!(info.contains("keyspace_misses:0"), "{info}");
8691    }
8692
8693    /// The shapes that look one key up more than once, which a real server
8694    /// counts once because it only looks once. See `misses::reading`.
8695    #[test]
8696    fn a_read_that_visits_its_key_twice_is_counted_once() {
8697        let mut f = Fixture::new();
8698        f.run(&[b"ZADD", b"z", b"1", b"m"]);
8699        f.run(&[b"ZRANGE", b"z", b"0", b"-1"]);
8700        f.run(&[b"ZMSCORE", b"z", b"m", b"gone", b"also gone"]);
8701        f.run(&[b"OBJECT", b"ENCODING", b"z"]);
8702        f.run(&[b"DUMP", b"z"]);
8703        let info = f.run(&[b"INFO", b"stats"]);
8704        assert!(info.contains("keyspace_hits:4"), "{info}");
8705        // A member that is not in the sorted set is not a miss. Only a key that
8706        // is not there is one.
8707        assert!(info.contains("keyspace_misses:0"), "{info}");
8708    }
8709
8710    /// A lookup on the way to a write is not a read, which is the other half of
8711    /// what `lookups::quiet` is for.
8712    #[test]
8713    fn the_key_a_read_writes_afterwards_is_not_counted() {
8714        let mut f = Fixture::new();
8715        f.run(&[b"SET", b"s", b"v"]);
8716        f.run(&[b"COPY", b"s", b"dst"]);
8717        f.run(&[b"GETEX", b"s", b"EX", b"100"]);
8718        f.run(&[b"BITOP", b"AND", b"into", b"s", b"nope"]);
8719        let info = f.run(&[b"INFO", b"stats"]);
8720        // The source of the copy, the key `GETEX` answers with, and one of the
8721        // two sources of the operation. The three destinations are written and
8722        // never read, so none of them is in here.
8723        assert!(info.contains("keyspace_hits:3"), "{info}");
8724        assert!(info.contains("keyspace_misses:1"), "{info}");
8725    }
8726
8727    #[test]
8728    fn the_object_subcommands_follow_the_policy() {
8729        let mut f = Fixture::new();
8730        f.run(&[b"SET", b"s", b"v"]);
8731        // Under the default the clock is kept and the counter is not, and under
8732        // an LFU policy it is the other way round. Each subcommand refuses on
8733        // the side where its reading of the three bytes means nothing.
8734        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
8735        assert!(
8736            f.run(&[b"OBJECT", b"FREQ", b"s"])
8737                .starts_with("-ERR An LFU maxmemory policy is not selected"),
8738        );
8739
8740        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
8741        assert!(
8742            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
8743                .starts_with("-ERR An LFU maxmemory policy is selected"),
8744        );
8745        // The key was written under a clock policy, so what comes back is that
8746        // clock read as a counter. It is a number and not an error, which is the
8747        // point: switching at runtime does not invalidate anything, it only makes
8748        // the old field mean something else until the key is used again.
8749        assert!(
8750            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
8751            "FREQ should answer under an LFU policy"
8752        );
8753    }
8754
8755    #[test]
8756    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
8757        let mut f = Fixture::new();
8758        f.run(&[b"SET", b"s", b"hello"]);
8759        f.run(&[b"SET", b"n", b"123"]);
8760        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
8761        f.run(&[b"SADD", b"ss", b"a", b"b"]);
8762        f.run(&[b"HSET", b"h", b"f", b"v"]);
8763        for (key, want) in [
8764            (b"s".as_slice(), "embstr"),
8765            (b"n", "int"),
8766            (b"si", "intset"),
8767            (b"ss", "listpack"),
8768            (b"h", "listpack"),
8769        ] {
8770            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
8771            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
8772        }
8773
8774        // A field deadline widens the blob rather than promoting it, and this
8775        // is the only place a client can see that happen.
8776        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
8777        assert_eq!(
8778            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
8779            "$10\r\nlistpackex\r\n"
8780        );
8781
8782        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
8783        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
8784        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
8785    }
8786
8787    #[test]
8788    fn object_answers_nil_for_a_key_that_is_not_there() {
8789        let mut f = Fixture::new();
8790        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
8791            assert_eq!(
8792                f.run(&[b"OBJECT", sub, b"nokey"]),
8793                "$-1\r\n",
8794                "a nil and not an error, which is what 8.10.1 does"
8795            );
8796        }
8797        // And the key is looked up before FREQ has its complaint, so the
8798        // complaint only reaches a key that exists.
8799        f.run(&[b"SET", b"s", b"v"]);
8800        assert!(
8801            f.run(&[b"OBJECT", b"FREQ", b"s"])
8802                .starts_with("-ERR An LFU maxmemory policy is not"),
8803        );
8804        assert_eq!(
8805            f.run(&[b"OBJECT", b"NOPE", b"s"]),
8806            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
8807        );
8808        assert_eq!(
8809            f.run(&[b"OBJECT", b"ENCODING"]),
8810            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
8811        );
8812        assert_eq!(
8813            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
8814            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
8815        );
8816        assert_eq!(
8817            f.run(&[b"OBJECT"]),
8818            "-ERR wrong number of arguments for 'object' command\r\n"
8819        );
8820    }
8821
8822    #[test]
8823    fn memory_usage_counts_the_record_the_body_and_a_share_of_the_index() {
8824        let mut f = Fixture::new();
8825        assert_eq!(
8826            f.run(&[b"MEMORY", b"USAGE", b"nokey"]),
8827            "$-1\r\n",
8828            "a null and not an error, the same as OBJECT"
8829        );
8830        f.run(&[b"SET", b"s", b"hello"]);
8831        let small = int_of(&f.run(&[b"MEMORY", b"USAGE", b"s"]));
8832        assert!(
8833            small > 5,
8834            "the value is in there and so are the name and the header"
8835        );
8836        // A longer value under the same name costs more, and by about what the
8837        // extra bytes are, since a string lives in its own record.
8838        f.run(&[b"SET", b"s", &[b'x'; 1000]]);
8839        let big = int_of(&f.run(&[b"MEMORY", b"USAGE", b"s"]));
8840        assert!(
8841            big - small >= 995 && big - small <= 1005,
8842            "{small} then {big}"
8843        );
8844        // A collection costs its body, so a set of a hundred members is worth
8845        // far more than a set of one.
8846        f.run(&[b"SADD", b"one", b"a"]);
8847        f.run(&[b"SADD", b"many", b"a"]);
8848        for i in 0..100u32 {
8849            f.run(&[b"SADD", b"many", format!("member:{i}").as_bytes()]);
8850        }
8851        assert!(
8852            int_of(&f.run(&[b"MEMORY", b"USAGE", b"many"]))
8853                > int_of(&f.run(&[b"MEMORY", b"USAGE", b"one"]))
8854        );
8855        // Asking twice gives the same answer, which is the property a sampled
8856        // estimate does not have.
8857        assert_eq!(
8858            f.run(&[b"MEMORY", b"USAGE", b"many"]),
8859            f.run(&[b"MEMORY", b"USAGE", b"many"])
8860        );
8861    }
8862
8863    #[test]
8864    fn memory_usage_reads_samples_and_does_not_use_it() {
8865        let mut f = Fixture::new();
8866        f.run(&[b"SET", b"s", b"v"]);
8867        let plain = f.run(&[b"MEMORY", b"USAGE", b"s"]);
8868        for count in [b"0".as_slice(), b"1", b"5", b"1000"] {
8869            assert_eq!(
8870                f.run(&[b"MEMORY", b"USAGE", b"s", b"SAMPLES", count]),
8871                plain
8872            );
8873        }
8874        // The last one wins, which is what the reference's loop does rather
8875        // than something it decided to do.
8876        assert_eq!(
8877            f.run(&[
8878                b"MEMORY", b"USAGE", b"s", b"SAMPLES", b"1", b"SAMPLES", b"2"
8879            ]),
8880            plain
8881        );
8882        assert_eq!(
8883            f.run(&[b"MEMORY", b"USAGE", b"s", b"SAMPLES"]),
8884            "-ERR syntax error\r\n"
8885        );
8886        assert_eq!(
8887            f.run(&[b"MEMORY", b"USAGE", b"s", b"SAMPLES", b"-1"]),
8888            "-ERR syntax error\r\n"
8889        );
8890        assert_eq!(
8891            f.run(&[b"MEMORY", b"USAGE", b"s", b"SAMPLES", b"nine"]),
8892            "-ERR value is not an integer or out of range\r\n"
8893        );
8894        assert_eq!(
8895            f.run(&[b"MEMORY", b"USAGE", b"s", b"BAD", b"1"]),
8896            "-ERR syntax error\r\n"
8897        );
8898        assert_eq!(
8899            f.run(&[b"MEMORY", b"USAGE"]),
8900            "-ERR wrong number of arguments for 'memory|usage' command\r\n"
8901        );
8902    }
8903
8904    #[test]
8905    fn memory_stats_grows_a_field_for_every_database_holding_a_key() {
8906        let mut f = Fixture::new();
8907        assert!(
8908            f.run(&[b"MEMORY", b"STATS"]).starts_with("*72\r\n"),
8909            "thirty six pairs on a server nobody has written to"
8910        );
8911        f.run(&[b"SET", b"a", b"1"]);
8912        assert!(f.run(&[b"MEMORY", b"STATS"]).starts_with("*74\r\n"));
8913        f.run(&[b"SELECT", b"7"]);
8914        f.run(&[b"SET", b"b", b"2"]);
8915        let reply = f.run(&[b"MEMORY", b"STATS"]);
8916        assert!(reply.starts_with("*76\r\n"));
8917        assert!(reply.contains("\r\n$4\r\ndb.0\r\n"));
8918        assert!(reply.contains("\r\n$4\r\ndb.7\r\n"));
8919        // And the row for a database is the pair a real server puts there.
8920        assert!(reply.contains("overhead.hashtable.main"));
8921        assert!(reply.contains("overhead.hashtable.expires"));
8922        assert!(reply.contains("fragmentation.bytes"));
8923    }
8924
8925    #[test]
8926    fn memory_answers_the_four_that_only_look() {
8927        let mut f = Fixture::new();
8928        assert!(f.run(&[b"MEMORY", b"HELP"]).starts_with("*14\r\n+MEMORY "));
8929        assert_eq!(f.run(&[b"MEMORY", b"PURGE"]), "+OK\r\n");
8930        assert_eq!(
8931            f.run(&[b"MEMORY", b"MALLOC-STATS"]),
8932            "$45\r\nStats not supported for the current allocator\r\n"
8933        );
8934        // An empty server is one the doctor will not form an opinion about, and
8935        // it says so in Sam's own words.
8936        assert!(
8937            f.run(&[b"MEMORY", b"DOCTOR"])
8938                .contains("my issues detector can't be used in these conditions")
8939        );
8940        assert_eq!(
8941            f.run(&[b"MEMORY", b"NOPE"]),
8942            "-ERR unknown subcommand 'NOPE'. Try MEMORY HELP.\r\n"
8943        );
8944        for sub in [
8945            b"STATS".as_slice(),
8946            b"DOCTOR",
8947            b"PURGE",
8948            b"MALLOC-STATS",
8949            b"HELP",
8950        ] {
8951            let name = String::from_utf8_lossy(sub).to_lowercase();
8952            assert_eq!(
8953                f.run(&[b"MEMORY", sub, b"extra"]),
8954                format!("-ERR wrong number of arguments for 'memory|{name}' command\r\n"),
8955                "the subcommand is named and not the container"
8956            );
8957        }
8958        assert_eq!(
8959            f.run(&[b"MEMORY"]),
8960            "-ERR wrong number of arguments for 'memory' command\r\n"
8961        );
8962    }
8963
8964    #[test]
8965    fn command_getkeys_finds_the_key_memory_usage_names() {
8966        let mut f = Fixture::new();
8967        assert_eq!(
8968            f.run(&[b"COMMAND", b"GETKEYS", b"MEMORY", b"USAGE", b"k"]),
8969            "*1\r\n$1\r\nk\r\n"
8970        );
8971        // And the subcommands that name none say so rather than answering an
8972        // empty list.
8973        assert!(
8974            f.run(&[b"COMMAND", b"GETKEYS", b"MEMORY", b"DOCTOR"])
8975                .starts_with("-ERR ")
8976        );
8977    }
8978
8979    #[test]
8980    fn config_moves_the_ladder_and_object_encoding_agrees() {
8981        let mut f = Fixture::new();
8982        assert_eq!(
8983            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
8984            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
8985            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
8986        );
8987        // The old spelling is the same number under a different name, and a
8988        // glob that catches both sends both.
8989        assert_eq!(
8990            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
8991            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
8992        );
8993        assert!(
8994            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
8995                .starts_with("*8\r\n")
8996        );
8997        assert!(
8998            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
8999                .starts_with("*6\r\n")
9000        );
9001
9002        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
9003        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
9004
9005        assert_eq!(
9006            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
9007            "+OK\r\n",
9008            "written under the old name and read back under the new one"
9009        );
9010        assert_eq!(
9011            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
9012            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
9013        );
9014        assert_eq!(
9015            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
9016            "$8\r\nlistpack\r\n",
9017            "the hash that already exists is left exactly where it was"
9018        );
9019        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
9020        assert_eq!(
9021            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
9022            "$9\r\nhashtable\r\n",
9023            "and the next one built goes straight to a table"
9024        );
9025
9026        // The set has three of these and all three move.
9027        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
9028        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
9029        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
9030        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
9031        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
9032        assert_eq!(
9033            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
9034            "$9\r\nhashtable\r\n"
9035        );
9036    }
9037
9038    #[test]
9039    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
9040        let mut f = Fixture::new();
9041        assert_eq!(
9042            f.run(&[
9043                b"CONFIG",
9044                b"SET",
9045                b"hash-max-listpack-entries",
9046                b"7",
9047                b"set-max-listpack-entries",
9048                b"abc"
9049            ]),
9050            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
9051        );
9052        assert_eq!(
9053            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
9054            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
9055            "the pair in front of the bad one did not go in"
9056        );
9057        // The name in the complaint is the one that was typed, so the old
9058        // spelling comes back as the old spelling.
9059        assert_eq!(
9060            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
9061            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
9062        );
9063        assert_eq!(
9064            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
9065            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
9066        );
9067        // A number past what an i64 holds is the parse complaint and not the
9068        // range one, which is upstream reading it before it checks it.
9069        assert_eq!(
9070            f.run(&[
9071                b"CONFIG",
9072                b"SET",
9073                b"set-max-intset-entries",
9074                b"99999999999999999999"
9075            ]),
9076            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
9077        );
9078        assert_eq!(
9079            f.run(&[
9080                b"CONFIG",
9081                b"SET",
9082                b"set-max-intset-entries",
9083                b"9223372036854775807"
9084            ]),
9085            "+OK\r\n"
9086        );
9087    }
9088
9089    #[test]
9090    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
9091        let mut f = Fixture::new();
9092        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
9093        f.run(&[b"SELECT", b"3"]);
9094        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
9095        assert_eq!(
9096            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
9097            "$9\r\nhashtable\r\n",
9098            "these are one server wide number in Redis, whatever a Keyspace carries"
9099        );
9100    }
9101
9102    #[test]
9103    fn info_reports_the_numbers_it_can_stand_behind() {
9104        let mut f = Fixture::new();
9105        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
9106        let all = f.run(&[b"INFO"]);
9107        assert!(all.contains("redis_version:8.8.0"), "{all}");
9108        assert!(
9109            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
9110            "{all}"
9111        );
9112        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
9113        assert!(all.contains("role:master"), "{all}");
9114        // One section is one section.
9115        let clients = f.run(&[b"INFO", b"clients"]);
9116        assert!(clients.contains("connected_clients:0"), "{clients}");
9117        assert!(!clients.contains("redis_version"), "{clients}");
9118        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
9119    }
9120
9121    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
9122    ///
9123    /// This is Redis's `unit/info-command` written against the fixture. Every
9124    /// assertion in it is one of theirs, in their order, and the two fields it
9125    /// turns on are the two that suite was failing on: `master_repl_offset`,
9126    /// which is in the default set, and `rejected_calls`, which is not.
9127    #[test]
9128    fn commandstats_is_asked_for_and_replication_is_not() {
9129        let mut f = Fixture::new();
9130        for arg in ["", "all", "default", "everything"] {
9131            let info = if arg.is_empty() {
9132                f.run(&[b"INFO"])
9133            } else {
9134                f.run(&[b"INFO", arg.as_bytes()])
9135            };
9136            assert!(info.contains("redis_version"), "{arg}: {info}");
9137            assert!(info.contains("used_cpu_user"), "{arg}: {info}");
9138            assert!(info.contains("used_memory"), "{arg}: {info}");
9139            assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
9140            let asked = arg == "all" || arg == "everything";
9141            assert_eq!(
9142                info.contains("rejected_calls"),
9143                asked,
9144                "{arg} should{} carry the command counters: {info}",
9145                if asked { "" } else { " not" }
9146            );
9147        }
9148
9149        let cpu = f.run(&[b"INFO", b"cpu"]);
9150        assert!(cpu.contains("used_cpu_user"), "{cpu}");
9151        assert!(!cpu.contains("used_memory"), "{cpu}");
9152
9153        // Their case, to make the point that a section name is not case
9154        // sensitive any more than a command name is.
9155        let stats = f.run(&[b"INFO", b"commandSTATS"]);
9156        assert!(!stats.contains("used_memory"), "{stats}");
9157        assert!(stats.contains("rejected_calls"), "{stats}");
9158
9159        // Two sections named, and neither of them pulls in a third.
9160        let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
9161        assert!(pair.contains("used_cpu_user"), "{pair}");
9162        assert!(!pair.contains("master_repl_offset"), "{pair}");
9163
9164        let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
9165        assert!(with_all.contains("used_memory"), "{with_all}");
9166        assert!(with_all.contains("master_repl_offset"), "{with_all}");
9167        assert!(with_all.contains("rejected_calls"), "{with_all}");
9168        // A section named twice is still written once.
9169        assert_eq!(
9170            with_all.matches("used_cpu_user_children").count(),
9171            1,
9172            "{with_all}"
9173        );
9174
9175        let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
9176        assert!(with_default.contains("used_memory"), "{with_default}");
9177        assert!(
9178            with_default.contains("master_repl_offset"),
9179            "{with_default}"
9180        );
9181        assert!(!with_default.contains("rejected_calls"), "{with_default}");
9182        assert_eq!(
9183            with_default.matches("used_cpu_user_children").count(),
9184            1,
9185            "{with_default}"
9186        );
9187    }
9188
9189    /// The threads section is the sum taken apart again.
9190    ///
9191    /// A connection belongs to the thread that accepted it for as long as it is
9192    /// open, so how the connections landed decides who does the work, and every
9193    /// other number in `INFO` adds the threads up before anybody sees it. This
9194    /// is the one place the split itself is visible. The test runs on one
9195    /// thread, so what it can show is that the section has a row per thread, and
9196    /// that the work it did all landed in one of them and adds back up to the
9197    /// total.
9198    #[test]
9199    fn the_threads_section_says_where_the_work_landed() {
9200        let mut server = Server::new();
9201        server.set_threads(4);
9202        let mut f = Fixture::on(server);
9203        for _ in 0..3 {
9204            f.run(&[b"PING"]);
9205        }
9206
9207        assert!(!f.run(&[b"INFO"]).contains("# Threads"));
9208        assert!(f.run(&[b"INFO", b"all"]).contains("# Threads"));
9209
9210        let info = f.run(&[b"INFO", b"threads"]);
9211        assert!(info.contains("io_threads:4"), "{info}");
9212        for at in 0..4 {
9213            assert!(info.contains(&format!("thread_{at}:clients=")), "{info}");
9214        }
9215        assert!(!info.contains("thread_4:"), "{info}");
9216
9217        let per = f.server.per_thread();
9218        assert_eq!(per.len(), 4);
9219        assert_eq!(
9220            per.iter().map(|t| t.commands).sum::<u64>(),
9221            f.server.totals().commands
9222        );
9223        assert_eq!(per.iter().filter(|t| t.commands > 0).count(), 1, "{per:?}");
9224    }
9225
9226    /// The three places the thread count is published all say the same number.
9227    ///
9228    /// `io_threads_active` and `io-threads` were both written down rather than
9229    /// read, so a server started with four threads told every client it had one
9230    /// and was not using it. A dashboard reading `INFO server` and a person
9231    /// reading `CONFIG GET` are asking the same question the `# Threads` section
9232    /// answers, and the three of them disagreeing is worse than any one of them
9233    /// being missing.
9234    #[test]
9235    fn the_thread_count_is_the_same_number_wherever_it_is_asked_for() {
9236        let mut f = Fixture::new();
9237        assert!(f.run(&[b"INFO", b"server"]).contains("io_threads_active:1"));
9238        assert_eq!(
9239            f.run(&[b"CONFIG", b"GET", b"io-threads"]),
9240            "*2\r\n$10\r\nio-threads\r\n$1\r\n1\r\n"
9241        );
9242
9243        let mut server = Server::new();
9244        server.set_threads(4);
9245        let mut f = Fixture::on(server);
9246        let info = f.run(&[b"INFO", b"all"]);
9247        assert!(info.contains("io_threads_active:4"), "{info}");
9248        assert!(info.contains("io_threads:4"), "{info}");
9249        assert_eq!(
9250            f.run(&[b"CONFIG", b"GET", b"io-threads"]),
9251            "*2\r\n$10\r\nio-threads\r\n$1\r\n4\r\n"
9252        );
9253        // Immutable the way the fixed settings are, so the write that changes
9254        // nothing is taken and every other one is refused.
9255        assert_eq!(f.run(&[b"CONFIG", b"SET", b"io-threads", b"4"]), "+OK\r\n");
9256        assert_eq!(
9257            f.run(&[b"CONFIG", b"SET", b"io-threads", b"1"]),
9258            "-ERR CONFIG SET failed (possibly related to argument 'io-threads') - can't set immutable config\r\n"
9259        );
9260        assert_eq!(
9261            f.run(&[b"CONFIG", b"SET", b"io-threads", b"lots"]),
9262            "-ERR CONFIG SET failed (possibly related to argument 'io-threads') - can't set immutable config\r\n"
9263        );
9264    }
9265
9266    /// The memory section says what this process may use, not what the machine
9267    /// has.
9268    ///
9269    /// The distinction is the whole point of it. A server inside a container
9270    /// that reports the host's memory is a server whose operator sizes it for
9271    /// memory it will be killed for touching, so all three numbers are there:
9272    /// what the machine has, what the cgroup allows, and the quarter of the
9273    /// tighter one that pools are sized from.
9274    #[test]
9275    fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
9276        let mut f = Fixture::new();
9277        let info = f.run(&[b"INFO", b"memory"]);
9278        for field in [
9279            "total_system_memory:",
9280            "mem_cgroup_limit:",
9281            "mem_limit:",
9282            "mem_budget:",
9283        ] {
9284            assert!(info.contains(field), "no {field} in {info}");
9285        }
9286
9287        let field = |name: &str| -> u64 {
9288            info.lines()
9289                .find_map(|l| l.strip_prefix(name))
9290                .unwrap_or_else(|| panic!("no {name} in {info}"))
9291                .trim()
9292                .parse()
9293                .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
9294        };
9295        let limit = field("mem_limit:");
9296        assert_eq!(field("mem_budget:"), limit / 4, "{info}");
9297        // Zero means there is no limit to report, which is a real answer on a
9298        // machine with no cgroups and no way to ask how big it is.
9299        if limit != 0 {
9300            let host = field("total_system_memory:");
9301            let cgroup = field("mem_cgroup_limit:");
9302            assert!(
9303                limit == host || limit == cgroup,
9304                "the limit came from neither number: {info}"
9305            );
9306        }
9307    }
9308
9309    /// The three counters, each on the path that raises it.
9310    ///
9311    /// `calls` on a command that worked, `failed_calls` on one that ran and
9312    /// answered with an error, and `rejected_calls` on one that never ran at
9313    /// all. The last two are the pair that is easy to collapse into one number
9314    /// and that Redis keeps apart, because a client sending the wrong number of
9315    /// arguments and a client asking for a list element that is not there are
9316    /// not the same problem.
9317    #[test]
9318    fn a_command_counts_what_it_did_separately_from_what_it_refused() {
9319        let mut f = Fixture::new();
9320        f.run(&[b"SET", b"k", b"v"]);
9321        f.run(&[b"SET", b"k", b"w"]);
9322        // Ran, and answered with an error, because `k` is not a list.
9323        f.run(&[b"LPUSH", b"k", b"x"]);
9324        // Never ran: `LPUSH` takes at least three arguments.
9325        f.run(&[b"LPUSH", b"k"]);
9326
9327        let stats = f.run(&[b"INFO", b"commandstats"]);
9328        assert!(
9329            stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
9330            "{stats}"
9331        );
9332        assert!(
9333            stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
9334            "{stats}"
9335        );
9336        assert!(
9337            !stats.contains("cmdstat_zadd"),
9338            "a command nobody has sent has no row: {stats}"
9339        );
9340    }
9341
9342    /// A cache that writes with a deadline and never reads back used to hold
9343    /// every key it had ever written, because lazy expiry needs somebody to walk
9344    /// past a key before it can reclaim it and nobody ever did.
9345    #[test]
9346    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
9347        // Four thousand keys is four thousand trips through dispatch, and what
9348        // Miri charges for is trips rather than keys, so this was over five
9349        // minutes there. An eighth of each keeps everything the test is about,
9350        // which is three keys with a deadline for every one without and a
9351        // sweep that has to reclaim all of the first kind and none of the
9352        // second.
9353        let (dead, live) = if cfg!(miri) {
9354            (375, 125)
9355        } else {
9356            (3_000, 1_000)
9357        };
9358        let mut f = Fixture::new();
9359        for i in 0..dead {
9360            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
9361        }
9362        for i in 0..live {
9363            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
9364        }
9365        let all = format!(":{}\r\n", dead + live);
9366        assert_eq!(f.run(&[b"DBSIZE"]), all);
9367        f.advance(100);
9368        assert_eq!(
9369            f.run(&[b"DBSIZE"]),
9370            all,
9371            "DBSIZE counts records and nothing has read past the dead ones yet"
9372        );
9373
9374        // What the shard loop does, one slice at a time.
9375        let rest = format!(":{live}\r\n");
9376        let mut spent = 0;
9377        for _ in 0..2_000 {
9378            spent += f.server.expire_step(4096);
9379            if f.run(&[b"DBSIZE"]) == rest {
9380                break;
9381            }
9382        }
9383        assert_eq!(f.run(&[b"DBSIZE"]), rest, "spent {spent} looks");
9384        assert!(
9385            f.run(&[b"INFO", b"stats"])
9386                .contains(&format!("expired_keys:{dead}"))
9387        );
9388        for i in 0..live {
9389            assert_eq!(
9390                f.run(&[b"GET", format!("k{i}").as_bytes()]),
9391                "$1\r\nv\r\n",
9392                "it took a key that had no deadline"
9393            );
9394        }
9395    }
9396
9397    #[test]
9398    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
9399        // The keys are only here so that the database the sweep walks is not an
9400        // empty one. Two hundred of them fills as many slots as a sweep looks
9401        // at and is a tenth of the interpreted work.
9402        let n = if cfg!(miri) { 200 } else { 2_000 };
9403        let mut f = Fixture::new();
9404        for i in 0..n {
9405            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
9406        }
9407        assert_eq!(f.server.expire_step(4096), 0);
9408        // And one database having them does not make the other fifteen pay.
9409        f.run(&[b"SELECT", b"3"]);
9410        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
9411        f.advance(100);
9412        for _ in 0..64 {
9413            f.server.expire_step(4096);
9414        }
9415        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9416        f.run(&[b"SELECT", b"0"]);
9417        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{n}\r\n"));
9418        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
9419    }
9420
9421    /// The gate, which is what stops a maintenance slice that runs every hundred
9422    /// nanoseconds from drawing a sample every hundred nanoseconds.
9423    #[test]
9424    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
9425        let mut f = Fixture::new();
9426        for i in 0..500u32 {
9427            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
9428        }
9429        f.advance(100);
9430        let at = f.server.striped(0).now_ms();
9431        f.server.set_clock_ms(at);
9432        // A small budget, so that one slice cannot finish the job and a second
9433        // one having nothing to do would mean the gate and not an empty
9434        // database.
9435        assert!(f.server.expire_slice(8) > 0, "the first one works");
9436        for _ in 0..1_000 {
9437            assert_eq!(
9438                f.server.expire_slice(8),
9439                0,
9440                "the millisecond has not moved and neither should this"
9441            );
9442        }
9443        assert!(
9444            f.server.striped(0).expires() > 400,
9445            "there is plenty left to take"
9446        );
9447        f.server.set_clock_ms(at + 1);
9448        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
9449    }
9450
9451    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
9452    /// how much of a cache is volatile was reading a constant.
9453    #[test]
9454    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
9455        let mut f = Fixture::new();
9456        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
9457        assert!(
9458            f.run(&[b"INFO", b"keyspace"])
9459                .contains("db0:keys=3,expires=0"),
9460            "none of them has one yet"
9461        );
9462        f.run(&[b"EXPIRE", b"a", b"1000"]);
9463        f.run(&[b"EXPIRE", b"b", b"1000"]);
9464        let two = f.run(&[b"INFO", b"keyspace"]);
9465        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
9466        f.run(&[b"PERSIST", b"a"]);
9467        f.run(&[b"DEL", b"b"]);
9468        let none = f.run(&[b"INFO", b"keyspace"]);
9469        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
9470
9471        // Each database answers for itself, the way Redis reports it.
9472        f.run(&[b"SELECT", b"1"]);
9473        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
9474        let both = f.run(&[b"INFO", b"keyspace"]);
9475        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
9476        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
9477    }
9478
9479    /// Not under Miri, which reads a zero on purpose because it has no
9480    /// `getrusage` to call, so the second half of this would burn a billion
9481    /// interpreted multiplications waiting for a number that is never going to
9482    /// move. The first half, that the section is there and has the fields Redis
9483    /// clients look for, is checked by the `INFO` tests above as well, and
9484    /// those do run there.
9485    #[cfg(unix)]
9486    #[cfg_attr(miri, ignore = "no getrusage under Miri, so the number is fixed")]
9487    #[test]
9488    fn info_cpu_reports_processor_time_that_was_really_measured() {
9489        let mut f = Fixture::new();
9490        let cpu = f.run(&[b"INFO", b"cpu"]);
9491        assert!(cpu.contains("# CPU"), "{cpu}");
9492        // Redis's unit/info-command asks for this one by name in three tests.
9493        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
9494        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
9495        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
9496        assert!(!cpu.contains("redis_version"), "{cpu}");
9497
9498        // It is a measurement and not a constant, so it goes up when work
9499        // happens. A tight loop rather than a sleep, because sleeping is the
9500        // one thing that does not move this number.
9501        let before = used_cpu_user(&cpu);
9502        let mut n = 0u64;
9503        let mut rounds = 0;
9504        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
9505            for i in 0..1_000_000u64 {
9506                n = n.wrapping_add(i.wrapping_mul(i));
9507            }
9508            rounds += 1;
9509            // A bound rather than a spin, so a platform where this number does
9510            // not move fails here instead of hanging. Even a clock with whole
9511            // millisecond granularity gets there in the first round or two.
9512            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
9513        }
9514    }
9515
9516    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
9517    #[cfg(unix)]
9518    fn used_cpu_user(info: &str) -> f64 {
9519        info.lines()
9520            .find_map(|l| l.strip_prefix("used_cpu_user:"))
9521            .expect("no used_cpu_user in the reply")
9522            .trim()
9523            .parse()
9524            .expect("used_cpu_user is not a number")
9525    }
9526
9527    /// The safety net under the rule that a body checks its arguments before
9528    /// it writes anything. `MGET` writes its array header first and then reads
9529    /// each key, so if a later argument could fail the header would already be
9530    /// out. Nothing in the string group does that today and this is what would
9531    /// catch the first one that did.
9532    #[test]
9533    fn a_command_that_fails_leaves_nothing_half_written() {
9534        let mut f = Fixture::new();
9535        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
9536        assert_eq!(reply, "-ERR offset is out of range\r\n");
9537        assert!(!reply.contains(':'), "no integer went out in front of it");
9538    }
9539
9540    #[test]
9541    fn quit_answers_first_and_closes_after() {
9542        let mut f = Fixture::new();
9543        let (flow, reply) = f.flow(&[b"QUIT"]);
9544        assert_eq!(reply, "+OK\r\n");
9545        assert_eq!(flow, Flow::Close);
9546    }
9547
9548    /// A server that has not been asked to stop is not stopping, and one that
9549    /// has says so without writing anything back.
9550    ///
9551    /// The empty reply is the point. Redis answers nothing at all here and the
9552    /// client sees the socket close, and an `OK` would be a promise from a
9553    /// process that is about to not exist.
9554    #[test]
9555    fn shutdown_writes_nothing_and_sets_the_flag() {
9556        let mut f = Fixture::new();
9557        assert!(!f.server.stopping(), "nobody has asked yet");
9558
9559        let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
9560        assert_eq!(reply, "");
9561        assert_eq!(flow, Flow::Close);
9562        assert!(f.server.stopping());
9563    }
9564
9565    /// Every flag combination 8.10.1 takes, and every one it refuses.
9566    ///
9567    /// The refusals are the half worth pinning down. `SAVE` and `NOSAVE`
9568    /// contradict each other, `ABORT` says to do nothing so it cannot be
9569    /// combined with a word about how to do it, and repeating any one of them
9570    /// is fine. All of it was read off a running 8.10.1 rather than worked out
9571    /// from the documentation, which does not say.
9572    ///
9573    /// The fixtures here save into a directory of their own because two of the
9574    /// combinations carry `SAVE`, and a test that writes a file into whatever
9575    /// directory the test runner happened to start in leaves it there.
9576    #[test]
9577    fn shutdown_takes_the_flags_redis_takes() {
9578        let s = Saves::new("shutdown-flags");
9579        for flags in [
9580            &[b"NOSAVE".as_slice()][..],
9581            &[b"SAVE"],
9582            &[b"NOW"],
9583            &[b"FORCE"],
9584            &[b"nosave"],
9585            &[b"NOW", b"NOW"],
9586            &[b"SAVE", b"SAVE"],
9587            &[b"NOSAVE", b"NOW", b"FORCE"],
9588        ] {
9589            let mut f = Fixture::new();
9590            f.server.set_dir(s.dir.clone());
9591            let mut parts = vec![b"SHUTDOWN".as_slice()];
9592            parts.extend_from_slice(flags);
9593            let (flow, reply) = f.flow(&parts);
9594            assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
9595            assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
9596            assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
9597        }
9598
9599        for flags in [
9600            &[b"BOGUS".as_slice()][..],
9601            &[b"SAVE", b"NOSAVE"],
9602            &[b"NOSAVE", b"SAVE"],
9603            &[b"ABORT", b"NOW"],
9604            &[b"NOSAVE", b"ABORT"],
9605            &[b"NOW", b"FORCE", b"ABORT"],
9606        ] {
9607            let mut f = Fixture::new();
9608            let mut parts = vec![b"SHUTDOWN".as_slice()];
9609            parts.extend_from_slice(flags);
9610            assert_eq!(
9611                f.run(&parts),
9612                "-ERR syntax error\r\n",
9613                "SHUTDOWN {flags:?} was accepted"
9614            );
9615            assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
9616        }
9617    }
9618
9619    /// `ABORT` has nothing to call off, ever.
9620    ///
9621    /// A shutdown here is decided and done inside one turn of the loop, so
9622    /// there is no window in which one is in progress. That makes Redis's
9623    /// message for a cancel with nothing to cancel the right answer every time
9624    /// rather than only when nothing happens to be pending. Two `ABORT`s is
9625    /// still one `ABORT`, which is what 8.10.1 does.
9626    #[test]
9627    fn shutdown_abort_never_has_anything_to_abort() {
9628        let mut f = Fixture::new();
9629        for parts in [
9630            &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
9631            &[b"SHUTDOWN", b"ABORT", b"ABORT"],
9632        ] {
9633            assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
9634            assert!(!f.server.stopping(), "an abort stopped the server");
9635        }
9636    }
9637
9638    /// A fixture whose server writes into a directory of its own.
9639    ///
9640    /// Every test here really writes files, because the whole point of the
9641    /// command is the files and a backup that is only a state machine would
9642    /// pass a test suite and fail the first person who tried to restore one.
9643    /// The directory carries the test's name so that the suite can run its
9644    /// tests in parallel the way it always does.
9645    struct Backups {
9646        f: Fixture,
9647        dir: PathBuf,
9648    }
9649
9650    impl Backups {
9651        fn new(name: &str) -> Backups {
9652            let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
9653            let _ = std::fs::remove_dir_all(&dir);
9654            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
9655            let mut f = Fixture::new();
9656            f.server.set_dir(dir.clone());
9657            Backups { f, dir }
9658        }
9659
9660        fn run(&mut self, parts: &[&[u8]]) -> String {
9661            self.f.run(parts)
9662        }
9663
9664        /// The names in `backupdir`, sorted, so a test can say what is on disk.
9665        fn files(&self) -> Vec<String> {
9666            let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
9667                Ok(entries) => entries
9668                    .filter_map(|e| e.ok())
9669                    .map(|e| e.file_name().to_string_lossy().into_owned())
9670                    .collect(),
9671                Err(_) => Vec::new(),
9672            };
9673            names.sort();
9674            names
9675        }
9676
9677        fn read(&self, name: &str) -> Vec<u8> {
9678            std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
9679        }
9680    }
9681
9682    impl Drop for Backups {
9683        fn drop(&mut self) {
9684            let _ = std::fs::remove_dir_all(&self.dir);
9685        }
9686    }
9687
9688    /// The four states and the moves between them, in the order a client walks
9689    /// them, with the files checked at every step.
9690    #[test]
9691    fn backup_walks_the_states_the_reference_walks() {
9692        let mut b = Backups::new("states");
9693        let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
9694
9695        assert!(status(&mut b).contains("idle"));
9696        assert!(b.files().is_empty(), "an idle server has written a backup");
9697
9698        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
9699        assert!(status(&mut b).contains("incrementing"));
9700        assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
9701
9702        assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
9703        assert!(status(&mut b).contains("sealed"));
9704        assert_eq!(
9705            b.files(),
9706            [
9707                "appendonly.aof.1.base.rdb",
9708                "appendonly.aof.1.incr.aof",
9709                "appendonly.aof.manifest",
9710            ]
9711        );
9712
9713        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
9714        assert!(status(&mut b).contains("idle"));
9715        assert!(b.files().is_empty(), "cleanup left something behind");
9716    }
9717
9718    /// Every move that is refused, in the reference's words.
9719    #[test]
9720    fn backup_refuses_the_moves_the_reference_refuses() {
9721        let mut b = Backups::new("refusals");
9722
9723        assert_eq!(
9724            b.run(&[b"BACKUP", b"SEAL"]),
9725            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
9726        );
9727        assert_eq!(
9728            b.run(&[b"BACKUP", b"ABORT"]),
9729            "-ERR No backup in progress\r\n"
9730        );
9731        // Cleanup from idle is not an error, it is a way of saying there was
9732        // nothing to clean up.
9733        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
9734
9735        b.run(&[b"BACKUP", b"START"]);
9736        assert_eq!(
9737            b.run(&[b"BACKUP", b"START"]),
9738            "-ERR A backup is already in progress, ABORT it first\r\n"
9739        );
9740        assert_eq!(
9741            b.run(&[b"BACKUP", b"CLEANUP"]),
9742            "-ERR Backup is in progress\r\n"
9743        );
9744
9745        b.run(&[b"BACKUP", b"SEAL"]);
9746        assert_eq!(
9747            b.run(&[b"BACKUP", b"START"]),
9748            "-ERR A sealed backup exists, CLEANUP it first\r\n"
9749        );
9750        assert_eq!(
9751            b.run(&[b"BACKUP", b"SEAL"]),
9752            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
9753        );
9754        assert_eq!(
9755            b.run(&[b"BACKUP", b"ABORT"]),
9756            "-ERR No backup in progress\r\n"
9757        );
9758    }
9759
9760    /// An abort takes the base file away and leaves a state saying who did it.
9761    ///
9762    /// The next backup takes the next sequence number rather than reusing the
9763    /// one whose files were just thrown away, so a directory somebody copied a
9764    /// half finished backup out of cannot end up with two different files under
9765    /// one name.
9766    #[test]
9767    fn backup_abort_removes_the_file_and_says_who_did_it() {
9768        let mut b = Backups::new("abort");
9769        b.run(&[b"BACKUP", b"START"]);
9770        assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
9771
9772        let status = b.run(&[b"BACKUP", b"STATUS"]);
9773        assert!(status.contains("failed"), "{status}");
9774        assert!(status.contains("aborted by user"), "{status}");
9775        assert!(b.files().is_empty(), "abort left the base file behind");
9776        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
9777
9778        // A start from failed works, and is the second backup.
9779        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
9780        assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
9781        let status = b.run(&[b"BACKUP", b"STATUS"]);
9782        assert!(status.contains("incrementing"), "{status}");
9783        assert!(!status.contains("aborted"), "the old error was kept");
9784    }
9785
9786    /// `LIST` names nothing, then one file, then three, and they are absolute.
9787    #[test]
9788    fn backup_list_names_the_files_that_are_pinned_so_far() {
9789        let mut b = Backups::new("list");
9790        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
9791
9792        b.run(&[b"BACKUP", b"START"]);
9793        let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
9794        let base = base.to_string_lossy().into_owned();
9795        assert_eq!(
9796            b.run(&[b"BACKUP", b"LIST"]),
9797            format!("*1\r\n${}\r\n{base}\r\n", base.len())
9798        );
9799
9800        b.run(&[b"BACKUP", b"SEAL"]);
9801        let listed = b.run(&[b"BACKUP", b"LIST"]);
9802        assert!(listed.starts_with("*3\r\n"), "{listed}");
9803        // The order is the manifest's order, base then incremental then the
9804        // manifest itself, which is the order a restore needs them in.
9805        let names: Vec<&str> = listed
9806            .lines()
9807            .filter(|l| l.starts_with('/') || l.contains(":\\"))
9808            .collect();
9809        assert_eq!(names.len(), 3, "{listed}");
9810        assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
9811        assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
9812        assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
9813    }
9814
9815    /// The base file is the dataset as it was at `START` and not at `SEAL`.
9816    ///
9817    /// That is D-46 and it is the one thing about this a client can notice, so
9818    /// it is pinned here rather than left to be discovered by whoever restores
9819    /// one. The incremental file is empty for the same reason: there is no
9820    /// append only log underneath this server to copy the writes in between out
9821    /// of.
9822    #[test]
9823    fn a_backup_holds_the_dataset_as_it_was_at_start() {
9824        let mut b = Backups::new("contents");
9825        b.run(&[b"SET", b"bk", b"v1"]);
9826        b.run(&[b"BACKUP", b"START"]);
9827        b.run(&[b"SET", b"bk", b"v2"]);
9828        b.run(&[b"BACKUP", b"SEAL"]);
9829
9830        let base = b.read("appendonly.aof.1.base.rdb");
9831        assert!(base.starts_with(b"REDIS"), "not an RDB file");
9832        assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
9833        assert!(
9834            !base.windows(2).any(|w| w == b"v2"),
9835            "the base file moved on after START"
9836        );
9837        // The aux field a loader acts on, and the one that says this file is
9838        // the base of an append only file rather than a standalone dump. Its
9839        // value is the one byte string 1, which the encoder writes as an
9840        // integer the way a real server writes it.
9841        let at = base
9842            .windows(8)
9843            .position(|w| w == b"aof-base")
9844            .expect("no aof-base aux field");
9845        assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
9846
9847        assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
9848        assert_eq!(
9849            String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
9850            "file appendonly.aof.1.base.rdb seq 1 type b\n\
9851             file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
9852        );
9853    }
9854
9855    /// `STATUS` is a map of four pairs on RESP3 and the same pairs flat on
9856    /// RESP2, which is what every other map shaped reply in this server does.
9857    #[test]
9858    fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
9859        let mut b = Backups::new("status");
9860        b.f.server.set_clock_ms(1_700_000_000_000);
9861
9862        assert_eq!(
9863            b.run(&[b"BACKUP", b"STATUS"]),
9864            "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
9865             $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
9866        );
9867
9868        b.f.out = Out::new(Proto::Resp3);
9869        b.run(&[b"BACKUP", b"START"]);
9870        assert_eq!(
9871            b.run(&[b"BACKUP", b"STATUS"]),
9872            "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
9873             $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
9874        );
9875
9876        b.run(&[b"BACKUP", b"SEAL"]);
9877        let sealed = b.run(&[b"BACKUP", b"STATUS"]);
9878        assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
9879    }
9880
9881    /// A sealed backup that nobody cleans up goes away on its own once
9882    /// `backup-sealed-ttl` seconds have passed since the seal.
9883    #[test]
9884    fn a_sealed_backup_is_swept_away_after_the_timeout() {
9885        let mut b = Backups::new("ttl");
9886        b.f.server.set_clock_ms(1_000_000);
9887        assert_eq!(
9888            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
9889            "+OK\r\n"
9890        );
9891        b.run(&[b"BACKUP", b"START"]);
9892        b.run(&[b"BACKUP", b"SEAL"]);
9893
9894        // A minute short of the deadline, nothing happens.
9895        b.f.server.set_clock_ms(1_000_000 + 59_000);
9896        b.f.server.backup_expire();
9897        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
9898        assert_eq!(b.files().len(), 3);
9899
9900        b.f.server.set_clock_ms(1_000_000 + 60_000);
9901        b.f.server.backup_expire();
9902        let status = b.run(&[b"BACKUP", b"STATUS"]);
9903        assert!(status.contains("idle"), "{status}");
9904        assert!(b.files().is_empty(), "the timeout left the files behind");
9905
9906        // Zero is the default and means a sealed backup is kept for ever.
9907        b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
9908        b.run(&[b"BACKUP", b"START"]);
9909        b.run(&[b"BACKUP", b"SEAL"]);
9910        b.f.server.set_clock_ms(9_000_000_000);
9911        b.f.server.backup_expire();
9912        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
9913    }
9914
9915    /// The three settings around the command, read and written the way 8.10.1
9916    /// reads and writes them.
9917    #[test]
9918    fn the_backup_settings_behave_the_way_the_reference_does() {
9919        let mut b = Backups::new("config");
9920        let dir = b.dir.to_string_lossy().into_owned();
9921
9922        assert_eq!(
9923            b.run(&[b"CONFIG", b"GET", b"dir"]),
9924            format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
9925        );
9926        assert_eq!(
9927            b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
9928            "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
9929        );
9930        assert_eq!(
9931            b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
9932            "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
9933        );
9934
9935        // `dir` is a protected config, so it is refused even for the value it
9936        // already holds, and `backupdirname` is immutable.
9937        assert_eq!(
9938            b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
9939            "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
9940        );
9941        assert_eq!(
9942            b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
9943            "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
9944        );
9945        assert!(
9946            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
9947                .contains("argument couldn't be parsed into an integer")
9948        );
9949        assert!(
9950            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
9951                .contains("argument must be between 0 and 9223372036854775807 inclusive")
9952        );
9953    }
9954
9955    /// The help text, which has `HELP` in it twice because the reference's does.
9956    #[test]
9957    fn backup_help_is_the_text_the_reference_sends() {
9958        let mut f = Fixture::new();
9959        let help = f.run(&[b"BACKUP", b"HELP"]);
9960        assert!(help.starts_with("*17\r\n"), "{help}");
9961        assert!(
9962            help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
9963        );
9964        assert!(help.contains("+    Start a new backup into the configured 'backupdirname'.\r\n"));
9965        assert!(help.contains("+    Freeze the current backup (BASE + INCR + manifest).\r\n"));
9966        assert!(help.contains("+    Return this help.\r\n+HELP\r\n+    Print this help.\r\n"));
9967    }
9968
9969    /// What a mistyped `BACKUP` gets told.
9970    ///
9971    /// The arity error names `backup` where the reference names `backup|start`,
9972    /// which is D-46: the table reports one arity for the container the way the
9973    /// reference does, and the per subcommand table that would carry the better
9974    /// name is not built yet. Every subcommand is exactly two words, so nothing
9975    /// legal is refused by it.
9976    #[test]
9977    fn backup_refuses_what_it_cannot_read() {
9978        let mut f = Fixture::new();
9979        assert_eq!(
9980            f.run(&[b"BACKUP"]),
9981            "-ERR wrong number of arguments for 'backup' command\r\n"
9982        );
9983        assert_eq!(
9984            f.run(&[b"BACKUP", b"START", b"x"]),
9985            "-ERR wrong number of arguments for 'backup' command\r\n"
9986        );
9987        assert_eq!(
9988            f.run(&[b"BACKUP", b"NOPE"]),
9989            "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
9990        );
9991    }
9992
9993    /// A fixture whose server saves into a directory of its own.
9994    ///
9995    /// The same shape and the same reason as [`Backups`]: these tests write real
9996    /// files, because a save that only moved a counter would pass a test suite
9997    /// and hand somebody an empty file.
9998    struct Saves {
9999        f: Fixture,
10000        dir: PathBuf,
10001    }
10002
10003    impl Saves {
10004        fn new(name: &str) -> Saves {
10005            let dir = std::env::temp_dir().join(format!("yo-save-{name}-{}", std::process::id()));
10006            let _ = std::fs::remove_dir_all(&dir);
10007            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
10008            let mut f = Fixture::new();
10009            f.server.set_dir(dir.clone());
10010            Saves { f, dir }
10011        }
10012
10013        fn run(&mut self, parts: &[&[u8]]) -> String {
10014            self.f.run(parts)
10015        }
10016
10017        /// The names in the directory, sorted.
10018        fn files(&self) -> Vec<String> {
10019            let mut names: Vec<String> = match std::fs::read_dir(&self.dir) {
10020                Ok(entries) => entries
10021                    .filter_map(|e| e.ok())
10022                    .map(|e| e.file_name().to_string_lossy().into_owned())
10023                    .collect(),
10024                Err(_) => Vec::new(),
10025            };
10026            names.sort();
10027            names
10028        }
10029
10030        fn image(&self) -> Vec<u8> {
10031            std::fs::read(self.dir.join("dump.rdb")).expect("could not read the file")
10032        }
10033
10034        /// One field out of `INFO persistence`.
10035        fn field(&mut self, name: &str) -> String {
10036            let text = self.run(&[b"INFO", b"persistence"]);
10037            let head = format!("\r\n{name}:");
10038            let at = text.find(&head).expect("the field is not in the section");
10039            let rest = &text[at + head.len()..];
10040            rest[..rest.find("\r\n").expect("the field has no end")].to_owned()
10041        }
10042    }
10043
10044    impl Drop for Saves {
10045        fn drop(&mut self) {
10046            let _ = std::fs::remove_dir_all(&self.dir);
10047        }
10048    }
10049
10050    #[test]
10051    fn save_writes_a_file_that_carries_the_dataset() {
10052        let mut s = Saves::new("writes");
10053        s.run(&[b"SET", b"k", b"v"]);
10054        s.run(&[b"RPUSH", b"l", b"a", b"b"]);
10055        assert!(
10056            s.files().is_empty(),
10057            "a server has saved without being asked"
10058        );
10059
10060        assert_eq!(s.run(&[b"SAVE"]), "+OK\r\n");
10061        assert_eq!(s.files(), ["dump.rdb"]);
10062
10063        // The header, the two databases the keys are in and the end marker,
10064        // which is as far as this test goes: what is between them is the
10065        // snapshot writer's own test, and a real server starting on one of
10066        // these files is what the harness checks.
10067        let image = s.image();
10068        assert!(
10069            image.starts_with(b"REDIS00"),
10070            "the header is not an RDB one"
10071        );
10072        assert!(
10073            image.windows(1).any(|w| w == [0xFF]),
10074            "there is no end marker"
10075        );
10076        assert!(image.len() > 40, "the file is too small to hold anything");
10077    }
10078
10079    #[test]
10080    fn a_save_leaves_no_temporary_file_behind() {
10081        let mut s = Saves::new("temp");
10082        s.run(&[b"SET", b"k", b"v"]);
10083        s.run(&[b"SAVE"]);
10084        s.run(&[b"BGSAVE"]);
10085        assert_eq!(s.files(), ["dump.rdb"]);
10086    }
10087
10088    #[test]
10089    fn a_save_that_cannot_write_says_so_in_one_word() {
10090        let mut s = Saves::new("nowhere");
10091        // A directory that is not there, which is the failure a real server
10092        // answers `-ERR` to with nothing after it.
10093        s.f.server.set_dir(s.dir.join("gone"));
10094        assert_eq!(s.run(&[b"SAVE"]), "-ERR\r\n");
10095        assert_eq!(s.field("rdb_last_bgsave_status"), "err");
10096        // And the count of attempts moved, because the attempt happened.
10097        assert_eq!(s.field("rdb_saves"), "1");
10098    }
10099
10100    #[test]
10101    fn lastsave_starts_at_the_time_the_server_did_and_moves_on_a_save() {
10102        let mut s = Saves::new("lastsave");
10103        let started = s.run(&[b"LASTSAVE"]);
10104        assert_eq!(started, format!(":{}\r\n", s.f.server.started_ms / 1_000));
10105
10106        s.f.server.set_clock_ms(s.f.server.started_ms + 5_000);
10107        s.run(&[b"SAVE"]);
10108        let after = s.run(&[b"LASTSAVE"]);
10109        assert_eq!(after, format!(":{}\r\n", s.f.server.started_ms / 1_000 + 5));
10110
10111        // A write does not move it. Only a save does.
10112        s.run(&[b"SET", b"k", b"v"]);
10113        assert_eq!(s.run(&[b"LASTSAVE"]), after);
10114    }
10115
10116    #[test]
10117    fn bgsave_takes_the_one_word_it_takes_and_nothing_else() {
10118        let mut s = Saves::new("bgsave");
10119        for parts in [
10120            &[b"BGSAVE".as_slice()][..],
10121            &[b"BGSAVE", b"SCHEDULE"],
10122            &[b"BGSAVE", b"schedule"],
10123        ] {
10124            assert_eq!(s.run(parts), "+Background saving started\r\n");
10125        }
10126        for parts in [
10127            &[b"BGSAVE".as_slice(), b"x"][..],
10128            &[b"BGSAVE", b"SCHEDULE", b"x"],
10129            &[b"BGSAVE", b"SCHEDULE", b"SCHEDULE"],
10130        ] {
10131            assert_eq!(s.run(parts), "-ERR syntax error\r\n");
10132        }
10133    }
10134
10135    #[test]
10136    fn a_save_inside_a_transaction_says_it_was_scheduled() {
10137        let mut s = Saves::new("queued");
10138        // `SAVE` never gets there, because it carries `no_multi`.
10139        assert_eq!(s.run(&[b"MULTI"]), "+OK\r\n");
10140        assert_eq!(
10141            s.run(&[b"SAVE"]),
10142            "-ERR Command not allowed inside a transaction\r\n"
10143        );
10144        assert_eq!(
10145            s.run(&[b"EXEC"]),
10146            "-EXECABORT Transaction discarded because of previous errors.\r\n"
10147        );
10148
10149        assert_eq!(s.run(&[b"MULTI"]), "+OK\r\n");
10150        assert_eq!(s.run(&[b"BGSAVE"]), "+QUEUED\r\n");
10151        assert_eq!(s.run(&[b"BGREWRITEAOF"]), "+QUEUED\r\n");
10152        assert_eq!(
10153            s.run(&[b"EXEC"]),
10154            "*2\r\n+Background saving scheduled\r\n\
10155             +Background append only file rewriting scheduled\r\n"
10156        );
10157        // And the file is there, which is the half of it that is not the words.
10158        assert_eq!(s.files(), ["dump.rdb"]);
10159    }
10160
10161    #[test]
10162    fn a_rewrite_counts_itself_and_writes_nothing() {
10163        let mut s = Saves::new("rewrite");
10164        assert_eq!(
10165            s.run(&[b"BGREWRITEAOF"]),
10166            "+Background append only file rewriting started\r\n"
10167        );
10168        assert_eq!(s.field("aof_rewrites"), "1");
10169        assert_eq!(s.field("aof_enabled"), "0");
10170        assert!(s.files().is_empty(), "a rewrite has written a file");
10171    }
10172
10173    #[test]
10174    fn role_says_master_with_nothing_following_it() {
10175        let mut f = Fixture::new();
10176        assert_eq!(f.run(&[b"ROLE"]), "*3\r\n$6\r\nmaster\r\n:0\r\n*0\r\n");
10177        assert_eq!(
10178            f.run(&[b"ROLE", b"x"]),
10179            "-ERR wrong number of arguments for 'role' command\r\n"
10180        );
10181    }
10182
10183    #[test]
10184    fn the_persistence_section_counts_the_saves_that_were_asked_for() {
10185        let mut s = Saves::new("counts");
10186        assert_eq!(s.field("rdb_saves"), "0");
10187        assert_eq!(s.field("rdb_last_bgsave_status"), "ok");
10188        s.run(&[b"SAVE"]);
10189        s.run(&[b"BGSAVE"]);
10190        s.run(&[b"BGSAVE", b"SCHEDULE"]);
10191        assert_eq!(s.field("rdb_saves"), "3");
10192        assert_eq!(s.field("rdb_bgsave_in_progress"), "0");
10193        assert_eq!(s.field("loading"), "0");
10194    }
10195
10196    #[test]
10197    fn the_persistence_section_is_in_a_bare_info_and_not_in_another_one() {
10198        let mut f = Fixture::new();
10199        assert!(f.run(&[b"INFO"]).contains("# Persistence"));
10200        assert!(f.run(&[b"INFO", b"persistence"]).contains("# Persistence"));
10201        assert!(f.run(&[b"INFO", b"all"]).contains("# Persistence"));
10202        assert!(!f.run(&[b"INFO", b"clients"]).contains("# Persistence"));
10203    }
10204
10205    #[test]
10206    fn the_file_name_reads_back_and_cannot_be_written() {
10207        let mut f = Fixture::new();
10208        assert_eq!(
10209            f.run(&[b"CONFIG", b"GET", b"dbfilename"]),
10210            "*2\r\n$10\r\ndbfilename\r\n$8\r\ndump.rdb\r\n"
10211        );
10212        assert_eq!(
10213            f.run(&[b"CONFIG", b"SET", b"dbfilename", b"other.rdb"]),
10214            "-ERR CONFIG SET failed (possibly related to argument 'dbfilename') - can't set protected config\r\n"
10215        );
10216        // Refused even when it is set to what it already is, which is what
10217        // being protected means and is not what being immutable means.
10218        assert_eq!(
10219            f.run(&[b"CONFIG", b"SET", b"dbfilename", b"dump.rdb"]),
10220            "-ERR CONFIG SET failed (possibly related to argument 'dbfilename') - can't set protected config\r\n"
10221        );
10222    }
10223
10224    #[test]
10225    fn shutdown_save_writes_the_file_and_shutdown_on_its_own_does_not() {
10226        let mut s = Saves::new("shutdown");
10227        s.run(&[b"SET", b"k", b"v"]);
10228        s.run(&[b"SHUTDOWN", b"NOSAVE"]);
10229        assert!(s.files().is_empty(), "a nosave shutdown wrote a file");
10230
10231        let mut s = Saves::new("shutdown-save");
10232        s.run(&[b"SET", b"k", b"v"]);
10233        s.run(&[b"SHUTDOWN", b"SAVE"]);
10234        assert_eq!(s.files(), ["dump.rdb"]);
10235    }
10236
10237    /// Every type survives the trip out to the file and back.
10238    ///
10239    /// This is the test the Redis suite is really running when it calls `DEBUG
10240    /// RELOAD` after a case: not that the command answers, but that what was in
10241    /// memory before it is what is in memory after it.
10242    #[test]
10243    fn debug_reload_brings_every_type_back_the_way_it_went_in() {
10244        let mut s = Saves::new("reload-types");
10245        s.run(&[b"SET", b"str", b"hello"]);
10246        s.run(&[b"SET", b"num", b"1234"]);
10247        s.run(&[b"RPUSH", b"list", b"a", b"b", b"c"]);
10248        s.run(&[b"SADD", b"set", b"x", b"y"]);
10249        s.run(&[b"SADD", b"ints", b"1", b"2", b"3"]);
10250        s.run(&[b"HSET", b"hash", b"f", b"v", b"g", b"w"]);
10251        s.run(&[b"ZADD", b"zset", b"1.5", b"m", b"2", b"n"]);
10252        s.run(&[b"XADD", b"stream", b"1-1", b"f", b"v"]);
10253        s.run(&[b"PEXPIREAT", b"str", b"4102444800000"]);
10254        let before = s.run(&[b"DBSIZE"]);
10255
10256        assert_eq!(s.run(&[b"DEBUG", b"RELOAD"]), "+OK\r\n");
10257
10258        assert_eq!(s.run(&[b"DBSIZE"]), before);
10259        assert_eq!(s.run(&[b"GET", b"str"]), "$5\r\nhello\r\n");
10260        assert_eq!(s.run(&[b"GET", b"num"]), "$4\r\n1234\r\n");
10261        assert_eq!(
10262            s.run(&[b"LRANGE", b"list", b"0", b"-1"]),
10263            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
10264        );
10265        assert_eq!(s.run(&[b"SCARD", b"set"]), ":2\r\n");
10266        assert_eq!(s.run(&[b"SISMEMBER", b"set", b"y"]), ":1\r\n");
10267        assert_eq!(s.run(&[b"SCARD", b"ints"]), ":3\r\n");
10268        assert_eq!(s.run(&[b"HGET", b"hash", b"g"]), "$1\r\nw\r\n");
10269        assert_eq!(s.run(&[b"ZSCORE", b"zset", b"m"]), "$3\r\n1.5\r\n");
10270        assert_eq!(s.run(&[b"XLEN", b"stream"]), ":1\r\n");
10271        // The deadline travels with the key, and it is the same deadline and not
10272        // one worked out again from a remaining time.
10273        assert_eq!(s.run(&[b"PEXPIRETIME", b"str"]), ":4102444800000\r\n");
10274        assert_eq!(s.run(&[b"PEXPIRETIME", b"num"]), ":-1\r\n");
10275    }
10276
10277    /// A key goes back into the database it came out of.
10278    #[test]
10279    fn debug_reload_puts_every_key_back_in_its_own_database() {
10280        let mut s = Saves::new("reload-dbs");
10281        s.run(&[b"SET", b"home", b"zero"]);
10282        s.run(&[b"SELECT", b"9"]);
10283        s.run(&[b"SET", b"away", b"nine"]);
10284        s.run(&[b"SELECT", b"0"]);
10285
10286        assert_eq!(s.run(&[b"DEBUG", b"RELOAD"]), "+OK\r\n");
10287
10288        assert_eq!(s.run(&[b"GET", b"home"]), "$4\r\nzero\r\n");
10289        assert_eq!(s.run(&[b"EXISTS", b"away"]), ":0\r\n");
10290        s.run(&[b"SELECT", b"9"]);
10291        assert_eq!(s.run(&[b"GET", b"away"]), "$4\r\nnine\r\n");
10292        assert_eq!(s.run(&[b"EXISTS", b"home"]), ":0\r\n");
10293    }
10294
10295    /// `NOSAVE` reads the file that is there rather than writing a new one.
10296    #[test]
10297    fn debug_reload_nosave_reads_the_file_that_is_already_there() {
10298        let mut s = Saves::new("reload-nosave");
10299        s.run(&[b"SET", b"k", b"first"]);
10300        s.run(&[b"SAVE"]);
10301        s.run(&[b"SET", b"k", b"second"]);
10302        s.run(&[b"SET", b"later", b"x"]);
10303
10304        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE"]), "+OK\r\n");
10305
10306        // Both changes are gone, because the file knows nothing about either.
10307        assert_eq!(s.run(&[b"GET", b"k"]), "$5\r\nfirst\r\n");
10308        assert_eq!(s.run(&[b"EXISTS", b"later"]), ":0\r\n");
10309    }
10310
10311    /// `NOFLUSH` lets the file land on what is already in memory.
10312    #[test]
10313    fn debug_reload_noflush_keeps_what_the_file_does_not_mention() {
10314        let mut s = Saves::new("reload-noflush");
10315        s.run(&[b"SET", b"k", b"first"]);
10316        s.run(&[b"SAVE"]);
10317        s.run(&[b"SET", b"k", b"second"]);
10318        s.run(&[b"SET", b"later", b"x"]);
10319
10320        assert_eq!(
10321            s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE", b"NOFLUSH"]),
10322            "+OK\r\n"
10323        );
10324
10325        // The file wins where the two disagree and memory keeps the rest, which
10326        // is what `MERGE` buys on a real server and is what happens here whether
10327        // the word was sent or not.
10328        assert_eq!(s.run(&[b"GET", b"k"]), "$5\r\nfirst\r\n");
10329        assert_eq!(s.run(&[b"GET", b"later"]), "$1\r\nx\r\n");
10330    }
10331
10332    /// The three words it takes, in any case, and one sentence for anything else.
10333    #[test]
10334    fn debug_reload_takes_its_three_words_and_no_others() {
10335        let mut s = Saves::new("reload-words");
10336        s.run(&[b"SET", b"k", b"v"]);
10337        for parts in [
10338            &[b"DEBUG".as_slice(), b"RELOAD"][..],
10339            &[b"DEBUG", b"RELOAD", b"NOSAVE"],
10340            &[b"DEBUG", b"RELOAD", b"nosave"],
10341            &[b"DEBUG", b"RELOAD", b"MERGE"],
10342            &[b"DEBUG", b"RELOAD", b"NOFLUSH"],
10343            &[b"DEBUG", b"RELOAD", b"MERGE", b"NOFLUSH", b"NOSAVE"],
10344            // Repeated is not an error on a real server either.
10345            &[b"DEBUG", b"RELOAD", b"NOSAVE", b"NOSAVE"],
10346        ] {
10347            assert_eq!(s.run(parts), "+OK\r\n", "{parts:?}");
10348        }
10349        for parts in [
10350            &[b"DEBUG".as_slice(), b"RELOAD", b"BOGUS"][..],
10351            &[b"DEBUG", b"RELOAD", b"NOSAVE", b"BOGUS"],
10352            &[b"DEBUG", b"RELOAD", b""],
10353        ] {
10354            assert_eq!(
10355                s.run(parts),
10356                "-ERR DEBUG RELOAD only supports the MERGE, NOFLUSH and NOSAVE options.\r\n",
10357                "{parts:?}"
10358            );
10359        }
10360        // And the dataset is still there after all of that.
10361        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
10362    }
10363
10364    /// A reload that cannot write its file says what a save says.
10365    #[test]
10366    fn debug_reload_that_cannot_write_the_file_says_so_in_one_word() {
10367        let mut s = Saves::new("reload-nowhere");
10368        s.run(&[b"SET", b"k", b"v"]);
10369        s.f.server.set_dir(s.dir.join("gone"));
10370        assert_eq!(s.run(&[b"DEBUG", b"RELOAD"]), "-ERR\r\n");
10371        // Nothing was thrown away, because nothing was read.
10372        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
10373    }
10374
10375    /// A reload that cannot read its file says to look in the log.
10376    ///
10377    /// Two ways to get there, a file that is not there and a file that is not
10378    /// one, and the reply is the same sentence for both because a client can do
10379    /// nothing with the difference.
10380    #[test]
10381    fn debug_reload_that_cannot_read_the_file_says_to_check_the_log() {
10382        let mut s = Saves::new("reload-unreadable");
10383        s.run(&[b"SET", b"k", b"v"]);
10384        let failed = "-ERR Error trying to load the RDB dump, check server logs.\r\n";
10385        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE"]), failed);
10386        // Refused before the flush, so the dataset is still here.
10387        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
10388
10389        s.run(&[b"SAVE"]);
10390        std::fs::write(s.dir.join("dump.rdb"), b"not an RDB file at all")
10391            .expect("could not write over the file");
10392        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE"]), failed);
10393        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
10394    }
10395
10396    /// A reload says what it would lose rather than losing it.
10397    ///
10398    /// A time series has no RDB type byte, so it is not in the file the save
10399    /// wrote, and flushing would make the round trip a delete. `NOFLUSH` is the
10400    /// way through: everything in the file lands on top of what is there and the
10401    /// key that could not be written stays where it is.
10402    #[test]
10403    fn debug_reload_refuses_to_drop_a_key_with_no_rdb_form() {
10404        let mut s = Saves::new("reload-foreign");
10405        s.run(&[b"SET", b"k", b"v"]);
10406        s.run(&[b"TS.CREATE", b"ts"]);
10407        s.run(&[b"TS.ADD", b"ts", b"1000", b"1.5"]);
10408
10409        assert_eq!(
10410            s.run(&[b"DEBUG", b"RELOAD"]),
10411            "-ERR DEBUG RELOAD would drop 1 key with no RDB form, use NOFLUSH to keep it\r\n"
10412        );
10413        assert_eq!(s.run(&[b"EXISTS", b"ts"]), ":1\r\n");
10414
10415        s.run(&[b"TS.CREATE", b"ts2"]);
10416        assert_eq!(
10417            s.run(&[b"DEBUG", b"RELOAD"]),
10418            "-ERR DEBUG RELOAD would drop 2 keys with no RDB form, use NOFLUSH to keep them\r\n"
10419        );
10420
10421        // And the way through keeps everything.
10422        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOFLUSH"]), "+OK\r\n");
10423        assert_eq!(s.run(&[b"EXISTS", b"ts"]), ":1\r\n");
10424        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
10425        assert_eq!(s.run(&[b"TS.GET", b"ts"]), "*2\r\n:1000\r\n+1.5\r\n");
10426    }
10427
10428    /// A key that died while the file was on disk does not come back.
10429    #[test]
10430    fn debug_reload_drops_a_key_whose_deadline_went_by() {
10431        let mut s = Saves::new("reload-expired");
10432        s.run(&[b"SET", b"gone", b"v"]);
10433        s.run(&[b"SET", b"stays", b"v"]);
10434        s.run(&[b"PEXPIREAT", b"gone", b"4102444800000"]);
10435        s.run(&[b"SAVE"]);
10436        s.f.server.set_clock_ms(4_102_444_800_001);
10437
10438        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE"]), "+OK\r\n");
10439
10440        assert_eq!(s.run(&[b"EXISTS", b"gone"]), ":0\r\n");
10441        assert_eq!(s.run(&[b"GET", b"stays"]), "$1\r\nv\r\n");
10442    }
10443
10444    /// The other caller of the same walk, which is `yodb serve --restore` and
10445    /// `yodb restore`: a file one server wrote, read into a server that has never
10446    /// seen it.
10447    ///
10448    /// The interesting half is that the second server is a different one. A
10449    /// reload reads a file its own writer produced a moment ago into a keyspace
10450    /// whose thresholds have not moved, and a restore does not, so this is the
10451    /// shape the migration story actually has.
10452    #[test]
10453    fn a_file_one_server_wrote_loads_into_a_server_that_has_never_seen_it() {
10454        let mut wrote = Saves::new("restore-across");
10455        wrote.run(&[b"SET", b"s", b"hello"]);
10456        wrote.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
10457        wrote.run(&[b"HSET", b"h", b"f", b"v"]);
10458        wrote.run(&[b"ZADD", b"z", b"1.5", b"m"]);
10459        wrote.run(&[b"SELECT", b"7"]);
10460        wrote.run(&[b"SADD", b"far", b"x"]);
10461        assert_eq!(wrote.run(&[b"SAVE"]), "+OK\r\n");
10462
10463        let mut fresh = Fixture::new();
10464        let done = fresh
10465            .server
10466            .load_image(&wrote.image(), true)
10467            .expect("the file one server wrote is a file another can read");
10468        assert_eq!(done.keys[0], 4);
10469        assert_eq!(done.keys[7], 1);
10470        assert_eq!(done.total(), 5);
10471        assert_eq!(done.expired, 0);
10472
10473        assert_eq!(fresh.run(&[b"GET", b"s"]), "$5\r\nhello\r\n");
10474        assert_eq!(
10475            fresh.run(&[b"LRANGE", b"l", b"0", b"-1"]),
10476            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
10477        );
10478        assert_eq!(fresh.run(&[b"HGET", b"h", b"f"]), "$1\r\nv\r\n");
10479        assert_eq!(fresh.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n1.5\r\n");
10480        fresh.run(&[b"SELECT", b"7"]);
10481        assert_eq!(fresh.run(&[b"SMEMBERS", b"far"]), "*1\r\n$1\r\nx\r\n");
10482    }
10483
10484    /// A load says how much of the file landed and how much of it was too old to
10485    /// keep, and `INFO persistence` says the same two numbers afterwards.
10486    #[test]
10487    fn a_load_reports_what_it_kept_and_what_had_already_died() {
10488        let mut wrote = Saves::new("restore-counts");
10489        wrote.run(&[b"SET", b"gone", b"v"]);
10490        wrote.run(&[b"SET", b"stays", b"v"]);
10491        wrote.run(&[b"PEXPIREAT", b"gone", b"4102444800000"]);
10492        assert_eq!(wrote.run(&[b"SAVE"]), "+OK\r\n");
10493
10494        let mut fresh = Saves::new("restore-counts-into");
10495        fresh.f.server.set_clock_ms(4_102_444_800_001);
10496        let done = fresh
10497            .f
10498            .server
10499            .load_image(&wrote.image(), true)
10500            .expect("a file with a dead key in it is still a good file");
10501        assert_eq!(done.total(), 1);
10502        assert_eq!(done.expired, 1);
10503
10504        assert_eq!(fresh.field("rdb_last_load_keys_loaded"), "1");
10505        assert_eq!(fresh.field("rdb_last_load_keys_expired"), "1");
10506        assert_eq!(fresh.run(&[b"EXISTS", b"gone"]), ":0\r\n");
10507        assert_eq!(fresh.run(&[b"GET", b"stays"]), "$1\r\nv\r\n");
10508    }
10509
10510    /// A server that has not loaded anything reports nought for both, which is
10511    /// true rather than a placeholder.
10512    #[test]
10513    fn a_server_that_has_loaded_nothing_says_so() {
10514        let mut s = Saves::new("restore-never");
10515        assert_eq!(s.field("rdb_last_load_keys_loaded"), "0");
10516        assert_eq!(s.field("rdb_last_load_keys_expired"), "0");
10517    }
10518
10519    /// Bytes that are not an RDB at all leave the keyspace exactly as it was.
10520    ///
10521    /// The magic is checked before anything is thrown away, which is the whole
10522    /// reason a restore is safe to point at the wrong file.
10523    #[test]
10524    fn a_file_that_is_not_an_rdb_is_refused_with_the_dataset_still_there() {
10525        let mut f = Fixture::new();
10526        f.run(&[b"SET", b"k", b"v"]);
10527        let refused = f
10528            .server
10529            .load_image(b"this is not a Redis dump at all, not even close", true)
10530            .expect_err("that is not an RDB");
10531        assert_eq!(refused.to_string(), "the file does not start with REDIS");
10532        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
10533    }
10534
10535    /// A file whose last eight bytes do not add up is refused too, and for the
10536    /// same reason it is safe: the checksum is over the whole file and is read
10537    /// before the first key comes out.
10538    #[test]
10539    fn a_damaged_file_is_refused_with_the_dataset_still_there() {
10540        let mut wrote = Saves::new("restore-damaged");
10541        wrote.run(&[b"SET", b"a", b"b"]);
10542        assert_eq!(wrote.run(&[b"SAVE"]), "+OK\r\n");
10543        let mut image = wrote.image();
10544        // One byte in the middle, so that the frame still parses and only the
10545        // checksum knows. Flipping the footer would be a different test.
10546        let middle = image.len() / 2;
10547        image[middle] ^= 0xff;
10548
10549        let mut f = Fixture::new();
10550        f.run(&[b"SET", b"k", b"v"]);
10551        let refused = f
10552            .server
10553            .load_image(&image, true)
10554            .expect_err("the checksum does not match");
10555        assert_eq!(
10556            refused.to_string(),
10557            "the checksum does not match, so the file is damaged"
10558        );
10559        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
10560    }
10561
10562    /// The fields of one `DEBUG OBJECT` line, read off a string.
10563    ///
10564    /// Every number in it is checked somewhere and this is the one that checks
10565    /// the shape: the field order, the spacing and the two that are constant.
10566    #[test]
10567    fn debug_object_describes_how_a_value_is_written_down() {
10568        let mut f = Fixture::new();
10569        f.run(&[b"SET", b"s", b"hello"]);
10570
10571        let line = f.run(&[b"DEBUG", b"OBJECT", b"s"]);
10572        let line = line
10573            .strip_prefix('+')
10574            .and_then(|l| l.strip_suffix("\r\n"))
10575            .expect("a simple string");
10576        let mut fields = line.split(' ');
10577        assert_eq!(fields.next(), Some("Value"));
10578        assert!(
10579            fields.next().expect("an address").starts_with("at:0x"),
10580            "{line}"
10581        );
10582        assert_eq!(fields.next(), Some("refcount:1"));
10583        assert_eq!(fields.next(), Some("encoding:embstr"));
10584        // Five bytes of hello and the one byte header a short string is
10585        // written with, which is the body and neither the type byte in front
10586        // of it nor the footer behind.
10587        assert_eq!(fields.next(), Some("serializedlength:6"));
10588        assert!(
10589            fields.next().expect("a clock").starts_with("lru:"),
10590            "{line}"
10591        );
10592        assert_eq!(fields.next(), Some("lru_seconds_idle:0"));
10593        assert_eq!(fields.next(), None);
10594    }
10595
10596    /// The five extra fields a list that broke into nodes carries.
10597    #[test]
10598    fn debug_object_counts_the_nodes_a_list_broke_into() {
10599        let mut f = Fixture::new();
10600        // Enough long members to be past the eight kilobyte band, so that the
10601        // list is a quicklist rather than one packed run.
10602        let member = vec![b'x'; 200];
10603        for _ in 0..100 {
10604            f.run(&[b"RPUSH", b"l", &member]);
10605        }
10606        assert_eq!(
10607            f.run(&[b"OBJECT", b"ENCODING", b"l"]),
10608            "$9\r\nquicklist\r\n"
10609        );
10610
10611        let line = f.run(&[b"DEBUG", b"OBJECT", b"l"]);
10612        let nodes: usize = field(&line, "ql_nodes:").parse().expect("a count");
10613        assert!(nodes > 1, "{line}");
10614        let avg: f64 = field(&line, "ql_avg_node:").parse().expect("an average");
10615        assert!((avg - 100.0 / nodes as f64).abs() < 0.01, "{line}");
10616        assert_eq!(field(&line, "ql_listpack_max:"), "-2");
10617        assert_eq!(field(&line, "ql_compressed:"), "0");
10618        let bytes: usize = field(&line, "ql_uncompressed_size:")
10619            .parse()
10620            .expect("a size");
10621        assert!(bytes > 100 * 200, "{line}");
10622
10623        // A list small enough to stay packed has none of them.
10624        f.run(&[b"RPUSH", b"small", b"a"]);
10625        let line = f.run(&[b"DEBUG", b"OBJECT", b"small"]);
10626        assert!(!line.contains("ql_nodes"), "{line}");
10627    }
10628
10629    /// Looking is not using, which is the property the whole subcommand rests
10630    /// on: a diagnostic that reset the number it reports would answer nought
10631    /// every time it was asked.
10632    #[test]
10633    fn debug_object_does_not_count_as_using_the_key() {
10634        let mut f = Fixture::new();
10635        f.run(&[b"SET", b"s", b"hello"]);
10636        let was = field(&f.run(&[b"DEBUG", b"OBJECT", b"s"]), "lru:").to_owned();
10637
10638        f.server.set_clock_ms(f.server.clock.now_ms() + 60_000);
10639        let line = f.run(&[b"DEBUG", b"OBJECT", b"s"]);
10640
10641        assert_eq!(field(&line, "lru_seconds_idle:"), "60");
10642        // The clock the idle time counts back from has not moved, because
10643        // nothing has touched the key.
10644        assert_eq!(field(&line, "lru:"), was);
10645    }
10646
10647    /// The two lengths `DEBUG SDSLEN` is read for, and the four numbers about
10648    /// an allocator that is not here, which is D-135.
10649    #[test]
10650    fn debug_sdslen_measures_the_name_and_the_string_under_it() {
10651        let mut f = Fixture::new();
10652        f.run(&[b"SET", b"name", b"hello"]);
10653
10654        assert_eq!(
10655            f.run(&[b"DEBUG", b"SDSLEN", b"name"]),
10656            "+key_sds_len:4, key_sds_avail:0, key_zmalloc: 4, \
10657             val_sds_len:5, val_sds_avail:0, val_zmalloc: 5\r\n"
10658        );
10659    }
10660
10661    /// What each of the four refuses, which is the half a suite branches on.
10662    #[test]
10663    fn the_inspecting_subcommands_refuse_what_they_cannot_describe() {
10664        let mut f = Fixture::new();
10665        f.run(&[b"SET", b"s", b"hello"]);
10666        f.run(&[b"SET", b"n", b"12345"]);
10667        f.run(&[b"RPUSH", b"l", b"a"]);
10668
10669        // A key that is not there is the same sentence from all four, and it is
10670        // an error rather than the nil `OBJECT ENCODING` answers.
10671        for sub in [
10672            b"OBJECT".as_slice(),
10673            b"SDSLEN".as_slice(),
10674            b"LISTPACK".as_slice(),
10675            b"QUICKLIST".as_slice(),
10676        ] {
10677            assert_eq!(
10678                f.run(&[b"DEBUG", sub, b"nosuch"]),
10679                "-ERR no such key\r\n",
10680                "{}",
10681                String::from_utf8_lossy(sub)
10682            );
10683        }
10684
10685        // An integer encoded string has no string in it to measure.
10686        assert_eq!(
10687            f.run(&[b"DEBUG", b"SDSLEN", b"n"]),
10688            "-ERR Not an sds encoded string.\r\n"
10689        );
10690        assert_eq!(
10691            f.run(&[b"DEBUG", b"SDSLEN", b"l"]),
10692            "-ERR Not an sds encoded string.\r\n"
10693        );
10694
10695        // Each structure dump takes the representation it is named after and
10696        // nothing else, whatever type the value is.
10697        assert_eq!(
10698            f.run(&[b"DEBUG", b"LISTPACK", b"l"]),
10699            "+Listpack structure printed on stdout\r\n"
10700        );
10701        assert_eq!(
10702            f.run(&[b"DEBUG", b"QUICKLIST", b"l"]),
10703            "-ERR Not a quicklist encoded object.\r\n"
10704        );
10705        assert_eq!(
10706            f.run(&[b"DEBUG", b"LISTPACK", b"s"]),
10707            "-ERR Not a listpack encoded object.\r\n"
10708        );
10709    }
10710
10711    /// A listpack is a representation and not a type, so the same subcommand
10712    /// answers for four different types and refuses the intset next to them.
10713    #[test]
10714    fn debug_listpack_answers_for_anything_written_as_one() {
10715        let mut f = Fixture::new();
10716        f.run(&[b"RPUSH", b"l", b"a"]);
10717        f.run(&[b"HSET", b"h", b"f", b"v"]);
10718        f.run(&[b"SADD", b"st", b"a"]);
10719        f.run(&[b"ZADD", b"z", b"1", b"m"]);
10720        f.run(&[b"SADD", b"ints", b"1", b"2"]);
10721
10722        for key in [b"l".as_slice(), b"h", b"st", b"z"] {
10723            assert_eq!(
10724                f.run(&[b"DEBUG", b"LISTPACK", key]),
10725                "+Listpack structure printed on stdout\r\n",
10726                "{}",
10727                String::from_utf8_lossy(key)
10728            );
10729        }
10730        assert_eq!(
10731            f.run(&[b"OBJECT", b"ENCODING", b"ints"]),
10732            "$6\r\nintset\r\n"
10733        );
10734        assert_eq!(
10735            f.run(&[b"DEBUG", b"LISTPACK", b"ints"]),
10736            "-ERR Not a listpack encoded object.\r\n"
10737        );
10738    }
10739
10740    /// The level argument on `QUICKLIST`, which is read and dropped, and the
10741    /// wrong argument count on either, which is the container's own sentence.
10742    #[test]
10743    fn debug_quicklist_takes_a_level_it_does_nothing_with() {
10744        let mut f = Fixture::new();
10745        let member = vec![b'x'; 200];
10746        for _ in 0..100 {
10747            f.run(&[b"RPUSH", b"l", &member]);
10748        }
10749
10750        let said = "+Quicklist structure printed on stdout\r\n";
10751        assert_eq!(f.run(&[b"DEBUG", b"QUICKLIST", b"l"]), said);
10752        assert_eq!(f.run(&[b"DEBUG", b"QUICKLIST", b"l", b"1"]), said);
10753        // A word that is not a number is taken rather than refused, which is
10754        // the reference: it reads the argument with atoi and gets nought.
10755        assert_eq!(f.run(&[b"DEBUG", b"QUICKLIST", b"l", b"abc"]), said);
10756
10757        assert_eq!(
10758            f.run(&[b"DEBUG", b"QUICKLIST", b"l", b"1", b"2"]),
10759            "-ERR unknown subcommand or wrong number of arguments for \
10760             'QUICKLIST'. Try DEBUG HELP.\r\n"
10761        );
10762        assert_eq!(
10763            f.run(&[b"DEBUG", b"LISTPACK", b"l", b"0"]),
10764            "-ERR unknown subcommand or wrong number of arguments for \
10765             'LISTPACK'. Try DEBUG HELP.\r\n"
10766        );
10767    }
10768
10769    /// Every one of these is a number read off redis-server 8.10.1 rather than
10770    /// one this build produced, which is the only kind of assertion worth
10771    /// making about a digest: a number computed a different way is not a worse
10772    /// digest, it is a useless one.
10773    #[test]
10774    fn a_value_digest_is_the_number_the_reference_computes() {
10775        let mut f = Fixture::new();
10776        f.run(&[b"SET", b"s", b"hello"]);
10777        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
10778        f.run(&[b"SADD", b"t", b"a", b"b", b"c"]);
10779        f.run(&[b"HSET", b"h", b"f", b"v"]);
10780        f.run(&[b"ZADD", b"z", b"1", b"a", b"2.5", b"b"]);
10781        f.run(&[b"XADD", b"x", b"1-1", b"f", b"v"]);
10782
10783        for (key, want) in [
10784            (&b"s"[..], "36b23a1456b2dce2c3ed252c456761301dba8060"),
10785            (b"l", "8bf72d812571eea9b927f3c11beb0c4165a6ff89"),
10786            (b"t", "593c2414786d75446e97f4ea5d4b731f3313da72"),
10787            (b"h", "90c76e9e9f4c62d642a34fc97c7dad503b51f906"),
10788            (b"z", "c45c5b051acd64070e5ed1a949939d5145f806c5"),
10789            (b"x", "2ed9a7a81688084b1f7eae33456ef7727d357031"),
10790        ] {
10791            assert_eq!(
10792                f.run(&[b"DEBUG", b"DIGEST-VALUE", key]),
10793                format!("*1\r\n+{want}\r\n"),
10794                "{}",
10795                String::from_utf8_lossy(key)
10796            );
10797        }
10798    }
10799
10800    /// The deadline is in the digest and the time left is not, which is what
10801    /// lets two servers that agree about a dataset agree about the number.
10802    #[test]
10803    fn a_deadline_shows_up_without_the_time_left_showing_up() {
10804        let mut f = Fixture::new();
10805        f.run(&[b"SET", b"e", b"value"]);
10806        let bare = "*1\r\n+d59ec93db87f4cd915db3cdf44bb63755bc4a635\r\n";
10807        let dated = "*1\r\n+331b37c26446a68dd4cdd72701d1acee416ae7b6\r\n";
10808        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"e"]), bare);
10809
10810        f.run(&[b"EXPIRE", b"e", b"1000"]);
10811        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"e"]), dated);
10812        // A different deadline on the same value is the same digest.
10813        f.run(&[b"EXPIRE", b"e", b"999999"]);
10814        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"e"]), dated);
10815        f.run(&[b"PERSIST", b"e"]);
10816        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"e"]), bare);
10817
10818        // The same again for a field of a hash, which says so with its own
10819        // word rather than with the key's.
10820        f.run(&[b"HSET", b"he", b"f1", b"v1", b"f2", b"v2"]);
10821        f.run(&[b"HEXPIRE", b"he", b"1000", b"FIELDS", b"1", b"f2"]);
10822        assert_eq!(
10823            f.run(&[b"DEBUG", b"DIGEST-VALUE", b"he"]),
10824            "*1\r\n+8911d6d4d198f5e022f80dfefd5e15d6c0eaabe3\r\n"
10825        );
10826    }
10827
10828    /// The value and not the entry, which is the difference between the two
10829    /// subcommands and is why a copy answers the same forty characters.
10830    #[test]
10831    fn a_value_digest_does_not_know_what_the_key_is_called() {
10832        let mut f = Fixture::new();
10833        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
10834        f.run(&[b"COPY", b"l", b"l2"]);
10835        let want = "*1\r\n+8bf72d812571eea9b927f3c11beb0c4165a6ff89\r\n";
10836        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"l"]), want);
10837        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"l2"]), want);
10838
10839        // Several at once, in the order asked for, with a key that is not
10840        // there answering forty zeros rather than an error.
10841        assert_eq!(
10842            f.run(&[b"DEBUG", b"DIGEST-VALUE", b"l", b"gone", b"l2"]),
10843            format!(
10844                "*3\r\n+8bf72d812571eea9b927f3c11beb0c4165a6ff89\r\n+{0}\r\n\
10845                 +8bf72d812571eea9b927f3c11beb0c4165a6ff89\r\n",
10846                "0".repeat(40)
10847            )
10848        );
10849        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE"]), "*0\r\n");
10850    }
10851
10852    /// The whole server, where the name is in it and the database number is
10853    /// in it and an empty database is not.
10854    #[test]
10855    fn the_whole_digest_folds_in_the_names_and_the_database_numbers() {
10856        let mut f = Fixture::new();
10857        let empty = format!("+{}\r\n", "0".repeat(40));
10858        assert_eq!(f.run(&[b"DEBUG", b"DIGEST"]), empty);
10859
10860        f.run(&[b"SET", b"k", b"hello"]);
10861        assert_eq!(
10862            f.run(&[b"DEBUG", b"DIGEST"]),
10863            "+d101db227d1e3b31616b18b0b8700f84c3ffa5e9\r\n"
10864        );
10865
10866        f.run(&[b"SELECT", b"3"]);
10867        f.run(&[b"SET", b"k", b"hello"]);
10868        assert_eq!(
10869            f.run(&[b"DEBUG", b"DIGEST"]),
10870            "+a541f66c15932c1014da1569ff27c15bcde7d1dc\r\n"
10871        );
10872
10873        // Emptying the first one leaves the same key in the same place and a
10874        // different number, because the database it is in is folded in.
10875        f.run(&[b"SELECT", b"0"]);
10876        f.run(&[b"FLUSHDB"]);
10877        assert_eq!(
10878            f.run(&[b"DEBUG", b"DIGEST"]),
10879            "+f9b35ab00ad2f456386a2f73d316bf8266013606\r\n"
10880        );
10881
10882        f.run(&[b"FLUSHALL"]);
10883        assert_eq!(f.run(&[b"DEBUG", b"DIGEST"]), empty);
10884    }
10885
10886    /// Digesting is not using, which is what makes it safe for a suite to call
10887    /// between every step of whatever it is measuring.
10888    #[test]
10889    fn digesting_does_not_count_as_using_anything() {
10890        let mut f = Fixture::new();
10891        f.run(&[b"SET", b"k", b"value"]);
10892        f.run(&[b"CONFIG", b"RESETSTAT"]);
10893        f.run(&[b"DEBUG", b"DIGEST"]);
10894        f.run(&[b"DEBUG", b"DIGEST-VALUE", b"k", b"gone"]);
10895        let stats = f.run(&[b"INFO", b"stats"]);
10896        assert!(stats.contains("keyspace_hits:0\r\n"), "{stats}");
10897        assert!(stats.contains("keyspace_misses:0\r\n"), "{stats}");
10898
10899        // And the counters are working, so the nought above is the command
10900        // holding still rather than the statistic never moving.
10901        f.run(&[b"GET", b"k"]);
10902        f.run(&[b"GET", b"gone"]);
10903        let stats = f.run(&[b"INFO", b"stats"]);
10904        assert!(stats.contains("keyspace_hits:1\r\n"), "{stats}");
10905        assert!(stats.contains("keyspace_misses:1\r\n"), "{stats}");
10906    }
10907
10908    /// One field out of a `DEBUG OBJECT` line, named by its label.
10909    fn field<'a>(line: &'a str, label: &str) -> &'a str {
10910        let at = line
10911            .find(label)
10912            .unwrap_or_else(|| panic!("no {label} in {line}"));
10913        let rest = &line[at + label.len()..];
10914        rest.split([' ', '\r']).next().expect("a value")
10915    }
10916
10917    /// A fixture on a server with a password, on a connection that has not met
10918    /// it.
10919    ///
10920    /// The two calls have to be in this order and both have to happen. Setting
10921    /// the password is the server's half and admitting nothing is the
10922    /// connection's, and the connection's half is what a real front does at
10923    /// accept time. A fixture that only set the password would be a connection
10924    /// that was open before it went on, which is the case in
10925    /// [`a_password_set_under_an_open_connection_leaves_it_alone`].
10926    fn guarded(password: &[u8]) -> Fixture {
10927        let mut f = Fixture::new();
10928        f.server.set_password(password);
10929        f.session.admit(false);
10930        f
10931    }
10932
10933    /// Nothing gets through without the password, and the sentence is the one
10934    /// a client branches on.
10935    #[test]
10936    fn a_server_with_a_password_answers_everything_else_with_noauth() {
10937        let mut f = guarded(b"hunter2");
10938        for parts in [
10939            &[b"PING".as_slice()][..],
10940            &[b"GET", b"k"],
10941            &[b"SET", b"k", b"v"],
10942            &[b"COMMAND", b"COUNT"],
10943            &[b"SUBSCRIBE", b"ch"],
10944            &[b"MULTI"],
10945            &[b"INFO"],
10946        ] {
10947            assert_eq!(
10948                f.run(parts),
10949                "-NOAUTH Authentication required.\r\n",
10950                "{parts:?} got through"
10951            );
10952        }
10953    }
10954
10955    /// The four commands a client may send before it has authenticated.
10956    ///
10957    /// `AUTH` because it is the way in, `HELLO` because it carries the option
10958    /// that is the other way in, `RESET` because starting over cannot need a
10959    /// password, and `QUIT` because leaving cannot either. Redis marks all four
10960    /// `no_auth` and the gate reads the flag rather than the names.
10961    #[test]
10962    fn the_four_commands_that_do_not_need_the_password_get_through() {
10963        for name in ["auth", "hello", "reset", "quit"] {
10964            let spec = table::lookup(name.as_bytes()).expect(name);
10965            assert!(spec.flags.contains(&"no_auth"), "{name} is not no_auth");
10966        }
10967        let mut f = guarded(b"hunter2");
10968        assert_eq!(f.run(&[b"QUIT"]), "+OK\r\n");
10969        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
10970        assert_eq!(
10971            f.run(&[b"AUTH", b"wrong"]),
10972            "-WRONGPASS invalid username-password pair or user is disabled.\r\n"
10973        );
10974        assert_eq!(f.run(&[b"AUTH", b"hunter2"]), "+OK\r\n");
10975        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
10976    }
10977
10978    /// Both spellings of `AUTH`, and the one user there is.
10979    #[test]
10980    fn auth_takes_the_password_on_its_own_or_behind_the_user_name() {
10981        let mut f = guarded(b"hunter2");
10982        let wrong = "-WRONGPASS invalid username-password pair or user is disabled.\r\n";
10983        assert_eq!(f.run(&[b"AUTH", b"default", b"hunter2"]), "+OK\r\n");
10984        assert_eq!(f.run(&[b"AUTH", b"default", b"wrong"]), wrong);
10985        // And a failed attempt does not throw out the connection that had
10986        // already got in, which was read off 8.10.1 rather than assumed.
10987        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
10988        assert_eq!(f.run(&[b"AUTH", b"someone", b"hunter2"]), wrong);
10989        assert_eq!(
10990            f.run(&[b"AUTH"]),
10991            "-ERR wrong number of arguments for 'auth' command\r\n"
10992        );
10993        assert_eq!(f.run(&[b"AUTH", b"a", b"b", b"c"]), "-ERR syntax error\r\n");
10994    }
10995
10996    /// On a server with no password the default user is `nopass`, and what that
10997    /// means is not what it sounds like.
10998    ///
10999    /// Any password at all is the right one for it, so the two argument form
11000    /// says `OK`. The one argument form is the exception and gets a sentence
11001    /// about the configuration instead, because a client that sends it has
11002    /// almost certainly reached a server it did not mean to reach.
11003    #[test]
11004    fn auth_on_a_server_with_no_password_says_so_at_length() {
11005        let mut f = Fixture::new();
11006        assert_eq!(
11007            f.run(&[b"AUTH", b"anything"]),
11008            "-ERR AUTH <password> called without any password configured for the \
11009             default user. Are you sure your configuration is correct?\r\n"
11010        );
11011        assert_eq!(f.run(&[b"AUTH", b"default", b"anything"]), "+OK\r\n");
11012        assert_eq!(
11013            f.run(&[b"AUTH", b"nobody", b"anything"]),
11014            "-WRONGPASS invalid username-password pair or user is disabled.\r\n"
11015        );
11016        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
11017    }
11018
11019    /// `HELLO` has a sentence of its own, and the order it decides things in is
11020    /// not the order they are written in.
11021    ///
11022    /// The protocol version first, so a bad one is a `NOPROTO` even from a
11023    /// connection that has not authenticated and would have been let in by the
11024    /// `AUTH` option on the same line. Then the option, so a wrong password is a
11025    /// `WRONGPASS`. Then the password at all, which is what a bare `HELLO` on a
11026    /// guarded server gets. All three read off 8.10.1.
11027    #[test]
11028    fn hello_says_which_option_would_have_worked() {
11029        let long = "-NOAUTH HELLO must be called with the client already authenticated, \
11030                    otherwise the HELLO <proto> AUTH <user> <pass> option can be used to \
11031                    authenticate the client and select the RESP protocol version at the \
11032                    same time\r\n";
11033        let mut f = guarded(b"hunter2");
11034        assert_eq!(f.run(&[b"HELLO"]), long);
11035        assert_eq!(f.run(&[b"HELLO", b"3"]), long);
11036        assert_eq!(
11037            f.run(&[b"HELLO", b"9"]),
11038            "-NOPROTO unsupported protocol version\r\n"
11039        );
11040        // The version is refused before the option is applied, so this leaves
11041        // the connection exactly as unauthenticated as it found it.
11042        assert_eq!(
11043            f.run(&[b"HELLO", b"9", b"AUTH", b"default", b"hunter2"]),
11044            "-NOPROTO unsupported protocol version\r\n"
11045        );
11046        assert_eq!(f.run(&[b"PING"]), "-NOAUTH Authentication required.\r\n");
11047        assert_eq!(
11048            f.run(&[b"HELLO", b"2", b"AUTH", b"default", b"wrong"]),
11049            "-WRONGPASS invalid username-password pair or user is disabled.\r\n"
11050        );
11051        assert!(
11052            f.run(&[b"HELLO", b"3", b"AUTH", b"default", b"hunter2"])
11053                .starts_with("%7\r\n"),
11054            "the option did not let it in"
11055        );
11056        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
11057    }
11058
11059    /// `EXEC` is answered the abort rather than the refusal, with the refusal
11060    /// spliced into it.
11061    ///
11062    /// A client that sent `EXEC` is waiting for the transaction to be over one
11063    /// way or another, so the reference turns every refusal the funnel makes of
11064    /// an `EXEC` into an abort carrying the reason. The code word is in the
11065    /// spliced reason as well as in front of the reply it would have been.
11066    #[test]
11067    fn exec_without_the_password_is_an_abort_and_says_why() {
11068        let mut f = guarded(b"hunter2");
11069        assert_eq!(f.run(&[b"MULTI"]), "-NOAUTH Authentication required.\r\n");
11070        assert_eq!(
11071            f.run(&[b"EXEC"]),
11072            "-EXECABORT Transaction discarded because of: NOAUTH Authentication \
11073             required.\r\n"
11074        );
11075    }
11076
11077    /// `RESET` puts the connection back to how it was accepted, password and
11078    /// all.
11079    #[test]
11080    fn reset_gives_the_password_back_to_the_server_to_ask_for_again() {
11081        let mut f = guarded(b"hunter2");
11082        assert_eq!(f.run(&[b"AUTH", b"hunter2"]), "+OK\r\n");
11083        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
11084        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
11085        assert_eq!(f.run(&[b"PING"]), "-NOAUTH Authentication required.\r\n");
11086
11087        // And on a server with no password it puts back the same nothing.
11088        let mut f = Fixture::new();
11089        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
11090        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
11091    }
11092
11093    /// A password set under a connection that is already open leaves it alone.
11094    ///
11095    /// This is the rule nobody would guess and it is the reference's: the flag
11096    /// is decided when the connection is accepted, so `CONFIG SET requirepass`
11097    /// locks out everybody who connects after it and nobody who is already
11098    /// there, including the connection that sent it.
11099    #[test]
11100    fn a_password_set_under_an_open_connection_leaves_it_alone() {
11101        let mut f = Fixture::new();
11102        f.session.admit(true);
11103        assert_eq!(
11104            f.run(&[b"CONFIG", b"SET", b"requirepass", b"hunter2"]),
11105            "+OK\r\n"
11106        );
11107        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
11108        // And taking it off again lets in a connection that never met it.
11109        let mut later = Fixture::on(Server::new());
11110        later.server.set_password(b"hunter2");
11111        later.session.admit(false);
11112        assert_eq!(
11113            later.run(&[b"PING"]),
11114            "-NOAUTH Authentication required.\r\n"
11115        );
11116        later.server.set_password(b"");
11117        assert_eq!(later.run(&[b"PING"]), "+PONG\r\n");
11118    }
11119
11120    /// The password reads back in the clear and is set and cleared by the same
11121    /// pair of words.
11122    #[test]
11123    fn requirepass_reads_back_what_was_written_and_an_empty_one_clears_it() {
11124        let mut f = Fixture::new();
11125        // Open before the password goes on, so the connection keeps talking
11126        // after it does and can read it back.
11127        f.session.admit(true);
11128        assert_eq!(
11129            f.run(&[b"CONFIG", b"GET", b"requirepass"]),
11130            "*2\r\n$11\r\nrequirepass\r\n$0\r\n\r\n"
11131        );
11132        f.run(&[b"CONFIG", b"SET", b"requirepass", b"hunter2"]);
11133        assert_eq!(
11134            f.run(&[b"CONFIG", b"GET", b"requirepass"]),
11135            "*2\r\n$11\r\nrequirepass\r\n$7\r\nhunter2\r\n"
11136        );
11137        assert!(f.server.guarded());
11138        f.run(&[b"CONFIG", b"SET", b"requirepass", b""]);
11139        assert!(!f.server.guarded(), "an empty password did not clear it");
11140        assert_eq!(
11141            f.run(&[b"CONFIG", b"GET", b"requirepass"]),
11142            "*2\r\n$11\r\nrequirepass\r\n$0\r\n\r\n"
11143        );
11144    }
11145
11146    /// Every `DEBUG PROTOCOL` type, on RESP2, byte for byte off 8.10.1.
11147    #[test]
11148    fn debug_protocol_writes_what_the_reference_writes_on_resp2() {
11149        let mut f = Fixture::new();
11150        for (kind, want) in [
11151            ("string", "$11\r\nHello World\r\n"),
11152            ("integer", ":12345\r\n"),
11153            ("double", "$5\r\n3.141\r\n"),
11154            ("bignum", "$37\r\n1234567999999999999999999999999999999\r\n"),
11155            ("null", "$-1\r\n"),
11156            ("array", "*3\r\n:0\r\n:1\r\n:2\r\n"),
11157            ("set", "*3\r\n:0\r\n:1\r\n:2\r\n"),
11158            ("map", "*6\r\n:0\r\n:0\r\n:1\r\n:1\r\n:2\r\n:0\r\n"),
11159            (
11160                "attrib",
11161                "$39\r\nSome real reply following the attribute\r\n",
11162            ),
11163            ("push", "-ERR RESP2 is not supported by this command\r\n"),
11164            ("verbatim", "$25\r\nThis is a verbatim\nstring\r\n"),
11165            ("true", ":1\r\n"),
11166            ("false", ":0\r\n"),
11167        ] {
11168            assert_eq!(
11169                f.run(&[b"DEBUG", b"PROTOCOL", kind.as_bytes()]),
11170                want,
11171                "{kind}"
11172            );
11173        }
11174    }
11175
11176    /// And on RESP3, where all thirteen are their own type.
11177    #[test]
11178    fn debug_protocol_writes_what_the_reference_writes_on_resp3() {
11179        let mut f = Fixture::new();
11180        f.out = Out::new(Proto::Resp3);
11181        for (kind, want) in [
11182            ("string", "$11\r\nHello World\r\n"),
11183            ("integer", ":12345\r\n"),
11184            ("double", ",3.141\r\n"),
11185            ("bignum", "(1234567999999999999999999999999999999\r\n"),
11186            ("null", "_\r\n"),
11187            ("array", "*3\r\n:0\r\n:1\r\n:2\r\n"),
11188            ("set", "~3\r\n:0\r\n:1\r\n:2\r\n"),
11189            ("map", "%3\r\n:0\r\n#f\r\n:1\r\n#t\r\n:2\r\n#f\r\n"),
11190            (
11191                "attrib",
11192                "|1\r\n$14\r\nkey-popularity\r\n*2\r\n$7\r\nkey:123\r\n:90\r\n\
11193                 $39\r\nSome real reply following the attribute\r\n",
11194            ),
11195            (
11196                "push",
11197                "$40\r\nSome real reply following the push reply\r\n\
11198                 >2\r\n$16\r\nserver-cpu-usage\r\n:42\r\n",
11199            ),
11200            ("verbatim", "=29\r\ntxt:This is a verbatim\nstring\r\n"),
11201            ("true", "#t\r\n"),
11202            ("false", "#f\r\n"),
11203        ] {
11204            assert_eq!(
11205                f.run(&[b"DEBUG", b"PROTOCOL", kind.as_bytes()]),
11206                want,
11207                "{kind}"
11208            );
11209        }
11210    }
11211
11212    /// A type name that is not one of the thirteen lists all thirteen.
11213    #[test]
11214    fn debug_protocol_names_every_type_when_it_is_given_none_of_them() {
11215        let mut f = Fixture::new();
11216        for kind in [&b"bogus"[..], b""] {
11217            assert_eq!(
11218                f.run(&[b"DEBUG", b"PROTOCOL", kind]),
11219                "-ERR Wrong protocol type name. Please use one of the following: \
11220                 string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false\r\n"
11221            );
11222        }
11223    }
11224
11225    /// The one sentence `DEBUG` says about everything it cannot do.
11226    ///
11227    /// A subcommand that does not exist and a subcommand handed the wrong number
11228    /// of arguments are the same case on a real server, because both fall off
11229    /// the end of the same chain of tests, and the name is echoed in the case it
11230    /// arrived in.
11231    #[test]
11232    fn debug_says_the_same_thing_about_a_bad_name_and_a_bad_count() {
11233        let mut f = Fixture::new();
11234        for parts in [
11235            &[b"DEBUG".as_slice(), b"NOSUCH"][..],
11236            &[b"DEBUG", b"PROTOCOL"],
11237            &[b"DEBUG", b"PROTOCOL", b"string", b"extra"],
11238            &[b"DEBUG", b"SLEEP"],
11239            &[b"DEBUG", b"SET-ACTIVE-EXPIRE", b"1", b"2"],
11240            &[b"DEBUG", b"HELP", b"me"],
11241        ] {
11242            let got = f.run(parts);
11243            let name = String::from_utf8_lossy(parts[1]).to_string();
11244            assert_eq!(
11245                got,
11246                format!(
11247                    "-ERR unknown subcommand or wrong number of arguments for '{name}'. \
11248                     Try DEBUG HELP.\r\n"
11249                ),
11250                "{name}"
11251            );
11252        }
11253        assert_eq!(
11254            f.run(&[b"DEBUG"]),
11255            "-ERR wrong number of arguments for 'debug' command\r\n"
11256        );
11257    }
11258
11259    /// `DEBUG ERROR` writes the line it was given and nothing around it.
11260    #[test]
11261    fn debug_error_hands_back_whatever_it_was_given() {
11262        let mut f = Fixture::new();
11263        assert_eq!(f.run(&[b"DEBUG", b"ERROR", b"my error"]), "-my error\r\n");
11264        assert_eq!(f.run(&[b"DEBUG", b"ERROR", b""]), "-\r\n");
11265        // A code the caller made up goes out as the code, which is the whole use
11266        // of this: a client library testing that it branches on one.
11267        assert_eq!(
11268            f.run(&[b"DEBUG", b"ERROR", b"-WEIRD thing"]),
11269            "--WEIRD thing\r\n"
11270        );
11271        // And a newline in the middle cannot become a second reply.
11272        assert_eq!(
11273            f.run(&[b"DEBUG", b"ERROR", b"two\nlines"]),
11274            "-two lines\r\n"
11275        );
11276    }
11277
11278    /// `DEBUG POPULATE` fills a database and leaves what is already there.
11279    #[test]
11280    fn debug_populate_fills_and_skips_what_is_there() {
11281        let mut f = Fixture::new();
11282        assert_eq!(f.run(&[b"SET", b"key:0", b"mine"]), "+OK\r\n");
11283        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"3"]), "+OK\r\n");
11284        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n");
11285        assert_eq!(f.run(&[b"GET", b"key:0"]), "$4\r\nmine\r\n");
11286        assert_eq!(f.run(&[b"GET", b"key:2"]), "$7\r\nvalue:2\r\n");
11287        // A prefix is the whole of the name in front of the colon, so the colon
11288        // in a prefix that has one is not the separator and there are two.
11289        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"1", b"p:"]), "+OK\r\n");
11290        assert_eq!(f.run(&[b"GET", b"p::0"]), "$7\r\nvalue:0\r\n");
11291        // A size pads with zero bytes, and one shorter than the name cuts it.
11292        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"1", b"q", b"9"]), "+OK\r\n");
11293        assert_eq!(f.raw(&[b"GET", b"q:0"]), b"$9\r\nvalue:0\0\0\r\n");
11294        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"1", b"r", b"4"]), "+OK\r\n");
11295        assert_eq!(f.run(&[b"GET", b"r:0"]), "$4\r\nvalu\r\n");
11296        // And nought is not a size of nothing, it is no size at all.
11297        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"1", b"s", b"0"]), "+OK\r\n");
11298        assert_eq!(f.run(&[b"GET", b"s:0"]), "$7\r\nvalue:0\r\n");
11299    }
11300
11301    /// Both of `POPULATE`'s numbers complain about the range and not the digits.
11302    #[test]
11303    fn debug_populate_wants_two_numbers_that_are_not_negative() {
11304        let mut f = Fixture::new();
11305        for parts in [
11306            &[b"DEBUG".as_slice(), b"POPULATE", b"abc"][..],
11307            &[b"DEBUG", b"POPULATE", b"-1"],
11308            &[b"DEBUG", b"POPULATE", b"1.5"],
11309            &[b"DEBUG", b"POPULATE", b"1", b"p", b"-1"],
11310            &[b"DEBUG", b"POPULATE", b"1", b"p", b"x"],
11311        ] {
11312            assert_eq!(
11313                f.run(parts),
11314                "-ERR value is out of range, must be positive\r\n"
11315            );
11316        }
11317        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"0"]), "+OK\r\n");
11318        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
11319    }
11320
11321    /// The packed threshold takes a memory value up to just under four gigabytes.
11322    ///
11323    /// The error sentence says bigger than one and smaller than 4gb and neither
11324    /// half of that is what is checked, which is why the numbers here were taken
11325    /// off a running server rather than off the sentence.
11326    #[test]
11327    fn debug_quicklist_packed_threshold_takes_what_the_reference_takes() {
11328        let mut f = Fixture::new();
11329        for good in [
11330            &b"1"[..],
11331            b"2",
11332            b"1b",
11333            b"1K",
11334            b"1kb",
11335            b"1G",
11336            b"3gb",
11337            b"0",
11338            b"0b",
11339        ] {
11340            assert_eq!(
11341                f.run(&[b"DEBUG", b"QUICKLIST-PACKED-THRESHOLD", good]),
11342                "+OK\r\n",
11343                "{}",
11344                String::from_utf8_lossy(good)
11345            );
11346        }
11347        for bad in [
11348            &b"4gb"[..],
11349            b"4294967295",
11350            b"4294967296",
11351            b"abc",
11352            b"",
11353            b"+5",
11354            b"1.5",
11355        ] {
11356            assert_eq!(
11357                f.run(&[b"DEBUG", b"QUICKLIST-PACKED-THRESHOLD", bad]),
11358                "-ERR argument must be a memory value bigger than 1 and smaller than 4gb\r\n",
11359                "{}",
11360                String::from_utf8_lossy(bad)
11361            );
11362        }
11363    }
11364
11365    /// The three gates really gate, and they say `OK` to anything.
11366    #[test]
11367    fn the_debug_gates_turn_the_things_they_name_off_and_on_again() {
11368        let mut f = Fixture::new();
11369        for (sub, read) in [
11370            (&b"SET-ACTIVE-EXPIRE"[..], 0),
11371            (b"DICT-RESIZING", 1),
11372            (b"PAUSE-CRON", 2),
11373        ] {
11374            let reads: [fn(&Server) -> bool; 3] =
11375                [Server::expiring, Server::resizing, Server::cron_running];
11376            let on = reads[read];
11377            // `PAUSE-CRON` is the one whose argument means the opposite of the
11378            // gate, since it names the stopping and the gate names the running.
11379            let stop: &[u8] = if read == 2 { b"1" } else { b"0" };
11380            let go: &[u8] = if read == 2 { b"0" } else { b"1" };
11381            assert!(on(&f.server), "{}", String::from_utf8_lossy(sub));
11382            assert_eq!(f.run(&[b"DEBUG", sub, stop]), "+OK\r\n");
11383            assert!(!on(&f.server), "{}", String::from_utf8_lossy(sub));
11384            // A word is nought to `atoi`, so it turns the gate off rather than
11385            // being refused, and on `PAUSE-CRON` that means it starts the cron.
11386            assert_eq!(f.run(&[b"DEBUG", sub, b"nonsense"]), "+OK\r\n");
11387            assert_eq!(on(&f.server), read == 2);
11388            assert_eq!(f.run(&[b"DEBUG", sub, go]), "+OK\r\n");
11389            assert!(on(&f.server), "{}", String::from_utf8_lossy(sub));
11390        }
11391    }
11392
11393    /// A key past its deadline is not swept while the sweep is off.
11394    ///
11395    /// The lazy read still reports it gone, which is the same split a real
11396    /// server has: `SET-ACTIVE-EXPIRE 0` stops the background cycle and does not
11397    /// make an expired key readable.
11398    #[test]
11399    fn the_sweep_stops_when_debug_turns_it_off() {
11400        let mut f = Fixture::new();
11401        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PX", b"10"]), "+OK\r\n");
11402        assert_eq!(f.run(&[b"DEBUG", b"SET-ACTIVE-EXPIRE", b"0"]), "+OK\r\n");
11403        f.advance(50);
11404        assert_eq!(f.server.expire_slice(64), 0);
11405        assert_eq!(f.run(&[b"DEBUG", b"SET-ACTIVE-EXPIRE", b"1"]), "+OK\r\n");
11406        assert_eq!(f.server.expire_slice(64), 1);
11407    }
11408
11409    /// Five of the ten `COMMAND INFO` fields are sets once RESP3 has a set.
11410    ///
11411    /// This is every command and not just `DEBUG`, and it only shows on RESP3,
11412    /// which is why it went unnoticed until a wire compare looked at the bytes
11413    /// rather than at what a client decoded them into.
11414    #[test]
11415    fn command_info_sends_sets_where_the_reference_sends_sets() {
11416        let mut f = Fixture::new();
11417        f.out = Out::new(Proto::Resp3);
11418        assert_eq!(
11419            f.run(&[b"COMMAND", b"INFO", b"get"]),
11420            "*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\
11421             ~3\r\n+@read\r\n+@string\r\n+@fast\r\n~0\r\n~1\r\n%3\r\n\
11422             $5\r\nflags\r\n~2\r\n+RO\r\n+access\r\n\
11423             $12\r\nbegin_search\r\n%2\r\n$4\r\ntype\r\n$5\r\nindex\r\n$4\r\nspec\r\n\
11424             %1\r\n$5\r\nindex\r\n:1\r\n\
11425             $9\r\nfind_keys\r\n%2\r\n$4\r\ntype\r\n$5\r\nrange\r\n$4\r\nspec\r\n\
11426             %3\r\n$7\r\nlastkey\r\n:0\r\n$7\r\nkeystep\r\n:1\r\n$5\r\nlimit\r\n:0\r\n\
11427             ~0\r\n"
11428        );
11429        // And RESP2, where a set is an array and nothing moved.
11430        let mut f = Fixture::new();
11431        assert_eq!(
11432            f.run(&[b"COMMAND", b"INFO", b"get"]),
11433            "*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\
11434             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*1\r\n*6\r\n\
11435             $5\r\nflags\r\n*2\r\n+RO\r\n+access\r\n\
11436             $12\r\nbegin_search\r\n*4\r\n$4\r\ntype\r\n$5\r\nindex\r\n$4\r\nspec\r\n\
11437             *2\r\n$5\r\nindex\r\n:1\r\n\
11438             $9\r\nfind_keys\r\n*4\r\n$4\r\ntype\r\n$5\r\nrange\r\n$4\r\nspec\r\n\
11439             *6\r\n$7\r\nlastkey\r\n:0\r\n$7\r\nkeystep\r\n:1\r\n$5\r\nlimit\r\n:0\r\n\
11440             *0\r\n"
11441        );
11442    }
11443
11444    /// `DEBUG` is admin, so no monitor is ever shown one.
11445    #[test]
11446    fn debug_is_admin_and_stays_off_a_monitor_feed() {
11447        let spec = table::lookup(b"debug").expect("debug is in the table");
11448        assert!(spec.flags.contains(&"admin"));
11449        assert_eq!(spec.arity, -2);
11450        assert_eq!(spec.acl, ["@admin", "@slow", "@dangerous"]);
11451    }
11452
11453    #[test]
11454    fn the_command_counter_counts_every_command_including_the_bad_ones() {
11455        let mut f = Fixture::new();
11456        f.run(&[b"PING"]);
11457        f.run(&[b"NOPE"]);
11458        f.run(&[b"GET"]);
11459        assert_eq!(f.server.totals().commands, 3);
11460    }
11461
11462    #[test]
11463    fn what_a_thread_marked_is_taken_by_the_maintenance_turn() {
11464        let mut server = Server::new();
11465        server.set_threads(2);
11466        // A fresh server has every database on the turn's list, so start from
11467        // nothing to see the one mark arrive.
11468        server.mine().turn.store(0, Relaxed);
11469        server.locals[1].mark(1 << 9);
11470        server.collect_marks();
11471        assert!(server.mine().wanted(9));
11472        // And taken once rather than left to be taken again next turn.
11473        assert_eq!(server.locals[1].dirty.load(Relaxed), 0);
11474    }
11475
11476    #[test]
11477    fn what_two_threads_counted_is_added_up_when_info_asks() {
11478        let mut server = Server::new();
11479        server.set_threads(2);
11480        // Written into the two sets by hand, because what is under test is the
11481        // adding up and not the claiming, and one test thread can only ever
11482        // claim one set.
11483        let ping = lookup(b"PING").expect("PING is a command");
11484        for (at, calls) in [(0, 2), (1, 3)] {
11485            let counters = &server.locals[at];
11486            for _ in 0..calls {
11487                counters.stats.commands.bump();
11488                counters.cmdstats.at(ping).calls.bump();
11489            }
11490            counters.stats.opened();
11491        }
11492        assert_eq!(server.totals().commands, 5);
11493        assert_eq!(server.totals().clients, 2);
11494        assert_eq!(server.totals().connections, 2);
11495        let rows: Vec<_> = server.command_stats().collect();
11496        assert_eq!(rows.len(), 1);
11497        assert_eq!(rows[0].0, "ping");
11498        assert_eq!(rows[0].1.calls, 5);
11499        // A reset takes the totals and leaves the open connections, which are
11500        // still open.
11501        server.reset_stats();
11502        assert_eq!(server.totals().commands, 0);
11503        assert_eq!(server.totals().connections, 0);
11504        assert_eq!(server.totals().clients, 2);
11505    }
11506
11507    #[test]
11508    fn the_parked_count_says_what_the_waiter_list_says() {
11509        let mut f = Fixture::new();
11510        assert_eq!(f.server.parked(), 0);
11511        for client in 1..=3u64 {
11512            f.session = Session::new(client);
11513            assert_eq!(f.flow(&[b"BLPOP", b"q", b"0"]).0, Flow::Block);
11514        }
11515        assert_eq!(f.server.parked(), 3);
11516        assert_eq!(f.server.waiters().len(), 3);
11517
11518        // The three ways the list gets shorter, each of which has to move the
11519        // number with it, because a number left behind is either a walk of the
11520        // list that never happens or one that runs off the end of it.
11521        f.server.forget_waiters(2);
11522        assert_eq!(f.server.parked(), f.server.waiters().len());
11523        f.server.forget_waiters(1);
11524        assert_eq!(f.server.parked(), f.server.waiters().len());
11525        f.run(&[b"RPUSH", b"q", b"v"]);
11526        let mut out = Out::new(Proto::Resp2);
11527        assert!(f.server.serve_waiter(3, 0, &mut out));
11528        f.server.forget_waiters(3);
11529        assert_eq!(f.server.parked(), 0);
11530        assert!(f.server.waiters().is_empty());
11531    }
11532
11533    #[test]
11534    fn a_set_goes_from_bytes_to_bytes() {
11535        let mut f = Fixture::new();
11536        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
11537        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
11538        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
11539        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
11540        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
11541        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
11542        assert_eq!(
11543            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
11544            "*3\r\n:1\r\n:0\r\n:1\r\n"
11545        );
11546        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
11547        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
11548    }
11549
11550    #[test]
11551    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
11552        let mut f = Fixture::new();
11553        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
11554        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
11555        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
11556        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
11557        assert_eq!(
11558            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
11559            "*2\r\n:0\r\n:0\r\n"
11560        );
11561        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
11562    }
11563
11564    #[test]
11565    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
11566        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
11567        // and one that gets a `*` hands it a list, without either of them being
11568        // told which command was sent.
11569        let mut f = Fixture::new();
11570        f.run(&[b"SADD", b"s", b"one"]);
11571        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
11572
11573        f.run(&[b"HELLO", b"3"]);
11574        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
11575    }
11576
11577    #[test]
11578    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
11579        // An intset holds the number, so these digits exist for the first time
11580        // in the reply buffer.
11581        let mut f = Fixture::new();
11582        f.run(&[b"SADD", b"s", b"42"]);
11583        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
11584        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
11585        assert_eq!(
11586            f.run(&[b"SISMEMBER", b"s", b"042"]),
11587            ":0\r\n",
11588            "the member is the bytes and not the number they parse to"
11589        );
11590    }
11591
11592    #[test]
11593    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
11594        let mut f = Fixture::new();
11595        f.run(&[b"SET", b"str", b"v"]);
11596        f.run(&[b"SADD", b"set", b"a"]);
11597
11598        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
11599        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
11600        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
11601        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
11602        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
11603        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
11604        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
11605        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
11606        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
11607
11608        // MGET is the one that does not, because Redis gives nil for the odd
11609        // key out rather than failing the good keys next to it.
11610        assert_eq!(
11611            f.run(&[b"MGET", b"str", b"set", b"nope"]),
11612            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
11613        );
11614        // And plain SET overwrites any type, which takes the body with it.
11615        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
11616        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
11617    }
11618
11619    #[test]
11620    fn a_wrongtype_leaves_nothing_half_written() {
11621        // SMISMEMBER writes an array header and then one reply per member, so
11622        // it is the first command in the server that could get a header out in
11623        // front of an error if it checked its key in the wrong order.
11624        let mut f = Fixture::new();
11625        f.run(&[b"SET", b"k", b"v"]);
11626        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
11627        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
11628        assert!(!reply.contains('*'), "an array header went out in front");
11629    }
11630
11631    #[test]
11632    fn emptying_a_set_takes_the_key_with_it() {
11633        let mut f = Fixture::new();
11634        f.run(&[b"SADD", b"s", b"a", b"b"]);
11635        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
11636        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
11637        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
11638        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
11639        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
11640    }
11641
11642    /// Pull the cursor and the members out of one `SSCAN` reply.
11643    ///
11644    /// Crude on purpose. A test that walked a set through a real client would
11645    /// be testing the client, and what these tests are about is the shape of
11646    /// the bytes and the fact that a walk sees every member once.
11647    fn split_scan(reply: &str) -> (String, Vec<String>) {
11648        let mut lines = reply.split("\r\n");
11649        assert_eq!(lines.next(), Some("*2"), "got {reply}");
11650        lines.next().expect("the cursor header");
11651        let cursor = lines.next().expect("the cursor").to_owned();
11652        let header = lines.next().expect("the member header");
11653        let n: usize = header[1..].parse().expect("a member count");
11654        let mut members = Vec::with_capacity(n);
11655        for _ in 0..n {
11656            lines.next().expect("a member header");
11657            members.push(lines.next().expect("a member").to_owned());
11658        }
11659        (cursor, members)
11660    }
11661
11662    #[test]
11663    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
11664        let mut f = Fixture::new();
11665        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
11666
11667        let one = f.run(&[b"SPOP", b"s"]);
11668        assert!(
11669            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
11670            "got {one}"
11671        );
11672        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
11673
11674        // A count takes that many, and the last one takes the key with it.
11675        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
11676        assert!(rest.starts_with("*3\r\n"), "got {rest}");
11677        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
11678        // And a pop at a key that is not there is a nil, not an empty bulk.
11679        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
11680        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
11681    }
11682
11683    #[test]
11684    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
11685        // The one place in the server where the reply type carries something
11686        // the command name does not. SPOP's members are distinct so a RESP3
11687        // client can build a set out of them. SRANDMEMBER with a negative count
11688        // can hand back the same member three times, and a set would lose two.
11689        let mut f = Fixture::new();
11690        f.run(&[b"HELLO", b"3"]);
11691        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
11692
11693        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
11694        // And a positive count is an array too, since Redis makes it one.
11695        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
11696
11697        // A negative count against a set of one is where the difference bites:
11698        // the same member three times, which is a three element reply and would
11699        // have been a one element reply if it had gone out as a set.
11700        f.run(&[b"SADD", b"one", b"z"]);
11701        assert_eq!(
11702            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
11703            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
11704        );
11705    }
11706
11707    #[test]
11708    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
11709        let mut f = Fixture::new();
11710        f.run(&[b"SADD", b"s", b"only"]);
11711        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
11712        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
11713        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
11714
11715        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
11716        // The count form answers an empty array rather than a nil, which is the
11717        // pair of answers Redis gives and is not the pair it looks like.
11718        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
11719        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
11720        // Asking for more than is there answers all of it once and not padding.
11721        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
11722    }
11723
11724    #[test]
11725    fn a_pop_count_that_is_not_a_positive_number_says_so() {
11726        let mut f = Fixture::new();
11727        f.run(&[b"SADD", b"s", b"a"]);
11728        let bad = "-ERR value is out of range, must be positive\r\n";
11729        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
11730        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
11731        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
11732        // Zero is allowed and is a real answer rather than an error.
11733        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
11734        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
11735    }
11736
11737    #[test]
11738    fn a_scan_walks_a_set_of_any_size_exactly_once() {
11739        let mut f = Fixture::new();
11740        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
11741        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
11742            .into_iter()
11743            .chain(members.iter().map(Vec::as_slice))
11744            .collect();
11745        f.run(&args);
11746
11747        let mut seen = Vec::new();
11748        let mut cursor = "0".to_owned();
11749        loop {
11750            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
11751            let (next, got) = split_scan(&reply);
11752            seen.extend(got);
11753            cursor = next;
11754            if cursor == "0" {
11755                break;
11756            }
11757        }
11758        seen.sort();
11759        seen.dedup();
11760        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
11761
11762        // A set small enough to be a listpack answers in one call whatever
11763        // cursor it was handed, which is what Redis does for that encoding.
11764        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
11765        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
11766        assert_eq!(cursor, "0");
11767        assert_eq!(got.len(), 3);
11768        // And a key that is not there is a finished scan of nothing.
11769        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
11770    }
11771
11772    #[test]
11773    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
11774        let mut f = Fixture::new();
11775        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
11776
11777        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
11778        let mut got = got;
11779        got.sort();
11780        assert_eq!(got, ["aa", "ab"]);
11781
11782        // An integer member has no digits stored anywhere, so MATCH is the one
11783        // place a scan pays to write some.
11784        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
11785        let mut got = got;
11786        got.sort();
11787        assert_eq!(got, ["12", "13"]);
11788
11789        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
11790        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
11791        assert_eq!(
11792            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
11793            "-ERR syntax error\r\n"
11794        );
11795        // A count under one is a syntax error and not a range error, which is
11796        // the odder of Redis's two answers and the reason it is copied exactly.
11797        assert_eq!(
11798            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
11799            "-ERR syntax error\r\n"
11800        );
11801    }
11802
11803    #[test]
11804    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
11805        let mut f = Fixture::new();
11806        f.run(&[b"SADD", b"src", b"a", b"b"]);
11807        f.run(&[b"SADD", b"dst", b"c"]);
11808
11809        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
11810        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
11811        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
11812        // A member that is not in the source is a zero and moves nothing.
11813        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
11814        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
11815
11816        // A destination that does not exist gets made, and a source that runs
11817        // out goes away.
11818        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
11819        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
11820        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
11821    }
11822
11823    #[test]
11824    fn moving_checks_the_types_in_the_order_redis_checks_them() {
11825        // Not the order it looks like it should be. A source that is not there
11826        // answers zero without ever looking at the destination, so this is a
11827        // zero and not a WRONGTYPE even though the destination is a string.
11828        let mut f = Fixture::new();
11829        f.run(&[b"SET", b"str", b"v"]);
11830        f.run(&[b"SADD", b"set", b"a"]);
11831
11832        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
11833        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
11834        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
11835        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
11836        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
11837        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
11838        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
11839        assert_eq!(
11840            f.run(&[b"SISMEMBER", b"set", b"a"]),
11841            ":1\r\n",
11842            "and none of that moved anything"
11843        );
11844    }
11845
11846    #[test]
11847    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
11848        // SSCAN writes an outer array header before it walks, so it is the
11849        // command most likely to get bytes out in front of an error.
11850        let mut f = Fixture::new();
11851        f.run(&[b"SADD", b"s", b"a"]);
11852        for bad in [
11853            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
11854            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
11855            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
11856        ] {
11857            let reply = f.run(bad);
11858            assert!(reply.starts_with("-ERR"), "got {reply}");
11859            assert!(!reply.contains('*'), "an array header went out in front");
11860        }
11861    }
11862
11863    #[test]
11864    fn a_hash_writes_reads_and_deletes_its_fields() {
11865        let mut f = Fixture::new();
11866        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
11867        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
11868        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
11869        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
11870        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
11871        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
11872        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
11873        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
11874        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
11875        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
11876
11877        // The value the client sent is `9`, so HGET h b must not find the `2`
11878        // that is a value. A search with a step of one would have.
11879        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
11880
11881        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
11882        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
11883        assert_eq!(
11884            f.run(&[b"EXISTS", b"h"]),
11885            ":0\r\n",
11886            "and losing the last field lost the key"
11887        );
11888    }
11889
11890    #[test]
11891    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
11892        let mut f = Fixture::new();
11893        f.run(&[b"HSET", b"h", b"a", b"1"]);
11894        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
11895        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
11896        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
11897        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
11898        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
11899
11900        f.run(&[b"HELLO", b"3"]);
11901        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
11902        assert_eq!(
11903            f.run(&[b"HGETALL", b"nokey"]),
11904            "%0\r\n",
11905            "a missing key is the empty hash and never a nil"
11906        );
11907        assert_eq!(
11908            f.run(&[b"HKEYS", b"h"]),
11909            "*1\r\n$1\r\na\r\n",
11910            "and the two that answer one side stay arrays"
11911        );
11912    }
11913
11914    #[test]
11915    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
11916        let mut f = Fixture::new();
11917        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
11918        assert_eq!(
11919            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
11920            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
11921            "the reply is positional, so b is a nil and not a gap"
11922        );
11923        assert_eq!(
11924            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
11925            "*2\r\n$-1\r\n$-1\r\n",
11926            "and a missing key is all nils rather than an empty array"
11927        );
11928
11929        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
11930        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
11931        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
11932    }
11933
11934    #[test]
11935    fn a_hash_counts_up_and_says_so_when_it_cannot() {
11936        let mut f = Fixture::new();
11937        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
11938        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
11939        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
11940        assert_eq!(
11941            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
11942            "$4\r\n10.5\r\n",
11943            "a bulk string and not a double, on both protocols"
11944        );
11945
11946        f.run(&[b"HSET", b"h", b"s", b"words"]);
11947        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
11948        assert!(
11949            bad.starts_with("-ERR hash value is not an integer"),
11950            "{bad}"
11951        );
11952        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
11953        assert!(
11954            bad.starts_with("-ERR value is not an integer"),
11955            "a bad argument is not yet a hash value, {bad}"
11956        );
11957        assert_eq!(
11958            f.run(&[b"HGET", b"h", b"s"]),
11959            "$5\r\nwords\r\n",
11960            "and neither of them wrote anything"
11961        );
11962    }
11963
11964    #[test]
11965    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
11966        // Fourteen minutes under Miri at five hundred, which was the slowest
11967        // test in this crate that was not about megabytes. What the count has
11968        // to be is more than one page of the cursor, and the count below is
11969        // thirty two, so ninety six is three pages and asks the same question.
11970        let fields = if cfg!(miri) { 96 } else { 500 };
11971        let mut f = Fixture::new();
11972        for i in 0..fields {
11973            let field = format!("field-{i}");
11974            let value = format!("value-{i}");
11975            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
11976        }
11977
11978        let mut seen: Vec<String> = Vec::new();
11979        let mut cursor = "0".to_owned();
11980        loop {
11981            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
11982            let (next, items) = scan_reply(&reply);
11983            assert_eq!(items.len() % 2, 0, "a pair went out half written");
11984            for pair in items.chunks(2) {
11985                assert_eq!(
11986                    pair[0].strip_prefix("field-"),
11987                    pair[1].strip_prefix("value-"),
11988                    "a field came back with someone else's value"
11989                );
11990                seen.push(pair[0].clone());
11991            }
11992            cursor = next;
11993            if cursor == "0" {
11994                break;
11995            }
11996        }
11997        seen.sort();
11998        seen.dedup();
11999        assert_eq!(seen.len(), fields, "every field once and only once");
12000
12001        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
12002        assert!(
12003            items.iter().all(|s| s.starts_with("field-")),
12004            "NOVALUES still sent the values"
12005        );
12006
12007        let last = fields - 1;
12008        let (_, one) = scan_reply(&f.run(&[
12009            b"HSCAN",
12010            b"h",
12011            b"0",
12012            b"MATCH",
12013            format!("field-{last}").as_bytes(),
12014            b"COUNT",
12015            b"1000",
12016        ]));
12017        assert_eq!(
12018            one,
12019            [format!("field-{last}"), format!("value-{last}")],
12020            "MATCH is on the field"
12021        );
12022    }
12023
12024    #[test]
12025    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
12026        let mut f = Fixture::new();
12027        f.run(&[b"HSET", b"h", b"a", b"1"]);
12028        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
12029        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
12030        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
12031        assert_eq!(
12032            f.run(&[b"HRANDFIELD", b"h", b"3"]),
12033            "*1\r\n$1\r\na\r\n",
12034            "a positive count is capped at the size of the hash"
12035        );
12036        assert_eq!(
12037            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
12038            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
12039            "and a negative one repeats itself"
12040        );
12041        assert_eq!(
12042            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
12043            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
12044            "flat on RESP2"
12045        );
12046
12047        f.run(&[b"HELLO", b"3"]);
12048        assert_eq!(
12049            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
12050            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
12051            "and nested on RESP3, but still an array and never a map"
12052        );
12053    }
12054
12055    #[test]
12056    fn every_hash_command_says_wrongtype_and_writes_nothing() {
12057        let mut f = Fixture::new();
12058        f.run(&[b"SET", b"str", b"v"]);
12059        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
12060
12061        for cmd in [
12062            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
12063            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
12064            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
12065            &[b"HGET".as_slice(), b"str", b"f"][..],
12066            &[b"HMGET".as_slice(), b"str", b"f"][..],
12067            &[b"HDEL".as_slice(), b"str", b"f"][..],
12068            &[b"HLEN".as_slice(), b"str"][..],
12069            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
12070            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
12071            &[b"HGETALL".as_slice(), b"str"][..],
12072            &[b"HKEYS".as_slice(), b"str"][..],
12073            &[b"HVALS".as_slice(), b"str"][..],
12074            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
12075            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
12076            &[b"HRANDFIELD".as_slice(), b"str"][..],
12077            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
12078            &[b"HSCAN".as_slice(), b"str", b"0"][..],
12079        ] {
12080            let reply = f.run(cmd);
12081            assert_eq!(reply, wrong, "{:?}", cmd[0]);
12082        }
12083        assert_eq!(
12084            f.run(&[b"GET", b"str"]),
12085            "$1\r\nv\r\n",
12086            "and none of them touched the value"
12087        );
12088    }
12089
12090    #[test]
12091    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
12092        let mut f = Fixture::new();
12093        f.run(&[b"HSET", b"h", b"f", b"v"]);
12094        for bad in [
12095            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
12096            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
12097            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
12098            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
12099        ] {
12100            let reply = f.run(bad);
12101            assert!(reply.starts_with("-ERR"), "got {reply}");
12102            assert!(!reply.contains('*'), "an array header went out in front");
12103        }
12104    }
12105
12106    #[test]
12107    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
12108        let mut f = Fixture::new();
12109        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
12110        assert_eq!(
12111            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
12112            "*1\r\n:1\r\n"
12113        );
12114        assert_eq!(
12115            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
12116            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
12117            "one answer per field, and the two sentinels are TTL's own"
12118        );
12119
12120        // The same deadline in the other three units, all of them derived from
12121        // the one number the store kept.
12122        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
12123        assert!((99_000..=100_000).contains(&ms), "got {ms}");
12124        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
12125        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
12126        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
12127        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
12128
12129        assert_eq!(
12130            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
12131            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
12132            "one for the deadline taken off, and it does not say what it was"
12133        );
12134        assert_eq!(
12135            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12136            "*1\r\n:-1\r\n"
12137        );
12138        assert_eq!(
12139            f.run(&[b"HGET", b"h", b"a"]),
12140            "$1\r\n1\r\n",
12141            "and the field is still there with the value it had"
12142        );
12143    }
12144
12145    #[test]
12146    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
12147        let mut f = Fixture::new();
12148        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
12149        assert_eq!(
12150            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
12151            "*1\r\n:2\r\n",
12152            "two, and not one, because nothing was stored"
12153        );
12154        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
12155        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
12156
12157        assert_eq!(
12158            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
12159            "*1\r\n:2\r\n"
12160        );
12161        assert_eq!(
12162            f.run(&[b"EXISTS", b"h"]),
12163            ":0\r\n",
12164            "and the last field going took the key with it"
12165        );
12166
12167        // Zero is a delete and not an error, where minus one is an error. That
12168        // is Redis's split and it is easy to get backwards.
12169        f.run(&[b"HSET", b"h", b"a", b"1"]);
12170        assert_eq!(
12171            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
12172            "*1\r\n:2\r\n"
12173        );
12174    }
12175
12176    #[test]
12177    fn a_field_is_gone_once_its_moment_passes() {
12178        let mut f = Fixture::new();
12179        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
12180        assert_eq!(
12181            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
12182            "*1\r\n:1\r\n"
12183        );
12184        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
12185
12186        // Time moves once per turn of the event loop and nowhere else, so a
12187        // test moves it by hand rather than by sleeping. There is nothing to
12188        // sleep for: the deadline is a number and so is the clock.
12189        f.server.advance_clock_ms(60);
12190        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
12191        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
12192        assert_eq!(
12193            f.run(&[b"HGETALL", b"h"]),
12194            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
12195            "and the walks do not hand back a field that has expired"
12196        );
12197    }
12198
12199    #[test]
12200    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
12201        let mut f = Fixture::new();
12202        for cmd in [
12203            &[
12204                b"HEXPIRE".as_slice(),
12205                b"nokey",
12206                b"100",
12207                b"FIELDS",
12208                b"2",
12209                b"a",
12210                b"b",
12211            ][..],
12212            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
12213            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
12214            &[
12215                b"HEXPIRETIME".as_slice(),
12216                b"nokey",
12217                b"FIELDS",
12218                b"2",
12219                b"a",
12220                b"b",
12221            ][..],
12222            &[
12223                b"HPERSIST".as_slice(),
12224                b"nokey",
12225                b"FIELDS",
12226                b"2",
12227                b"a",
12228                b"b",
12229            ][..],
12230        ] {
12231            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
12232        }
12233    }
12234
12235    #[test]
12236    fn writing_a_field_clears_the_deadline_that_was_on_it() {
12237        let mut f = Fixture::new();
12238        f.run(&[b"HSET", b"h", b"a", b"1"]);
12239        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
12240        f.run(&[b"HSET", b"h", b"a", b"2"]);
12241        assert_eq!(
12242            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12243            "*1\r\n:-1\r\n",
12244            "Redis has done this since 7.4, and it is why HGETEX exists"
12245        );
12246    }
12247
12248    #[test]
12249    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
12250        let mut f = Fixture::new();
12251        f.run(&[b"HSET", b"h", b"a", b"1"]);
12252        assert_eq!(
12253            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
12254            "*1\r\n:0\r\n",
12255            "XX on a field with no deadline changes nothing"
12256        );
12257        assert_eq!(
12258            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
12259            "*1\r\n:1\r\n"
12260        );
12261        assert_eq!(
12262            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
12263            "*1\r\n:0\r\n",
12264            "and NX will not move one that is already there"
12265        );
12266        assert_eq!(
12267            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
12268            "*1\r\n:0\r\n"
12269        );
12270        assert_eq!(
12271            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
12272            "*1\r\n:1\r\n"
12273        );
12274        assert_eq!(
12275            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
12276            "*1\r\n:1\r\n"
12277        );
12278        assert_eq!(
12279            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12280            "*1\r\n:50\r\n"
12281        );
12282    }
12283
12284    #[test]
12285    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
12286        let mut f = Fixture::new();
12287        f.run(&[b"HSET", b"h", b"a", b"1"]);
12288        for (bad, want) in [
12289            (
12290                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
12291                "-ERR invalid expire time, must be >= 0",
12292            ),
12293            (
12294                &[
12295                    b"HEXPIRE".as_slice(),
12296                    b"h",
12297                    b"9999999999999999",
12298                    b"FIELDS",
12299                    b"1",
12300                    b"a",
12301                ][..],
12302                "-ERR invalid expire time in 'hexpire' command",
12303            ),
12304            (
12305                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
12306                "-ERR wrong number of arguments for 'hexpire' command",
12307            ),
12308            (
12309                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
12310                "-ERR Parameter `numFields` should be greater than 0",
12311            ),
12312            (
12313                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
12314                "-ERR wrong number of arguments",
12315            ),
12316            (
12317                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
12318                "-ERR wrong number of arguments",
12319            ),
12320        ] {
12321            let reply = f.run(bad);
12322            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
12323            assert!(!reply.contains('*'), "an array header went out in front");
12324        }
12325        assert_eq!(
12326            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12327            "*1\r\n:-1\r\n",
12328            "and not one of them put a deadline on anything"
12329        );
12330    }
12331
12332    #[test]
12333    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
12334        let mut f = Fixture::new();
12335        f.run(&[b"SET", b"str", b"v"]);
12336        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
12337
12338        for cmd in [
12339            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
12340            &[
12341                b"HPEXPIRE".as_slice(),
12342                b"str",
12343                b"100",
12344                b"FIELDS",
12345                b"1",
12346                b"f",
12347            ][..],
12348            &[
12349                b"HEXPIREAT".as_slice(),
12350                b"str",
12351                b"9999999999",
12352                b"FIELDS",
12353                b"1",
12354                b"f",
12355            ][..],
12356            &[
12357                b"HPEXPIREAT".as_slice(),
12358                b"str",
12359                b"9999999999999",
12360                b"FIELDS",
12361                b"1",
12362                b"f",
12363            ][..],
12364            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
12365            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
12366            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
12367            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
12368            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
12369        ] {
12370            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
12371        }
12372        assert_eq!(
12373            f.run(&[b"GET", b"str"]),
12374            "$1\r\nv\r\n",
12375            "and none of them touched the value"
12376        );
12377    }
12378
12379    #[test]
12380    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
12381        let mut f = Fixture::new();
12382        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
12383        assert_eq!(
12384            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
12385            "*2\r\n$1\r\n1\r\n$-1\r\n",
12386            "positional, so the field that was not there is a nil in its place"
12387        );
12388        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
12389        assert_eq!(
12390            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
12391            "*1\r\n$-1\r\n"
12392        );
12393        assert_eq!(
12394            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
12395            "*1\r\n$1\r\n2\r\n"
12396        );
12397        assert_eq!(
12398            f.run(&[b"EXISTS", b"h"]),
12399            ":0\r\n",
12400            "and the last field took the key"
12401        );
12402    }
12403
12404    #[test]
12405    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
12406        let mut f = Fixture::new();
12407        f.run(&[b"HSET", b"h", b"a", b"1"]);
12408        assert_eq!(
12409            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
12410            "*1\r\n$1\r\n1\r\n"
12411        );
12412        assert_eq!(
12413            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12414            "*1\r\n:-1\r\n",
12415            "no option means leave it alone, which is the one place this is not GETEX"
12416        );
12417
12418        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
12419        assert_eq!(
12420            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12421            "*1\r\n:100\r\n"
12422        );
12423        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
12424        assert_eq!(
12425            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12426            "*1\r\n:100\r\n",
12427            "and a plain read really does leave it alone"
12428        );
12429        assert_eq!(
12430            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
12431            "*1\r\n$1\r\n1\r\n"
12432        );
12433        assert_eq!(
12434            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12435            "*1\r\n:-1\r\n"
12436        );
12437
12438        assert_eq!(
12439            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
12440            "*1\r\n$1\r\n1\r\n",
12441            "the value goes out before the deadline that has already gone is applied"
12442        );
12443        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
12444        assert_eq!(
12445            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
12446            "*1\r\n$-1\r\n"
12447        );
12448    }
12449
12450    #[test]
12451    fn hsetex_writes_all_of_it_or_none_of_it() {
12452        let mut f = Fixture::new();
12453        assert_eq!(
12454            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
12455            ":1\r\n"
12456        );
12457        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
12458        assert_eq!(
12459            f.run(&[
12460                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
12461            ]),
12462            ":0\r\n",
12463            "FNX wants every field named to be missing"
12464        );
12465        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
12466        assert_eq!(
12467            f.run(&[b"HEXISTS", b"h", b"new"]),
12468            ":0\r\n",
12469            "and none of the list was written"
12470        );
12471        assert_eq!(
12472            f.run(&[
12473                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
12474            ]),
12475            ":0\r\n",
12476            "and FXX wants every one of them to be there"
12477        );
12478        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
12479        assert_eq!(
12480            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
12481            ":1\r\n"
12482        );
12483        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
12484
12485        assert_eq!(
12486            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
12487            ":0\r\n"
12488        );
12489        assert_eq!(
12490            f.run(&[b"EXISTS", b"gone"]),
12491            ":0\r\n",
12492            "a key with no fields cannot meet FXX and is not created trying"
12493        );
12494    }
12495
12496    #[test]
12497    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
12498        let mut f = Fixture::new();
12499        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
12500        assert_eq!(
12501            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12502            "*1\r\n:100\r\n"
12503        );
12504
12505        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
12506        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
12507        assert_eq!(
12508            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12509            "*1\r\n:100\r\n",
12510            "KEEPTTL put back what the write cleared"
12511        );
12512
12513        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
12514        assert_eq!(
12515            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12516            "*1\r\n:-1\r\n",
12517            "and without it a write clears the deadline the way HSET does"
12518        );
12519
12520        // Any order, because Redis reads these in a loop and not in a fixed
12521        // sequence.
12522        assert_eq!(
12523            f.run(&[
12524                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
12525            ]),
12526            ":1\r\n"
12527        );
12528        assert_eq!(
12529            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12530            "*1\r\n:100\r\n"
12531        );
12532
12533        assert_eq!(
12534            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
12535            ":1\r\n",
12536            "written, and not the separate code the HEXPIRE family has for this"
12537        );
12538        assert_eq!(
12539            f.run(&[b"EXISTS", b"h"]),
12540            ":0\r\n",
12541            "and storing it and then removing it emptied the hash"
12542        );
12543    }
12544
12545    #[test]
12546    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
12547        let mut f = Fixture::new();
12548        f.run(&[b"HSET", b"h", b"a", b"1"]);
12549        for (bad, want) in [
12550            // HGETDEL has three sentences of its own for these three mistakes.
12551            (
12552                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
12553                "-ERR Number of fields must be a positive integer",
12554            ),
12555            (
12556                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
12557                "-ERR The `numfields` parameter must match the number of arguments",
12558            ),
12559            (
12560                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
12561                "-ERR Mandatory argument FIELDS is missing or not at the right position",
12562            ),
12563            // And HGETEX and HSETEX have three different ones between them.
12564            (
12565                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
12566                "-ERR invalid number of fields",
12567            ),
12568            (
12569                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
12570                "-ERR wrong number of arguments",
12571            ),
12572            (
12573                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
12574                "-ERR unknown argument: FIELD",
12575            ),
12576            (
12577                &[
12578                    b"HGETEX".as_slice(),
12579                    b"h",
12580                    b"KEEPTTL",
12581                    b"FIELDS",
12582                    b"1",
12583                    b"a",
12584                ][..],
12585                "-ERR unknown argument: KEEPTTL",
12586            ),
12587            (
12588                &[
12589                    b"HGETEX".as_slice(),
12590                    b"h",
12591                    b"EX",
12592                    b"100",
12593                    b"PERSIST",
12594                    b"FIELDS",
12595                    b"1",
12596                    b"a",
12597                ][..],
12598                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
12599            ),
12600            (
12601                &[
12602                    b"HSETEX".as_slice(),
12603                    b"h",
12604                    b"EX",
12605                    b"1",
12606                    b"KEEPTTL",
12607                    b"FIELDS",
12608                    b"1",
12609                    b"a",
12610                    b"1",
12611                ][..],
12612                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
12613            ),
12614            (
12615                &[
12616                    b"HSETEX".as_slice(),
12617                    b"h",
12618                    b"FNX",
12619                    b"FXX",
12620                    b"FIELDS",
12621                    b"1",
12622                    b"a",
12623                    b"1",
12624                ][..],
12625                "-ERR Only one of FXX or FNX arguments can be specified",
12626            ),
12627            (
12628                &[
12629                    b"HSETEX".as_slice(),
12630                    b"h",
12631                    b"FIELDS",
12632                    b"2",
12633                    b"a",
12634                    b"1",
12635                    b"b",
12636                ][..],
12637                "-ERR wrong number of arguments",
12638            ),
12639            (
12640                &[
12641                    b"HGETEX".as_slice(),
12642                    b"h",
12643                    b"EX",
12644                    b"-1",
12645                    b"FIELDS",
12646                    b"1",
12647                    b"a",
12648                ][..],
12649                "-ERR invalid expire time, must be >= 0",
12650            ),
12651            (
12652                &[
12653                    b"HGETEX".as_slice(),
12654                    b"h",
12655                    b"PXAT",
12656                    b"99999999999999",
12657                    b"FIELDS",
12658                    b"1",
12659                    b"a",
12660                ][..],
12661                "-ERR invalid expire time in 'hgetex' command",
12662            ),
12663            (
12664                &[
12665                    b"HSETEX".as_slice(),
12666                    b"h",
12667                    b"EX",
12668                    b"abc",
12669                    b"FIELDS",
12670                    b"1",
12671                    b"a",
12672                    b"1",
12673                ][..],
12674                "-ERR value is not an integer or out of range",
12675            ),
12676        ] {
12677            let reply = f.run(bad);
12678            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
12679            assert!(!reply.contains('*'), "an array header went out in front");
12680        }
12681        assert_eq!(
12682            f.run(&[b"HGET", b"h", b"a"]),
12683            "$1\r\n1\r\n",
12684            "and not one of them wrote anything"
12685        );
12686        assert_eq!(
12687            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12688            "*1\r\n:-1\r\n"
12689        );
12690    }
12691
12692    #[test]
12693    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
12694        let mut f = Fixture::new();
12695        f.run(&[b"SET", b"str", b"v"]);
12696        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
12697        for cmd in [
12698            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
12699            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
12700            &[
12701                b"HGETEX".as_slice(),
12702                b"str",
12703                b"EX",
12704                b"100",
12705                b"FIELDS",
12706                b"1",
12707                b"f",
12708            ][..],
12709            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
12710        ] {
12711            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
12712        }
12713        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
12714    }
12715
12716    /// The two orders `HIMPORT` juggles, which are not the same order.
12717    ///
12718    /// Values arrive in the order the fields were declared in and the hash is
12719    /// built in sorted order, so the first value is not generally the first
12720    /// field. And the sort is by length before bytes, which nothing else here
12721    /// sorts names with: `b` comes before `aa` where a plain byte comparison
12722    /// would put `aa` first. Both read off 8.10.1.
12723    #[test]
12724    fn himport_writes_declared_values_into_sorted_fields() {
12725        let mut f = Fixture::new();
12726        assert_eq!(
12727            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
12728            "+OK\r\n"
12729        );
12730        assert_eq!(
12731            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
12732            "+OK\r\n"
12733        );
12734        assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
12735        assert_eq!(
12736            f.run(&[b"HGETALL", b"k"]),
12737            bulks(&["a", "3", "b", "1", "aa", "2"])
12738        );
12739    }
12740
12741    /// It replaces the key rather than writing over it, so a field the fieldset
12742    /// does not name is gone afterwards and so is the deadline.
12743    #[test]
12744    fn himport_set_replaces_the_whole_key() {
12745        let mut f = Fixture::new();
12746        f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
12747        f.run(&[b"EXPIRE", b"k", b"100"]);
12748        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
12749        assert_eq!(
12750            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
12751            "+OK\r\n"
12752        );
12753        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
12754        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
12755    }
12756
12757    /// A fieldset is connection state. `SELECT` leaves them alone and `RESET`
12758    /// throws them away, and a key built from one outlives it.
12759    #[test]
12760    fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
12761        let mut f = Fixture::new();
12762        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
12763        f.run(&[b"SELECT", b"1"]);
12764        assert_eq!(
12765            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
12766            "+OK\r\n"
12767        );
12768        f.run(&[b"SELECT", b"0"]);
12769        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
12770        assert_eq!(
12771            f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
12772            "-ERR no such fieldset\r\n"
12773        );
12774    }
12775
12776    /// Which complaint wins when a line is wrong in more than one place.
12777    ///
12778    /// The type of the key beats both of the others, so a `HIMPORT SET` against
12779    /// a string is a WRONGTYPE even when the fieldset is missing too, which is
12780    /// the ordering a real server has and not the one the argument order
12781    /// suggests.
12782    #[test]
12783    fn himport_complains_in_the_order_a_real_server_does() {
12784        let mut f = Fixture::new();
12785        f.run(&[b"SET", b"str", b"v"]);
12786        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
12787        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
12788        assert_eq!(
12789            f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
12790            wrong,
12791            "the type beats a missing fieldset"
12792        );
12793        assert_eq!(
12794            f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
12795            wrong,
12796            "and it beats a value count that does not fit"
12797        );
12798        assert_eq!(
12799            f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
12800            "-ERR no such fieldset\r\n"
12801        );
12802        // One sentence for too few and for too many alike.
12803        for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
12804            let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
12805            line.extend_from_slice(values);
12806            assert_eq!(
12807                f.run(&line),
12808                "-ERR value count does not match fieldset field count\r\n",
12809                "{} values into two fields",
12810                values.len()
12811            );
12812        }
12813        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
12814    }
12815
12816    /// The arity of each subcommand, and the unknown one.
12817    #[test]
12818    fn himport_checks_each_subcommand_count_under_its_own_name() {
12819        let mut f = Fixture::new();
12820        assert_eq!(
12821            f.run(&[b"HIMPORT"]),
12822            "-ERR wrong number of arguments for 'himport' command\r\n"
12823        );
12824        for (rest, name) in [
12825            (&["PREPARE"][..], "prepare"),
12826            (&["PREPARE", "fs"][..], "prepare"),
12827            (&["SET"][..], "set"),
12828            (&["SET", "k"][..], "set"),
12829            (&["SET", "k", "fs"][..], "set"),
12830            (&["DISCARD"][..], "discard"),
12831            (&["DISCARD", "a", "b"][..], "discard"),
12832            (&["DISCARDALL", "x"][..], "discardall"),
12833        ] {
12834            let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
12835            line.extend(rest.iter().map(|a| a.as_bytes()));
12836            assert_eq!(
12837                f.run(&line),
12838                format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
12839                "HIMPORT {}",
12840                rest.join(" ")
12841            );
12842        }
12843        assert_eq!(
12844            f.run(&[b"HIMPORT", b"NOPE", b"x"]),
12845            "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
12846        );
12847    }
12848
12849    /// A `PREPARE` that fails leaves the name pointing where it pointed, which
12850    /// is the answer of the two that could not be guessed from outside.
12851    #[test]
12852    fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
12853        let mut f = Fixture::new();
12854        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
12855        assert_eq!(
12856            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
12857            "-ERR duplicate field name in fieldset\r\n"
12858        );
12859        assert_eq!(
12860            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
12861            "+OK\r\n"
12862        );
12863        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
12864    }
12865
12866    /// Preparing the same name twice replaces it, and the two discards count
12867    /// what they took rather than answering OK.
12868    #[test]
12869    fn himport_prepare_replaces_and_the_discards_count() {
12870        let mut f = Fixture::new();
12871        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
12872        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
12873        assert_eq!(
12874            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
12875            "+OK\r\n"
12876        );
12877        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
12878
12879        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
12880        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
12881        f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
12882        f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
12883        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
12884        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
12885    }
12886
12887    /// The one integer of a single element array reply.
12888    /// The number out of a plain integer reply.
12889    ///
12890    /// [`int_reply`] is the same thing wrapped in a one element array, which is
12891    /// the shape every hash field command answers in.
12892    fn int(reply: &str) -> i64 {
12893        let body = reply
12894            .strip_prefix(':')
12895            .and_then(|s| s.strip_suffix("\r\n"))
12896            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
12897        body.parse().expect("an integer")
12898    }
12899
12900    fn int_reply(reply: &str) -> i64 {
12901        let body = reply
12902            .strip_prefix("*1\r\n:")
12903            .and_then(|s| s.strip_suffix("\r\n"))
12904            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
12905        body.parse().expect("an integer")
12906    }
12907
12908    /// The cursor and the flat items of a scan reply.
12909    fn scan_reply(reply: &str) -> (String, Vec<String>) {
12910        let mut lines = reply.split("\r\n");
12911        assert_eq!(lines.next(), Some("*2"), "got {reply}");
12912        lines.next().expect("the cursor header");
12913        let cursor = lines.next().expect("a cursor").to_owned();
12914        let header = lines.next().expect("an item count");
12915        let n: usize = header[1..].parse().expect("a count");
12916        let mut items = Vec::with_capacity(n);
12917        for _ in 0..n {
12918            lines.next().expect("an item header");
12919            items.push(lines.next().expect("an item").to_owned());
12920        }
12921        (cursor, items)
12922    }
12923
12924    /// The members of a set reply, sorted, since none of these promise an
12925    /// order and a test that asserted one would be asserting an accident.
12926    fn sorted(reply: &str) -> Vec<String> {
12927        let mut lines = reply.split("\r\n");
12928        let header = lines.next().expect("a header");
12929        assert!(
12930            header.starts_with('*') || header.starts_with('~'),
12931            "got {reply}"
12932        );
12933        let n: usize = header[1..].parse().expect("a member count");
12934        let mut got = Vec::with_capacity(n);
12935        for _ in 0..n {
12936            lines.next().expect("a member header");
12937            got.push(lines.next().expect("a member").to_owned());
12938        }
12939        got.sort();
12940        got
12941    }
12942
12943    #[test]
12944    fn the_algebra_answers_what_the_sets_share_and_do_not() {
12945        let mut f = Fixture::new();
12946        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
12947        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
12948        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
12949
12950        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
12951        assert_eq!(
12952            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
12953            ["1", "2", "3", "4", "5"]
12954        );
12955        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
12956        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
12957
12958        // A key that is not there is an empty set, which empties an
12959        // intersection and does nothing at all to a union.
12960        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
12961        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
12962        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
12963        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
12964    }
12965
12966    #[test]
12967    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
12968        let mut f = Fixture::new();
12969        f.run(&[b"SADD", b"a", b"x"]);
12970        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
12971        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
12972        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
12973
12974        f.run(&[b"HELLO", b"3"]);
12975        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
12976        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
12977        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
12978        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
12979    }
12980
12981    #[test]
12982    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
12983        let mut f = Fixture::new();
12984        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
12985        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
12986
12987        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
12988        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
12989        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
12990        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
12991        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
12992        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
12993
12994        // An empty answer deletes the destination rather than leaving an empty
12995        // set behind, and the destination may be one of the sources.
12996        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
12997        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
12998        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
12999        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
13000
13001        // And a destination holding something else is overwritten, the same way
13002        // SET overwrites, rather than refused.
13003        f.run(&[b"SET", b"str", b"v"]);
13004        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
13005        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
13006    }
13007
13008    #[test]
13009    fn sintercard_counts_without_building_and_stops_at_a_limit() {
13010        let mut f = Fixture::new();
13011        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
13012        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
13013
13014        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
13015        assert_eq!(
13016            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
13017            ":2\r\n"
13018        );
13019        assert_eq!(
13020            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
13021            ":3\r\n",
13022            "a limit of zero is no limit"
13023        );
13024        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
13025        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
13026
13027        // The counted keys are what make its three error messages its own.
13028        assert_eq!(
13029            f.run(&[b"SINTERCARD", b"0", b"a"]),
13030            "-ERR numkeys should be greater than 0\r\n"
13031        );
13032        assert_eq!(
13033            f.run(&[b"SINTERCARD", b"abc", b"a"]),
13034            "-ERR numkeys should be greater than 0\r\n"
13035        );
13036        assert_eq!(
13037            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
13038            "-ERR Number of keys can't be greater than number of args\r\n"
13039        );
13040        assert_eq!(
13041            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
13042            "-ERR LIMIT can't be negative\r\n"
13043        );
13044        assert_eq!(
13045            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
13046            "-ERR syntax error\r\n"
13047        );
13048        // A key really can be called LIMIT, which is why the count exists.
13049        f.run(&[b"SADD", b"LIMIT", b"2"]);
13050        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
13051    }
13052
13053    /// The two Redis 8.10 added, which are SINTERCARD's shape over a union and
13054    /// over a difference. Every number here was read off 8.10.1 first.
13055    #[test]
13056    fn sunioncard_and_sdiffcard_count_without_building() {
13057        let mut f = Fixture::new();
13058        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
13059        f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
13060
13061        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
13062        assert_eq!(
13063            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
13064            ":2\r\n"
13065        );
13066        assert_eq!(
13067            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
13068            ":6\r\n",
13069            "a limit of zero is no limit"
13070        );
13071        assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
13072        assert_eq!(
13073            f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
13074            ":4\r\n",
13075            "a missing key adds nothing to a union"
13076        );
13077
13078        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
13079        assert_eq!(
13080            f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
13081            ":1\r\n"
13082        );
13083        assert_eq!(
13084            f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
13085            ":2\r\n",
13086            "a difference is not symmetric"
13087        );
13088        assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
13089        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
13090        assert_eq!(
13091            f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
13092            ":0\r\n",
13093            "nothing taken away from nothing"
13094        );
13095
13096        // The same three messages SINTERCARD has, because the line is the same
13097        // line and is parsed once for all three.
13098        for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
13099            assert_eq!(
13100                f.run(&[name, b"0", b"a"]),
13101                "-ERR numkeys should be greater than 0\r\n"
13102            );
13103            assert_eq!(
13104                f.run(&[name, b"abc", b"a"]),
13105                "-ERR numkeys should be greater than 0\r\n"
13106            );
13107            assert_eq!(
13108                f.run(&[name, b"-1", b"a"]),
13109                "-ERR numkeys should be greater than 0\r\n"
13110            );
13111            assert_eq!(
13112                f.run(&[name, b"3", b"a", b"b"]),
13113                "-ERR Number of keys can't be greater than number of args\r\n"
13114            );
13115            assert_eq!(
13116                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
13117                "-ERR LIMIT can't be negative\r\n"
13118            );
13119            assert_eq!(
13120                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
13121                "-ERR LIMIT can't be negative\r\n",
13122                "a LIMIT that is not a number gets the negative message too"
13123            );
13124            assert_eq!(
13125                f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
13126                "-ERR syntax error\r\n"
13127            );
13128            assert_eq!(
13129                f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
13130                "-ERR syntax error\r\n"
13131            );
13132            assert_eq!(
13133                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
13134                "-ERR syntax error\r\n"
13135            );
13136        }
13137
13138        // And a key called LIMIT is a key, here as much as on SINTERCARD.
13139        f.run(&[b"SADD", b"LIMIT", b"2"]);
13140        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
13141        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
13142    }
13143
13144    #[test]
13145    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
13146        let mut f = Fixture::new();
13147        f.run(&[b"SADD", b"a", b"1"]);
13148        f.run(&[b"SADD", b"d", b"old"]);
13149        f.run(&[b"SET", b"str", b"v"]);
13150
13151        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
13152        for bad in [
13153            &[b"SINTER".as_slice(), b"a", b"str"][..],
13154            &[b"SUNION".as_slice(), b"str"][..],
13155            &[b"SDIFF".as_slice(), b"a", b"str"][..],
13156            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
13157            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
13158            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
13159            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
13160        ] {
13161            let reply = f.run(bad);
13162            assert_eq!(reply, wrong, "for {:?}", bad[0]);
13163        }
13164        assert_eq!(
13165            f.run(&[b"SMEMBERS", b"d"]),
13166            "*1\r\n$3\r\nold\r\n",
13167            "and the destination was left alone every time"
13168        );
13169    }
13170
13171    /// The leak a set can spring that nothing on the wire would ever show: the
13172    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
13173    /// Not under Miri. What this claims is that memory does not grow over two
13174    /// hundred passes, so the passes are the claim rather than the way it
13175    /// happens to be written, and two hundred passes of a two hundred member
13176    /// collection is forty thousand trips through dispatch, which is what an
13177    /// interpreter charges for. A count small enough to run there would leave a
13178    /// server that reclaims nothing inside the bound and the test would pass on
13179    /// a leak. Nothing about memory safety goes uninterpreted either way: this
13180    /// is an accounting claim, and the same commands are run a few at a time by
13181    /// the tests around it.
13182    #[cfg_attr(miri, ignore = "the volume is the claim")]
13183    #[test]
13184    fn churning_sets_does_not_grow_the_server() {
13185        let mut f = Fixture::new();
13186        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
13187        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
13188            .chain(std::iter::once(&b"s"[..]))
13189            .chain(members.iter().map(Vec::as_slice))
13190            .collect();
13191
13192        f.run(&args);
13193        f.run(&[b"DEL", b"s"]);
13194        f.server.compact_step();
13195        let after_first = f.server.memory_bytes();
13196
13197        for _ in 0..200 {
13198            f.run(&args);
13199            f.run(&[b"DEL", b"s"]);
13200            f.server.compact_step();
13201        }
13202        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
13203        assert!(
13204            f.server.memory_bytes() <= after_first * 2,
13205            "held {} after two hundred passes against {after_first} after one",
13206            f.server.memory_bytes()
13207        );
13208    }
13209
13210    // --------------------------------------------------------------- bitmaps
13211
13212    /// The two single bit commands, and the encoding rule underneath them.
13213    ///
13214    /// A write always leaves the value `raw` and a read never re-encodes, which
13215    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
13216    /// with its first digit changed after a `SETBIT`.
13217    #[test]
13218    fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
13219        let mut f = Fixture::new();
13220        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
13221        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
13222        assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
13223        assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
13224        assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
13225        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
13226
13227        // Writing a nought past the end still creates the key and still pads.
13228        assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
13229        assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
13230        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
13231
13232        f.run(&[b"SET", b"num", b"12345"]);
13233        assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
13234        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
13235        assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
13236        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
13237        assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
13238    }
13239
13240    /// Counting, in bytes and in bits.
13241    ///
13242    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
13243    /// says 22 for it. The server is the thing being copied here.
13244    #[test]
13245    fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
13246        let mut f = Fixture::new();
13247        f.run(&[b"SET", b"mykey", b"foobar"]);
13248        assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
13249        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
13250        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
13251        assert_eq!(
13252            f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
13253            ":6\r\n"
13254        );
13255        assert_eq!(
13256            f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
13257            ":25\r\n"
13258        );
13259        assert_eq!(
13260            f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
13261            ":17\r\n"
13262        );
13263        assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
13264
13265        // A start past the end is left where it is and the end is pulled back,
13266        // so the range comes out backwards and counts nothing.
13267        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
13268
13269        // A lone start is a syntax error here, where BITPOS allows it.
13270        assert_eq!(
13271            f.run(&[b"BITCOUNT", b"mykey", b"0"]),
13272            "-ERR syntax error\r\n"
13273        );
13274        assert_eq!(
13275            f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
13276            "-ERR syntax error\r\n"
13277        );
13278    }
13279
13280    /// Searching, and the one place a miss is not minus one.
13281    ///
13282    /// A search for a nought that runs to the end of the string answers the
13283    /// length in bits, because the string is treated as if it had noughts after
13284    /// it forever. Give it an explicit end and it answers minus one instead.
13285    #[test]
13286    fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
13287        let mut f = Fixture::new();
13288        f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
13289        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
13290        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
13291        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
13292        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
13293        assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
13294
13295        f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
13296        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
13297        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
13298        assert_eq!(
13299            f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
13300            ":8\r\n"
13301        );
13302
13303        // A missing key is all noughts, so a one is never found and a nought is
13304        // at position zero.
13305        assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
13306        assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
13307    }
13308
13309    /// The eight operations, with the answers a real server gives for them.
13310    #[test]
13311    fn the_eight_combinations_write_what_a_real_server_writes() {
13312        let mut f = Fixture::new();
13313        f.run(&[b"SET", b"a", b"abc"]);
13314        f.run(&[b"SET", b"b", b"abd"]);
13315        let cases: &[(&[u8], &str)] = &[
13316            (b"AND", "ab`"),
13317            (b"OR", "abg"),
13318            (b"XOR", "\u{0}\u{0}\u{7}"),
13319            (b"DIFF", "\u{0}\u{0}\u{3}"),
13320            (b"DIFF1", "\u{0}\u{0}\u{4}"),
13321            (b"ANDOR", "ab`"),
13322            (b"ONE", "\u{0}\u{0}\u{7}"),
13323        ];
13324        for (op, want) in cases {
13325            assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
13326            assert_eq!(
13327                f.run(&[b"GET", b"d"]),
13328                format!("$3\r\n{want}\r\n"),
13329                "{op:?}"
13330            );
13331        }
13332        // The one whose answer is not text, so it is compared as bytes.
13333        assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
13334        assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
13335
13336        // A missing source is a string of noughts as long as it needs to be, so
13337        // an AND against one writes three zero bytes rather than nothing.
13338        assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
13339        assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
13340
13341        // Every source missing is an empty result, and an empty result takes
13342        // the destination with it.
13343        f.run(&[b"SET", b"dest", b"x"]);
13344        assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
13345        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
13346    }
13347
13348    /// What `BITOP` says when it is asked for something it cannot do.
13349    #[test]
13350    fn bitop_names_the_operation_in_its_own_complaints() {
13351        let mut f = Fixture::new();
13352        f.run(&[b"SET", b"a", b"abc"]);
13353        assert_eq!(
13354            f.run(&[b"BITOP", b"nope", b"d", b"a"]),
13355            "-ERR syntax error\r\n"
13356        );
13357        assert_eq!(
13358            f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
13359            "-ERR BITOP NOT must be called with a single source key.\r\n"
13360        );
13361        for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
13362            assert_eq!(
13363                f.run(&[b"BITOP", op, b"d", b"a"]),
13364                format!(
13365                    "-ERR BITOP {} must be called with at least two source keys.\r\n",
13366                    String::from_utf8_lossy(op)
13367                )
13368            );
13369        }
13370        f.run(&[b"LPUSH", b"l", b"x"]);
13371        assert_eq!(
13372            f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
13373            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
13374        );
13375    }
13376
13377    /// Packed fields, the three overflow policies and the `#` offset.
13378    #[test]
13379    fn bitfield_reads_and_writes_packed_fields() {
13380        let mut f = Fixture::new();
13381        assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
13382        assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
13383
13384        assert_eq!(
13385            f.run(&[
13386                b"BITFIELD",
13387                b"bf",
13388                b"INCRBY",
13389                b"u2",
13390                b"100",
13391                b"1",
13392                b"GET",
13393                b"u4",
13394                b"0"
13395            ]),
13396            "*2\r\n:1\r\n:0\r\n"
13397        );
13398        // The field at bit 100 is two bits wide, so it ends in the thirteenth
13399        // byte and the value grew to thirteen bytes to hold it.
13400        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
13401
13402        // A `#` offset counts in fields rather than in bits.
13403        assert_eq!(
13404            f.run(&[
13405                b"BITFIELD",
13406                b"bf",
13407                b"SET",
13408                b"u8",
13409                b"#0",
13410                b"255",
13411                b"GET",
13412                b"u8",
13413                b"#0"
13414            ]),
13415            "*2\r\n:0\r\n:255\r\n"
13416        );
13417
13418        assert_eq!(
13419            f.run(&[
13420                b"BITFIELD",
13421                b"bf",
13422                b"OVERFLOW",
13423                b"SAT",
13424                b"INCRBY",
13425                b"i8",
13426                b"0",
13427                b"120",
13428                b"INCRBY",
13429                b"i8",
13430                b"0",
13431                b"120"
13432            ]),
13433            "*2\r\n:119\r\n:127\r\n"
13434        );
13435        assert_eq!(
13436            f.run(&[
13437                b"BITFIELD",
13438                b"bf2",
13439                b"OVERFLOW",
13440                b"FAIL",
13441                b"INCRBY",
13442                b"u2",
13443                b"0",
13444                b"5"
13445            ]),
13446            "*1\r\n$-1\r\n"
13447        );
13448        assert_eq!(
13449            f.run(&[
13450                b"BITFIELD",
13451                b"bf3",
13452                b"OVERFLOW",
13453                b"WRAP",
13454                b"INCRBY",
13455                b"u2",
13456                b"0",
13457                b"5"
13458            ]),
13459            "*1\r\n:1\r\n"
13460        );
13461        assert_eq!(
13462            f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
13463            "*1\r\n:4611686018427387904\r\n"
13464        );
13465    }
13466
13467    /// A bad subcommand anywhere in the line stops all of it.
13468    ///
13469    /// Redis checks the whole argument list before it runs any of it, so the
13470    /// `SET` in front of the bad type here never happens and the key it would
13471    /// have created is not there afterwards.
13472    #[test]
13473    fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
13474        let mut f = Fixture::new();
13475        let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
13476        assert_eq!(
13477            f.run(&[
13478                b"BITFIELD",
13479                b"bad",
13480                b"SET",
13481                b"u8",
13482                b"0",
13483                b"1",
13484                b"GET",
13485                b"u99",
13486                b"0"
13487            ]),
13488            bad_type
13489        );
13490        assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
13491        assert_eq!(
13492            f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
13493            bad_type
13494        );
13495        assert_eq!(
13496            f.run(&[b"BITFIELD", b"bad", b"GET"]),
13497            "-ERR syntax error\r\n"
13498        );
13499        assert_eq!(
13500            f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
13501            "-ERR syntax error\r\n"
13502        );
13503        assert_eq!(
13504            f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
13505            "-ERR syntax error\r\n"
13506        );
13507        assert_eq!(
13508            f.run(&[
13509                b"BITFIELD",
13510                b"bad",
13511                b"OVERFLOW",
13512                b"NOPE",
13513                b"GET",
13514                b"u8",
13515                b"0"
13516            ]),
13517            "-ERR Invalid OVERFLOW type specified\r\n"
13518        );
13519        assert_eq!(
13520            f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
13521            "-ERR value is not an integer or out of range\r\n"
13522        );
13523        for at in [&b"#-1"[..], b"abc"] {
13524            assert_eq!(
13525                f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
13526                "-ERR bit offset is not an integer or out of range\r\n"
13527            );
13528        }
13529    }
13530
13531    /// The read only twin reads, refuses to write, and creates nothing.
13532    #[test]
13533    fn bitfield_ro_answers_gets_and_refuses_the_rest() {
13534        let mut f = Fixture::new();
13535        f.run(&[b"SET", b"n", b"123"]);
13536        assert_eq!(
13537            f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
13538            "*1\r\n:49\r\n"
13539        );
13540        // A read does not unpack an int the way a write does.
13541        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
13542
13543        // An OVERFLOW word is allowed even though nothing here can overflow.
13544        assert_eq!(
13545            f.run(&[
13546                b"BITFIELD_RO",
13547                b"n",
13548                b"OVERFLOW",
13549                b"SAT",
13550                b"GET",
13551                b"u8",
13552                b"0"
13553            ]),
13554            "*1\r\n:49\r\n"
13555        );
13556        for sub in [&b"SET"[..], b"INCRBY"] {
13557            assert_eq!(
13558                f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
13559                "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
13560            );
13561        }
13562
13563        assert_eq!(
13564            f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
13565            "*1\r\n:0\r\n"
13566        );
13567        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13568    }
13569
13570    /// The offsets a bitmap command will not take.
13571    #[test]
13572    fn an_offset_off_the_end_of_the_world_is_refused() {
13573        let mut f = Fixture::new();
13574        let bad = "-ERR bit offset is not an integer or out of range\r\n";
13575        for arg in [&b"abc"[..], b"-1", b"4294967296"] {
13576            assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
13577            assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
13578        }
13579        for arg in [&b"2"[..], b"-1"] {
13580            assert_eq!(
13581                f.run(&[b"BITPOS", b"k", arg]),
13582                "-ERR The bit argument must be 1 or 0.\r\n"
13583            );
13584        }
13585        assert_eq!(
13586            f.run(&[b"BITPOS", b"k", b"abc"]),
13587            "-ERR value is not an integer or out of range\r\n"
13588        );
13589        assert_eq!(
13590            f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
13591            "-ERR value is not an integer or out of range\r\n"
13592        );
13593        let bad_bit = "-ERR bit is not an integer or out of range\r\n";
13594        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
13595        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
13596    }
13597
13598    /// Every one of the seven refuses a key that is not a string.
13599    #[test]
13600    fn every_bitmap_command_says_wrongtype() {
13601        let mut f = Fixture::new();
13602        f.run(&[b"LPUSH", b"l", b"x"]);
13603        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
13604        let cases: &[&[&[u8]]] = &[
13605            &[b"SETBIT", b"l", b"0", b"1"],
13606            &[b"GETBIT", b"l", b"0"],
13607            &[b"BITCOUNT", b"l"],
13608            &[b"BITPOS", b"l", b"1"],
13609            &[b"BITOP", b"AND", b"d", b"l"],
13610            &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
13611            &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
13612        ];
13613        for case in cases {
13614            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
13615        }
13616    }
13617
13618    // --------------------------------------------------------- hyperloglogs
13619
13620    #[test]
13621    fn a_sketch_is_added_to_and_counted() {
13622        let mut f = Fixture::new();
13623        // Creating the key counts as a change, even with nothing to add.
13624        assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
13625        assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
13626        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
13627        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
13628        // And it is a string, which is not an implementation detail: a client
13629        // can `GET` a sketch out of one server and `SET` it into another.
13630        assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
13631        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
13632
13633        assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
13634        assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
13635        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
13636    }
13637
13638    #[test]
13639    fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
13640        let mut f = Fixture::new();
13641        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
13642        // Not text, so it is compared as bytes.
13643        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";
13644        let mut reply = b"$27\r\n".to_vec();
13645        reply.extend_from_slice(want);
13646        reply.extend_from_slice(b"\r\n");
13647        assert_eq!(f.raw(&[b"GET", b"h"]), reply);
13648    }
13649
13650    #[test]
13651    fn counting_several_keys_counts_their_union() {
13652        let mut f = Fixture::new();
13653        f.run(&[b"PFADD", b"a", b"x", b"y"]);
13654        f.run(&[b"PFADD", b"b", b"y", b"z"]);
13655        assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
13656        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
13657        // A key that is not there is an empty sketch, not an error and not
13658        // something that gets created by being counted.
13659        assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
13660        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
13661        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13662    }
13663
13664    #[test]
13665    fn a_merge_keeps_what_the_destination_had() {
13666        let mut f = Fixture::new();
13667        f.run(&[b"PFADD", b"a", b"x", b"y"]);
13668        f.run(&[b"PFADD", b"b", b"z"]);
13669        assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
13670        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
13671        // The destination is one of the sources, so a second merge adds to it.
13672        f.run(&[b"PFADD", b"c", b"w"]);
13673        assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
13674        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
13675        // And with no sources it is a no-op that still answers OK and still
13676        // creates a destination that was not there.
13677        assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
13678        assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
13679    }
13680
13681    /// Not under Miri, and not for the number of commands: a dense sketch is
13682    /// sixteen thousand three hundred and eighty four registers and every
13683    /// command here walks all of them, so one `PFCOUNT` is more interpreted
13684    /// work than a hundred ordinary tests. The registers and the walking are in
13685    /// `yo-kv`, where fifteen tests of their own cover both encodings and where
13686    /// the interpreter does run over them. What is left here is the dispatch
13687    /// around it, which is the same dispatch every other command in this file
13688    /// goes through.
13689    #[cfg_attr(miri, ignore = "sixteen thousand registers a command")]
13690    #[test]
13691    fn the_debug_forms_answer_four_different_shapes() {
13692        let mut f = Fixture::new();
13693        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
13694        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
13695        assert_eq!(
13696            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
13697            "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
13698        );
13699        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
13700        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
13701        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
13702        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
13703        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
13704        // A dense sketch has no opcodes left to print.
13705        assert_eq!(
13706            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
13707            "-ERR HLL encoding is not sparse\r\n"
13708        );
13709
13710        // All 16384 registers, of which three are not nought.
13711        let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
13712        assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
13713        assert_eq!(reply.matches(":0\r\n").count(), 16381);
13714        assert_eq!(reply.matches(":1\r\n").count(), 2);
13715        assert_eq!(reply.matches(":2\r\n").count(), 1);
13716
13717        assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
13718    }
13719
13720    #[test]
13721    fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
13722        let mut f = Fixture::new();
13723        f.run(&[b"SET", b"plain", b"not a sketch"]);
13724        let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
13725        assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
13726        assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
13727        assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
13728        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
13729
13730        // A key that is not a string at all gets the ordinary sentence, and a
13731        // destination that would have been written is not created.
13732        f.run(&[b"RPUSH", b"l", b"x"]);
13733        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
13734        assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
13735        assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
13736        assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
13737        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
13738        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
13739    }
13740
13741    #[test]
13742    fn pfdebug_has_its_own_complaints() {
13743        let mut f = Fixture::new();
13744        f.run(&[b"PFADD", b"h", b"a"]);
13745        // The word is quoted exactly as the client spelled it, and this is not
13746        // the "Try X HELP." sentence every other container command uses.
13747        assert_eq!(
13748            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
13749            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
13750        );
13751        // Where all three of the real commands take a missing key as empty.
13752        let gone = "-ERR The specified key does not exist\r\n";
13753        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
13754        assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
13755        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
13756        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
13757        assert_eq!(
13758            f.run(&[b"PFDEBUG"]),
13759            "-ERR wrong number of arguments for 'pfdebug' command\r\n"
13760        );
13761        assert_eq!(
13762            f.run(&[b"PFSELFTEST", b"x"]),
13763            "-ERR wrong number of arguments for 'pfselftest' command\r\n"
13764        );
13765    }
13766
13767    #[test]
13768    fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
13769        let mut f = Fixture::new();
13770        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
13771        // The sketch with its last byte cut off, which is still a header and a
13772        // magic and is a run length encoding that stops short of register 16384.
13773        let reply = f.raw(&[b"GET", b"h"]);
13774        let short = reply[5..reply.len() - 3].to_vec();
13775        f.run(&[b"SET", b"h", &short]);
13776        assert_eq!(
13777            f.run(&[b"PFCOUNT", b"h"]),
13778            "-INVALIDOBJ Corrupted HLL object detected\r\n"
13779        );
13780    }
13781
13782    #[test]
13783    fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
13784        let mut f = Fixture::new();
13785        // One that stays sparse and one that has gone dense, since the payload
13786        // carries the bytes and the two encodings are different lengths.
13787        f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
13788        // Ten thousand elements is what takes a sketch dense on its own, and it
13789        // is ten thousand trips through dispatch, which is what Miri charges
13790        // for. There the same sketch is taken across by hand. What this test is
13791        // about is a dense payload surviving a round trip and the encoding is
13792        // dense either way: that a sketch converts when it fills up is what
13793        // `the_debug_forms_answer_four_different_shapes` is for.
13794        if cfg!(miri) {
13795            f.run(&[b"PFADD", b"big", b"a", b"b", b"c"]);
13796            f.run(&[b"PFDEBUG", b"TODENSE", b"big"]);
13797        } else {
13798            for i in 0..10_000u32 {
13799                let ele = format!("e{i}");
13800                f.run(&[b"PFADD", b"big", ele.as_bytes()]);
13801            }
13802        }
13803        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
13804        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
13805
13806        for key in [&b"small"[..], b"big"] {
13807            let mut copy = key.to_vec();
13808            copy.push(b'2');
13809            let bytes = payload(&f.raw(&[b"DUMP", key]));
13810            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
13811            // The bytes, the encoding and the estimate all come back, which is
13812            // the whole of what byte compatibility across a round trip means.
13813            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
13814            assert_eq!(
13815                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
13816                f.run(&[b"PFDEBUG", b"ENCODING", key])
13817            );
13818            assert_eq!(f.run(&[b"PFCOUNT", &copy]), f.run(&[b"PFCOUNT", key]));
13819        }
13820        assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
13821        assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
13822    }
13823
13824    /// One RESP2 bulk string. The JSON replies are almost all one of these and
13825    /// the text inside them has quotes in it, so writing the frame out by hand
13826    /// buries the part of the assertion that matters.
13827    fn bulk(s: &str) -> String {
13828        format!("${}\r\n{s}\r\n", s.len())
13829    }
13830
13831    /// A RESP2 array of bulk strings, which is what most of the list replies
13832    /// are and what writing them out by hand in every assertion looks like.
13833    fn bulks(parts: &[&str]) -> String {
13834        let mut s = format!("*{}\r\n", parts.len());
13835        for p in parts {
13836            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
13837        }
13838        s
13839    }
13840
13841    #[test]
13842    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
13843        let mut f = Fixture::new();
13844        // Each element in turn goes at the head, so the last one sent is at the
13845        // front when it is over. That reads like a bug in the client and it is
13846        // what every Redis has always done.
13847        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
13848        assert_eq!(
13849            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13850            bulks(&["c", "b", "a"])
13851        );
13852        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
13853        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
13854        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
13855        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
13856        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
13857        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
13858    }
13859
13860    #[test]
13861    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
13862        let mut f = Fixture::new();
13863        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
13864        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
13865        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
13866        f.run(&[b"RPUSH", b"k", b"a"]);
13867        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
13868        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
13869        assert_eq!(
13870            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13871            bulks(&["z", "a", "y"])
13872        );
13873    }
13874
13875    /// The four ways a pop can come back with nothing, which are three
13876    /// different replies and a RESP2 client can tell all of them apart.
13877    #[test]
13878    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
13879        let mut f = Fixture::new();
13880        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
13881        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
13882        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
13883        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
13884        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
13885        // A count of zero against a list that is there is an empty array and
13886        // not a null array, which is the fourth answer.
13887        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
13888        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
13889        // More than there is takes what there is and the key goes with it.
13890        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
13891        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
13892    }
13893
13894    #[test]
13895    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
13896        let mut f = Fixture::new();
13897        f.run(&[b"RPUSH", b"k", b"a"]);
13898        let range = "-ERR value is out of range, must be positive\r\n";
13899        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
13900        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
13901        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
13902        // Redis calls this an arity error and not a syntax error, which is a
13903        // distinction it does not always make.
13904        assert_eq!(
13905            f.run(&[b"LPOP", b"k", b"1", b"2"]),
13906            "-ERR wrong number of arguments for 'lpop' command\r\n"
13907        );
13908        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
13909    }
13910
13911    #[test]
13912    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
13913        let mut f = Fixture::new();
13914        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
13915        assert_eq!(
13916            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13917            bulks(&["a", "b", "c"])
13918        );
13919        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
13920        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
13921        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
13922        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
13923        assert_eq!(
13924            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
13925            bulks(&["a", "b", "c"])
13926        );
13927        // A key that is not there is an empty range and not a nil, which is the
13928        // one place a list disagrees with a set.
13929        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
13930        assert_eq!(
13931            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
13932            "-ERR value is not an integer or out of range\r\n"
13933        );
13934    }
13935
13936    #[test]
13937    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
13938        let mut f = Fixture::new();
13939        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
13940        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
13941        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
13942        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
13943        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
13944        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
13945        assert_eq!(
13946            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13947            bulks(&["a", "b", "z"])
13948        );
13949        // Both ways of missing are errors here rather than a nil, because a
13950        // list is never empty and there is nothing else the reply could be.
13951        assert_eq!(
13952            f.run(&[b"LSET", b"k", b"99", b"z"]),
13953            "-ERR index out of range\r\n"
13954        );
13955        assert_eq!(
13956            f.run(&[b"LSET", b"nope", b"0", b"z"]),
13957            "-ERR no such key\r\n"
13958        );
13959    }
13960
13961    #[test]
13962    fn linsert_says_three_things_with_one_signed_number() {
13963        let mut f = Fixture::new();
13964        // Zero for a key that is not there, which is not the same as minus one
13965        // for a pivot that is not in a list that is.
13966        assert_eq!(
13967            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
13968            ":0\r\n"
13969        );
13970        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
13971        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
13972        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
13973        assert_eq!(
13974            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13975            bulks(&["X", "a", "b", "Y"])
13976        );
13977        assert_eq!(
13978            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
13979            ":-1\r\n"
13980        );
13981        assert_eq!(
13982            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
13983            "-ERR syntax error\r\n"
13984        );
13985    }
13986
13987    #[test]
13988    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
13989        let mut f = Fixture::new();
13990        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
13991        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
13992        assert_eq!(
13993            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13994            bulks(&["b", "c", "a"])
13995        );
13996        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
13997        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
13998        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
13999        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
14000        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
14001        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
14002    }
14003
14004    #[test]
14005    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
14006        let mut f = Fixture::new();
14007        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
14008        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
14009        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
14010        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
14011        // leave `EXISTS` answering zero rather than leaving an empty one.
14012        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
14013        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
14014        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
14015    }
14016
14017    #[test]
14018    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
14019        let mut f = Fixture::new();
14020        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
14021        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
14022        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
14023        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
14024        assert_eq!(
14025            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
14026            "*2\r\n:0\r\n:3\r\n"
14027        );
14028        assert_eq!(
14029            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
14030            "*3\r\n:6\r\n:3\r\n:0\r\n"
14031        );
14032        // MAXLEN counts elements looked at and not matches found, so three
14033        // stops after `a b c` and finds the one match in it.
14034        assert_eq!(
14035            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
14036            "*1\r\n:0\r\n"
14037        );
14038        // Nothing found is three different replies depending on how it was
14039        // asked and whether the key is there at all.
14040        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
14041        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
14042        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
14043        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
14044    }
14045
14046    #[test]
14047    fn lpos_words_its_three_mistakes_the_way_redis_does() {
14048        let mut f = Fixture::new();
14049        f.run(&[b"RPUSH", b"p", b"a"]);
14050        // The whole sentence and not a prefix, because the older wording of it
14051        // is still all over the internet and clients match on the text.
14052        assert_eq!(
14053            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
14054            "-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"
14055        );
14056        assert_eq!(
14057            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
14058            "-ERR COUNT can't be negative\r\n"
14059        );
14060        assert_eq!(
14061            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
14062            "-ERR MAXLEN can't be negative\r\n"
14063        );
14064        assert_eq!(
14065            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
14066            "-ERR syntax error\r\n"
14067        );
14068        assert_eq!(
14069            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
14070            "-ERR syntax error\r\n"
14071        );
14072    }
14073
14074    #[test]
14075    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
14076        let mut f = Fixture::new();
14077        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
14078        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
14079        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
14080        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
14081        assert_eq!(
14082            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
14083            "$1\r\na\r\n"
14084        );
14085        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
14086        // The same key twice is the documented way to rotate a list and falls
14087        // out of taking the element before deciding where to put it.
14088        f.run(&[b"DEL", b"r"]);
14089        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
14090        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
14091        assert_eq!(
14092            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
14093            bulks(&["3", "1", "2"])
14094        );
14095        assert_eq!(
14096            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
14097            "$-1\r\n"
14098        );
14099        assert_eq!(
14100            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
14101            "-ERR syntax error\r\n"
14102        );
14103    }
14104
14105    #[test]
14106    fn a_move_checks_the_destination_before_it_takes_anything() {
14107        let mut f = Fixture::new();
14108        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
14109        f.run(&[b"SET", b"str", b"v"]);
14110        assert_eq!(
14111            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
14112            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
14113        );
14114        // The element is still where it was, rather than having gone nowhere.
14115        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
14116    }
14117
14118    #[test]
14119    fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
14120        // OBO is what you get from sending LMOVE that many times, BULK keeps
14121        // the source order. The two only differ when both ends are the same,
14122        // which is the whole reason the word exists.
14123        for (from, to, order, want) in [
14124            ("LEFT", "RIGHT", "OBO", ["a", "b"]),
14125            ("LEFT", "RIGHT", "BULK", ["a", "b"]),
14126            ("LEFT", "LEFT", "OBO", ["b", "a"]),
14127            ("LEFT", "LEFT", "BULK", ["a", "b"]),
14128            ("RIGHT", "LEFT", "OBO", ["d", "e"]),
14129            ("RIGHT", "LEFT", "BULK", ["d", "e"]),
14130            ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
14131            ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
14132        ] {
14133            let mut f = Fixture::new();
14134            f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
14135            let how = format!("{from} {to} {order}");
14136            let reply = f.run(&[
14137                b"LMOVEM",
14138                b"s",
14139                b"d",
14140                from.as_bytes(),
14141                to.as_bytes(),
14142                b"COUNT",
14143                b"2",
14144                order.as_bytes(),
14145            ]);
14146            assert_eq!(reply, bulks(&want), "the reply for {how}");
14147            assert_eq!(
14148                f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
14149                bulks(&want),
14150                "the destination for {how}"
14151            );
14152        }
14153    }
14154
14155    #[test]
14156    fn a_block_move_of_one_needs_no_count_at_all() {
14157        let mut f = Fixture::new();
14158        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
14159        assert_eq!(
14160            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
14161            bulks(&["a"])
14162        );
14163        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
14164        // Six and seven arguments are neither of the two forms, so the
14165        // reference calls both of them a syntax error rather than guessing.
14166        assert_eq!(
14167            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
14168            "-ERR syntax error\r\n"
14169        );
14170        assert_eq!(
14171            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
14172            "-ERR syntax error\r\n"
14173        );
14174    }
14175
14176    #[test]
14177    fn a_block_move_with_exactly_takes_all_of_them_or_none() {
14178        let mut f = Fixture::new();
14179        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
14180        // A null array and not a null bulk string, which `redis-cli` prints as
14181        // `(nil)` either way and only the raw wire tells apart. What it would
14182        // have sent is an array, so its nothing is an array's nothing.
14183        assert_eq!(
14184            f.run(&[
14185                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
14186            ]),
14187            "*-1\r\n"
14188        );
14189        assert_eq!(
14190            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
14191            bulks(&["a", "b", "c"])
14192        );
14193        // COUNT takes what there is, and an emptied source goes away.
14194        assert_eq!(
14195            f.run(&[
14196                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
14197            ]),
14198            bulks(&["a", "b", "c"])
14199        );
14200        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
14201        assert_eq!(
14202            f.run(&[
14203                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
14204            ]),
14205            "*-1\r\n"
14206        );
14207    }
14208
14209    #[test]
14210    fn a_block_move_onto_itself_rotates_by_the_count() {
14211        let mut f = Fixture::new();
14212        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
14213        assert_eq!(
14214            f.run(&[
14215                b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
14216            ]),
14217            bulks(&["a", "b"])
14218        );
14219        assert_eq!(
14220            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
14221            bulks(&["c", "a", "b"])
14222        );
14223    }
14224
14225    #[test]
14226    fn a_block_move_reads_the_count_before_the_ordering_word() {
14227        let mut f = Fixture::new();
14228        f.run(&[b"RPUSH", b"s", b"a", b"b"]);
14229        f.run(&[b"SET", b"str", b"v"]);
14230        let count = "-ERR count should be greater than 0\r\n";
14231        assert_eq!(
14232            f.run(&[
14233                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
14234            ]),
14235            count
14236        );
14237        assert_eq!(
14238            f.run(&[
14239                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
14240            ]),
14241            count
14242        );
14243        assert_eq!(
14244            f.run(&[
14245                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
14246            ]),
14247            "-ERR syntax error\r\n"
14248        );
14249        assert_eq!(
14250            f.run(&[
14251                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
14252            ]),
14253            "-ERR syntax error\r\n"
14254        );
14255        // Every argument is read before the keys are looked at, so a bad count
14256        // beats a wrong type even when the type is wrong on the source.
14257        assert_eq!(
14258            f.run(&[
14259                b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
14260            ]),
14261            count
14262        );
14263        assert_eq!(
14264            f.run(&[
14265                b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
14266            ]),
14267            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
14268        );
14269        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
14270    }
14271
14272    #[test]
14273    fn lmpop_answers_from_the_first_key_that_has_anything() {
14274        let mut f = Fixture::new();
14275        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
14276        // The name of the key that answered comes back with the elements,
14277        // because the client cannot work out which one it was.
14278        assert_eq!(
14279            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
14280            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
14281        );
14282        assert_eq!(
14283            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
14284            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
14285        );
14286        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
14287        // A null array and not a null, even though what it stands in for is an
14288        // array holding a key name and then another array.
14289        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
14290    }
14291
14292    #[test]
14293    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
14294        let mut f = Fixture::new();
14295        f.run(&[b"RPUSH", b"k", b"a"]);
14296        assert_eq!(
14297            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
14298            "-ERR numkeys should be greater than 0\r\n"
14299        );
14300        assert_eq!(
14301            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
14302            "-ERR numkeys should be greater than 0\r\n"
14303        );
14304        assert_eq!(
14305            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
14306            "-ERR count should be greater than 0\r\n"
14307        );
14308        // A key count that eats the direction is a syntax error and not a
14309        // sentence about key counts, because the direction is simply not there.
14310        assert_eq!(
14311            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
14312            "-ERR syntax error\r\n"
14313        );
14314        assert_eq!(
14315            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
14316            "-ERR syntax error\r\n"
14317        );
14318        assert_eq!(
14319            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
14320            "-ERR syntax error\r\n"
14321        );
14322        assert_eq!(
14323            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
14324            "-ERR syntax error\r\n"
14325        );
14326        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
14327    }
14328
14329    #[test]
14330    fn every_list_command_says_wrongtype_and_writes_nothing() {
14331        let mut f = Fixture::new();
14332        f.run(&[b"SET", b"str", b"v"]);
14333        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14334        for cmd in [
14335            &[b"LPUSH".as_slice(), b"str", b"a"][..],
14336            &[b"RPUSH", b"str", b"a"],
14337            &[b"LPUSHX", b"str", b"a"],
14338            &[b"RPUSHX", b"str", b"a"],
14339            &[b"LPOP", b"str"],
14340            &[b"LPOP", b"str", b"2"],
14341            &[b"RPOP", b"str"],
14342            &[b"LLEN", b"str"],
14343            &[b"LRANGE", b"str", b"0", b"-1"],
14344            &[b"LINDEX", b"str", b"0"],
14345            &[b"LSET", b"str", b"0", b"a"],
14346            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
14347            &[b"LREM", b"str", b"0", b"a"],
14348            &[b"LTRIM", b"str", b"0", b"-1"],
14349            &[b"LPOS", b"str", b"a"],
14350            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
14351            &[b"RPOPLPUSH", b"str", b"d"],
14352            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
14353            &[b"LMPOP", b"1", b"str", b"LEFT"],
14354        ] {
14355            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
14356        }
14357        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
14358        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
14359    }
14360
14361    /// A timeout is not an integer and it is not an ordinary float either: the
14362    /// three sentences it can answer with are its own, and which one a given
14363    /// argument gets is not what reading the code would suggest.
14364    #[test]
14365    fn a_timeout_has_three_ways_of_being_wrong() {
14366        let mut f = Fixture::new();
14367        let not_float = "-ERR timeout is not a float or out of range\r\n";
14368        let range = "-ERR timeout is out of range\r\n";
14369        for (bad, want) in [
14370            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
14371            (&[b"BLPOP", b"k", b"nan"], not_float),
14372            (&[b"BLPOP", b"k", b""], not_float),
14373            // Whitespace on either side, which `strtold` would take and Redis
14374            // does not.
14375            (&[b"BLPOP", b"k", b" 1"], not_float),
14376            (&[b"BLPOP", b"k", b"1 "], not_float),
14377            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
14378            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
14379            // These three parse, so they are not the not-a-float error, and all
14380            // three are further off than an i64 of milliseconds reaches.
14381            (&[b"BLPOP", b"k", b"1e400"], range),
14382            (&[b"BLPOP", b"k", b"inf"], range),
14383            (&[b"BLPOP", b"k", b"9999999999999999"], range),
14384            (&[b"BRPOP", b"k", b"abc"], not_float),
14385            (
14386                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
14387                not_float,
14388            ),
14389            (
14390                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
14391                "-ERR timeout is negative\r\n",
14392            ),
14393            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
14394        ] {
14395            assert_eq!(f.run(bad), want, "for {bad:?}");
14396        }
14397    }
14398
14399    /// A timeout of exactly zero means no timeout, and there are two ways of
14400    /// writing exactly zero.
14401    #[test]
14402    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
14403        let mut f = Fixture::new();
14404        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
14405            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
14406            assert_eq!(flow, Flow::Block, "for {timeout:?}");
14407            assert!(out.is_empty(), "for {timeout:?}");
14408        }
14409        // Positive, so it is a real deadline, and the deadline is this
14410        // millisecond. Nothing is written here either: the reply comes from the
14411        // sweep, which is the engine's and not this layer's.
14412        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
14413        assert_eq!(flow, Flow::Block);
14414        assert!(out.is_empty());
14415    }
14416
14417    #[test]
14418    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
14419        let mut f = Fixture::new();
14420        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
14421
14422        // The one difference from LPOP: the reply names the key that answered,
14423        // which is what makes BLPOP over several keys usable.
14424        assert_eq!(
14425            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
14426            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
14427        );
14428        assert_eq!(
14429            f.run(&[b"BRPOP", b"L", b"0"]),
14430            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
14431        );
14432        assert_eq!(
14433            f.run(&[
14434                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
14435            ]),
14436            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
14437        );
14438        assert_eq!(
14439            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
14440            "$1\r\nd\r\n"
14441        );
14442        assert_eq!(
14443            f.run(&[b"EXISTS", b"L"]),
14444            ":0\r\n",
14445            "and the key went with it"
14446        );
14447        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
14448        // Onto itself, which is how a list is rotated and is a real thing to ask
14449        // a blocking move for.
14450        f.run(&[b"RPUSH", b"D", b"x"]);
14451        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
14452        assert_eq!(
14453            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
14454            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
14455        );
14456    }
14457
14458    #[test]
14459    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
14460        let mut f = Fixture::new();
14461        f.run(&[b"RPUSH", b"k", b"a"]);
14462        for (bad, want) in [
14463            (
14464                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
14465                "-ERR numkeys should be greater than 0\r\n",
14466            ),
14467            (
14468                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
14469                "-ERR numkeys should be greater than 0\r\n",
14470            ),
14471            // Two keys named and one given, so the word that should have been
14472            // the direction is a key and there is no direction left.
14473            (
14474                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
14475                "-ERR syntax error\r\n",
14476            ),
14477            (
14478                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
14479                "-ERR syntax error\r\n",
14480            ),
14481            (
14482                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
14483                "-ERR syntax error\r\n",
14484            ),
14485            (
14486                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
14487                "-ERR syntax error\r\n",
14488            ),
14489            // A count that is not a number at all gets the same sentence a zero
14490            // or a negative one gets, rather than the usual one about integers.
14491            (
14492                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
14493                "-ERR count should be greater than 0\r\n",
14494            ),
14495            (
14496                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
14497                "-ERR count should be greater than 0\r\n",
14498            ),
14499        ] {
14500            assert_eq!(f.run(bad), want, "for {bad:?}");
14501        }
14502        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
14503    }
14504
14505    #[test]
14506    fn a_blocking_move_reads_its_directions_before_its_timeout() {
14507        let mut f = Fixture::new();
14508        // Both are wrong. Redis checks the directions first, so this is the
14509        // syntax error and not a complaint about the timeout.
14510        assert_eq!(
14511            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
14512            "-ERR syntax error\r\n"
14513        );
14514        assert_eq!(
14515            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
14516            "-ERR syntax error\r\n"
14517        );
14518    }
14519
14520    /// `BLMOVEM` answers exactly what `LMOVEM` answers when it does not have to
14521    /// wait, which is the same relationship every other command in this file has
14522    /// with the one it wraps.
14523    #[test]
14524    fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
14525        let mut f = Fixture::new();
14526        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
14527        assert_eq!(
14528            f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
14529            (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
14530        );
14531        assert_eq!(
14532            f.run(&[
14533                b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
14534            ]),
14535            bulks(&["e", "d"])
14536        );
14537        assert_eq!(
14538            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
14539            bulks(&["a", "e", "d"])
14540        );
14541        // `EXACTLY` with enough there does not wait either.
14542        assert_eq!(
14543            f.run(&[
14544                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
14545            ]),
14546            bulks(&["b", "c"])
14547        );
14548        assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
14549    }
14550
14551    /// The one thing `BLMOVEM` decides differently from the other five: `COUNT`
14552    /// is ready as soon as there is anything and `EXACTLY` is not ready until the
14553    /// whole block has arrived.
14554    #[test]
14555    fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
14556        let mut f = Fixture::new();
14557        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
14558        // Two there and three asked for. `COUNT` takes the two.
14559        assert_eq!(
14560            f.flow(&[
14561                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
14562            ]),
14563            (Flow::Continue, bulks(&["a", "b"]))
14564        );
14565
14566        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
14567        // The same line with `EXACTLY` parks instead, and takes nothing on the
14568        // way past.
14569        assert_eq!(
14570            f.flow(&[
14571                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
14572            ])
14573            .0,
14574            Flow::Block
14575        );
14576        assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
14577    }
14578
14579    #[test]
14580    fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
14581        let mut f = Fixture::new();
14582        let syntax = "-ERR syntax error\r\n";
14583        // All three are wrong and the directions are read first.
14584        assert_eq!(
14585            f.run(&[
14586                b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
14587            ]),
14588            syntax
14589        );
14590        // Directions fine, timeout and count both wrong, so the timeout wins.
14591        assert_eq!(
14592            f.run(&[
14593                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
14594            ]),
14595            "-ERR timeout is not a float or out of range\r\n"
14596        );
14597        assert_eq!(
14598            f.run(&[
14599                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
14600            ]),
14601            "-ERR timeout is negative\r\n"
14602        );
14603        // And with the timeout fine, the count before the ordering word.
14604        assert_eq!(
14605            f.run(&[
14606                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
14607            ]),
14608            "-ERR count should be greater than 0\r\n"
14609        );
14610        assert_eq!(
14611            f.run(&[
14612                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
14613            ]),
14614            syntax
14615        );
14616        // Seven and eight arguments are neither of the two forms, the same way
14617        // six and seven are for `LMOVEM`.
14618        assert_eq!(
14619            f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
14620            syntax
14621        );
14622        assert_eq!(
14623            f.run(&[
14624                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
14625            ]),
14626            syntax
14627        );
14628    }
14629
14630    /// The four ways a blocking command sees a key of another type, and the one
14631    /// way it does not.
14632    #[test]
14633    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
14634        let mut f = Fixture::new();
14635        f.run(&[b"SET", b"S", b"v"]);
14636        f.run(&[b"RPUSH", b"D", b"x"]);
14637        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14638
14639        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
14640        // Every key is checked even when an earlier one would have blocked, so
14641        // an empty key in front of a string does not hide it.
14642        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
14643        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
14644        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
14645        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
14646        // The destination, which is only reached because the source has
14647        // something in it.
14648        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
14649        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
14650        assert_eq!(
14651            f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
14652            wrong
14653        );
14654        assert_eq!(
14655            f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
14656            wrong
14657        );
14658
14659        // And the one that does not: an empty source means the destination is
14660        // never looked at, so this waits rather than erroring, and on a real
14661        // server it times out.
14662        assert_eq!(
14663            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
14664                .0,
14665            Flow::Block
14666        );
14667        // `BLMOVEM` has a second way of not being ready, and it hides the
14668        // destination just as well: the source is a list with two elements in it
14669        // and `EXACTLY` wants three, so the string never gets looked at.
14670        assert_eq!(
14671            f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
14672                .0,
14673            Flow::Block
14674        );
14675        f.run(&[b"RPUSH", b"E", b"1", b"2"]);
14676        assert_eq!(
14677            f.flow(&[
14678                b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
14679            ])
14680            .0,
14681            Flow::Block
14682        );
14683    }
14684
14685    /// The same churn the set and the string get, because a list that leaks a
14686    /// chunk per push looks exactly like one that does not until it has run for
14687    /// an afternoon.
14688    /// Not under Miri, for the reason on `churning_sets_does_not_grow_the_server`.
14689    #[cfg_attr(miri, ignore = "the volume is the claim")]
14690    #[test]
14691    fn churning_lists_does_not_grow_the_server() {
14692        let mut f = Fixture::new();
14693        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
14694        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
14695            .into_iter()
14696            .chain(vals.iter().map(Vec::as_slice))
14697            .collect();
14698
14699        f.run(&args);
14700        f.run(&[b"DEL", b"k"]);
14701        f.server.compact_step();
14702        let after_first = f.server.memory_bytes();
14703
14704        for _ in 0..200 {
14705            f.run(&args);
14706            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
14707            f.server.compact_step();
14708        }
14709        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
14710        assert!(
14711            f.server.memory_bytes() <= after_first * 2,
14712            "held {} after two hundred passes against {after_first} after one",
14713            f.server.memory_bytes()
14714        );
14715    }
14716
14717    // ------------------------------------------------------------ sorted set
14718
14719    #[test]
14720    fn a_sorted_set_takes_scores_and_gives_them_back() {
14721        let mut f = Fixture::new();
14722        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
14723        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
14724        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
14725        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
14726        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
14727        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
14728        assert_eq!(
14729            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
14730            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
14731        );
14732        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
14733        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
14734        // The key goes when the last member does.
14735        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
14736        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
14737    }
14738
14739    #[test]
14740    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
14741        let mut f = Fixture::new();
14742        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
14743        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
14744        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
14745        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
14746
14747        f.out = Out::new(Proto::Resp3);
14748        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
14749        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
14750        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
14751        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
14752    }
14753
14754    #[test]
14755    fn the_zadd_options_gate_what_gets_written() {
14756        let mut f = Fixture::new();
14757        f.run(&[b"ZADD", b"z", b"5", b"a"]);
14758        // NX leaves a member that is there alone, XX will not create one.
14759        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
14760        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
14761        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
14762        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
14763        // GT and LT only move a score one way.
14764        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
14765        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
14766        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
14767        // CH counts a moved score and plain ZADD does not.
14768        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
14769        assert_eq!(
14770            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
14771            ":2\r\n"
14772        );
14773    }
14774
14775    #[test]
14776    fn zadd_incr_answers_a_score_or_nothing_at_all() {
14777        let mut f = Fixture::new();
14778        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
14779        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
14780        // A gate that refuses is the string nil, because the reply it stands in
14781        // for is a score.
14782        assert_eq!(
14783            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
14784            "$-1\r\n"
14785        );
14786        assert_eq!(
14787            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
14788            "$-1\r\n"
14789        );
14790        assert_eq!(
14791            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
14792            "$-1\r\n"
14793        );
14794        assert_eq!(
14795            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
14796            "$1\r\n8\r\n"
14797        );
14798        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
14799        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
14800    }
14801
14802    #[test]
14803    fn the_two_infinities_will_not_be_added_together() {
14804        let mut f = Fixture::new();
14805        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
14806        let nan = "-ERR resulting score is not a number (NaN)\r\n";
14807        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
14808        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
14809        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
14810        // And a key made for an increment that then fails does not stay behind.
14811        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
14812    }
14813
14814    #[test]
14815    fn zadd_says_its_mistakes_the_way_redis_says_them() {
14816        let mut f = Fixture::new();
14817        // The pairs are counted before the options are looked at, so this is a
14818        // syntax error about having none and not a complaint about NX and XX.
14819        assert_eq!(
14820            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
14821            "-ERR syntax error\r\n"
14822        );
14823        assert_eq!(
14824            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
14825            "-ERR XX and NX options at the same time are not compatible\r\n"
14826        );
14827        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
14828        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
14829        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
14830        assert_eq!(
14831            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
14832            "-ERR INCR option supports a single increment-element pair\r\n"
14833        );
14834        // An odd number of arguments after the options.
14835        assert_eq!(
14836            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
14837            "-ERR syntax error\r\n"
14838        );
14839        // Every score is read before the first is stored.
14840        assert_eq!(
14841            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
14842            "-ERR value is not a valid float\r\n"
14843        );
14844        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
14845    }
14846
14847    #[test]
14848    fn a_rank_says_where_a_member_sits_from_either_end() {
14849        let mut f = Fixture::new();
14850        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14851        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
14852        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
14853        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
14854        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
14855        // WITHSCORE changes both shapes: the answer and the nothing.
14856        assert_eq!(
14857            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
14858            "*2\r\n:1\r\n$1\r\n2\r\n"
14859        );
14860        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
14861        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
14862        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
14863        // A bad option is a syntax error and one argument too many is an arity
14864        // error, which is Redis's split.
14865        assert_eq!(
14866            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
14867            "-ERR syntax error\r\n"
14868        );
14869        assert_eq!(
14870            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
14871            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
14872        );
14873    }
14874
14875    #[test]
14876    fn the_two_counts_read_their_two_kinds_of_bound() {
14877        let mut f = Fixture::new();
14878        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14879        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
14880        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
14881        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
14882        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
14883        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
14884        assert_eq!(
14885            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
14886            "-ERR min or max is not a float\r\n"
14887        );
14888
14889        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
14890        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
14891        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
14892        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
14893        // A bare member is not a bound, because a member can start with any
14894        // byte and there would be no way to say the bracket if it were optional.
14895        assert_eq!(
14896            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
14897            "-ERR min or max not valid string range item\r\n"
14898        );
14899    }
14900
14901    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
14902    ///
14903    /// Every byte in here was read off a real 8.10.1 rather than worked out,
14904    /// because the interesting part of this command is not what it selects, it
14905    /// is which of the two ends the client is expected to name first.
14906    #[test]
14907    fn one_range_command_selects_by_rank_or_score_or_name() {
14908        let mut f = Fixture::new();
14909        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14910        assert_eq!(
14911            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
14912            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
14913        );
14914        assert_eq!(
14915            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
14916            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
14917        );
14918        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
14919        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
14920        // REV over ranks reverses the walk and leaves the two arguments alone,
14921        // because a rank counts from the end the walk starts at.
14922        assert_eq!(
14923            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
14924            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
14925        );
14926        assert_eq!(
14927            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
14928            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
14929        );
14930        // And REV over scores does swap them, since a bound does not count from
14931        // anywhere. This is the one line of the parse that tells the two apart.
14932        assert_eq!(
14933            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
14934            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
14935        );
14936        assert_eq!(
14937            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
14938            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
14939        );
14940        assert_eq!(
14941            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
14942            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
14943        );
14944    }
14945
14946    /// The older spellings, which are the same six windows with the mode in the
14947    /// name and the high end named first on the three that go backwards.
14948    #[test]
14949    fn the_older_range_spellings_name_their_high_end_first() {
14950        let mut f = Fixture::new();
14951        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14952        assert_eq!(
14953            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
14954            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
14955        );
14956        assert_eq!(
14957            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
14958            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
14959        );
14960        assert_eq!(
14961            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
14962            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
14963        );
14964        assert_eq!(
14965            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
14966            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
14967        );
14968        // The two arguments the wrong way round is an empty answer and not an
14969        // error, which is what the swap being in the parse rather than in the
14970        // window buys.
14971        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
14972        assert_eq!(
14973            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
14974            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
14975        );
14976        assert_eq!(
14977            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
14978            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
14979        );
14980        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
14981        // way of spelling the mode, they are a syntax error.
14982        for cmd in [
14983            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
14984            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
14985            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
14986        ] {
14987            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
14988        }
14989    }
14990
14991    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
14992    /// only some of them accept.
14993    #[test]
14994    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
14995        let mut f = Fixture::new();
14996        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14997        assert_eq!(
14998            f.run(&[
14999                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
15000            ]),
15001            "*1\r\n$1\r\nb\r\n"
15002        );
15003        // A negative offset skips past everything, a negative count is no bound.
15004        assert_eq!(
15005            f.run(&[
15006                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
15007            ]),
15008            "*0\r\n"
15009        );
15010        assert_eq!(
15011            f.run(&[
15012                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
15013            ]),
15014            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
15015        );
15016        // The two options in either order, which falls out of the parse loop.
15017        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";
15018        assert_eq!(
15019            f.run(&[
15020                b"ZRANGEBYSCORE",
15021                b"z",
15022                b"1",
15023                b"3",
15024                b"WITHSCORES",
15025                b"LIMIT",
15026                b"0",
15027                b"2"
15028            ]),
15029            both
15030        );
15031        assert_eq!(
15032            f.run(&[
15033                b"ZRANGEBYSCORE",
15034                b"z",
15035                b"1",
15036                b"3",
15037                b"LIMIT",
15038                b"0",
15039                b"2",
15040                b"WITHSCORES"
15041            ]),
15042            both
15043        );
15044        // LIMIT on a range by rank is refused after the whole option list has
15045        // been read, so this complains about LIMIT and not about WITHSCORES.
15046        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
15047        assert_eq!(
15048            f.run(&[
15049                b"ZREVRANGE",
15050                b"z",
15051                b"0",
15052                b"-1",
15053                b"WITHSCORES",
15054                b"LIMIT",
15055                b"0",
15056                b"1"
15057            ]),
15058            needs_by
15059        );
15060        assert_eq!(
15061            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
15062            needs_by
15063        );
15064        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
15065        assert_eq!(
15066            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
15067            not_bylex
15068        );
15069        assert_eq!(
15070            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
15071            not_bylex
15072        );
15073        // Two modes at once, an option nobody knows, a LIMIT missing its count,
15074        // and the three number errors, which are three different sentences.
15075        for cmd in [
15076            &[
15077                b"ZRANGE".as_slice(),
15078                b"z",
15079                b"0",
15080                b"-1",
15081                b"BYSCORE",
15082                b"BYLEX",
15083            ][..],
15084            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
15085            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
15086        ] {
15087            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
15088        }
15089        assert_eq!(
15090            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
15091            "-ERR min or max is not a float\r\n"
15092        );
15093        assert_eq!(
15094            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
15095            "-ERR min or max not valid string range item\r\n"
15096        );
15097        assert_eq!(
15098            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
15099            "-ERR value is not an integer or out of range\r\n"
15100        );
15101    }
15102
15103    /// `WITHSCORES` is the one place in this group where the two protocols
15104    /// disagree about the shape of the reply and not just the type of a value.
15105    #[test]
15106    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
15107        let mut f = Fixture::new();
15108        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15109        assert_eq!(
15110            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
15111            "*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"
15112        );
15113        f.out = Out::new(Proto::Resp3);
15114        assert_eq!(
15115            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
15116            "*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"
15117        );
15118        assert_eq!(
15119            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
15120            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
15121        );
15122    }
15123
15124    /// The store form, which is the same parse with the destination in front.
15125    #[test]
15126    fn a_range_store_writes_the_window_into_another_key() {
15127        let mut f = Fixture::new();
15128        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15129        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
15130        // A window that selects nothing deletes the destination rather than
15131        // leaving an empty sorted set, because an empty one does not exist.
15132        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
15133        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
15134        assert_eq!(
15135            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
15136            ":2\r\n"
15137        );
15138        assert_eq!(
15139            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
15140            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
15141        );
15142        // The destination is allowed to be the source, because the result is
15143        // built whole before anything is written over.
15144        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
15145        assert_eq!(
15146            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
15147            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
15148        );
15149        // It takes every option ZRANGE takes except WITHSCORES, which is a
15150        // plain syntax error here and not the sentence about BYLEX.
15151        assert_eq!(
15152            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
15153            "-ERR syntax error\r\n"
15154        );
15155    }
15156
15157    /// The three removals, which are the read side's window with the walk
15158    /// turned into a removal and no options at all.
15159    #[test]
15160    fn the_three_removals_share_their_window_with_the_reads() {
15161        let mut f = Fixture::new();
15162        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15163        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
15164        assert_eq!(
15165            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
15166            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
15167        );
15168        assert_eq!(
15169            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
15170            ":1\r\n"
15171        );
15172        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
15173        // The last member going takes the key with it.
15174        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
15175        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
15176        assert_eq!(
15177            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
15178            ":0\r\n"
15179        );
15180        assert_eq!(
15181            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
15182            "-ERR value is not an integer or out of range\r\n"
15183        );
15184    }
15185
15186    /// The algebra, which is one gather and three names for it.
15187    #[test]
15188    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
15189        let mut f = Fixture::new();
15190        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15191        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
15192        assert_eq!(
15193            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
15194            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
15195        );
15196        // The scores are added where a member is in both, and the answer comes
15197        // out in the order those combined scores put it in.
15198        assert_eq!(
15199            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
15200            "*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"
15201        );
15202        assert_eq!(
15203            f.run(&[
15204                b"ZUNION",
15205                b"2",
15206                b"z",
15207                b"y",
15208                b"WEIGHTS",
15209                b"2",
15210                b"3",
15211                b"WITHSCORES"
15212            ]),
15213            "*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"
15214        );
15215        assert_eq!(
15216            f.run(&[
15217                b"ZUNION",
15218                b"2",
15219                b"z",
15220                b"y",
15221                b"AGGREGATE",
15222                b"MIN",
15223                b"WITHSCORES"
15224            ]),
15225            "*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"
15226        );
15227        assert_eq!(
15228            f.run(&[
15229                b"ZUNION",
15230                b"2",
15231                b"z",
15232                b"y",
15233                b"AGGREGATE",
15234                b"MAX",
15235                b"WITHSCORES"
15236            ]),
15237            "*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"
15238        );
15239        assert_eq!(
15240            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
15241            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
15242        );
15243        assert_eq!(
15244            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
15245            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
15246        );
15247        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
15248        // A plain set is an input, and it behaves as a sorted set in which
15249        // every member scores one.
15250        f.run(&[b"SADD", b"p", b"a", b"d"]);
15251        assert_eq!(
15252            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
15253            "*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"
15254        );
15255        // A difference never combines two scores, so it has nothing for either
15256        // of the two options to do and refuses both.
15257        for cmd in [
15258            &[
15259                b"ZDIFF".as_slice(),
15260                b"2",
15261                b"z",
15262                b"y",
15263                b"WEIGHTS",
15264                b"1",
15265                b"1",
15266            ][..],
15267            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
15268        ] {
15269            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
15270        }
15271    }
15272
15273    /// The count of keys, which is what lets a key be named `WEIGHTS`.
15274    #[test]
15275    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
15276        let mut f = Fixture::new();
15277        f.run(&[b"ZADD", b"z", b"1", b"a"]);
15278        f.run(&[b"ZADD", b"y", b"2", b"b"]);
15279        // Redis names the command in this one, so each spelling says its own.
15280        assert_eq!(
15281            f.run(&[b"ZUNION", b"0", b"z"]),
15282            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
15283        );
15284        assert_eq!(
15285            f.run(&[b"ZUNION", b"-1", b"z"]),
15286            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
15287        );
15288        assert_eq!(
15289            f.run(&[b"ZINTERCARD", b"0", b"z"]),
15290            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
15291        );
15292        // A count bigger than the line is a plain syntax error, which reads
15293        // oddly and is what Redis says.
15294        assert_eq!(
15295            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
15296            "-ERR syntax error\r\n"
15297        );
15298        assert_eq!(
15299            f.run(&[b"ZUNION", b"x", b"z"]),
15300            "-ERR value is not an integer or out of range\r\n"
15301        );
15302        // A WEIGHTS list that is not one per key is a syntax error, and a
15303        // weight that is not a number gets a sentence of its own.
15304        assert_eq!(
15305            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
15306            "-ERR syntax error\r\n"
15307        );
15308        assert_eq!(
15309            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
15310            "-ERR weight value is not a float\r\n"
15311        );
15312        assert_eq!(
15313            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
15314            "-ERR syntax error\r\n"
15315        );
15316    }
15317
15318    /// The three store forms, which answer a count and take no WITHSCORES.
15319    #[test]
15320    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
15321        let mut f = Fixture::new();
15322        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15323        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
15324        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
15325        assert_eq!(
15326            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
15327            "*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"
15328        );
15329        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
15330        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
15331        // An empty result deletes the destination rather than leaving an empty
15332        // sorted set, because an empty one does not exist.
15333        assert_eq!(
15334            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
15335            ":0\r\n"
15336        );
15337        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
15338        // The destination is allowed to name its own source.
15339        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
15340        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
15341        for cmd in [
15342            &[
15343                b"ZUNIONSTORE".as_slice(),
15344                b"d",
15345                b"2",
15346                b"z",
15347                b"y",
15348                b"WITHSCORES",
15349            ][..],
15350            &[
15351                b"ZDIFFSTORE",
15352                b"d",
15353                b"2",
15354                b"z",
15355                b"y",
15356                b"WEIGHTS",
15357                b"1",
15358                b"1",
15359            ],
15360        ] {
15361            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
15362        }
15363    }
15364
15365    /// `ZINTERCARD`, which counts without building anything.
15366    #[test]
15367    fn intercard_counts_and_stops_at_its_limit() {
15368        let mut f = Fixture::new();
15369        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15370        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
15371        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
15372        // A limit of zero is no limit, which is Redis's reading of it.
15373        assert_eq!(
15374            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
15375            ":2\r\n"
15376        );
15377        assert_eq!(
15378            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
15379            ":1\r\n"
15380        );
15381        // A negative limit and a limit that is not a number at all get the same
15382        // sentence, which looks like a mistake in Redis and is copied as one.
15383        let bad = "-ERR LIMIT can't be negative\r\n";
15384        assert_eq!(
15385            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
15386            bad
15387        );
15388        assert_eq!(
15389            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
15390            bad
15391        );
15392        for cmd in [
15393            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
15394            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
15395            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
15396        ] {
15397            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
15398        }
15399    }
15400
15401    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
15402    #[test]
15403    fn a_draw_answers_one_member_or_an_array_of_them() {
15404        let mut f = Fixture::new();
15405        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15406        // No count is one member or a nil, a count is an array that may be
15407        // empty, and those are two reply types the client has to tell apart.
15408        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
15409        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
15410        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
15411        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
15412        // A positive count draws without replacement, so a count over the size
15413        // answers the whole set and never a member twice.
15414        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
15415        assert!(all.starts_with("*3\r\n"), "{all}");
15416        for m in ["a", "b", "c"] {
15417            assert!(all.contains(m), "{all}");
15418        }
15419        // A negative one draws with replacement and answers exactly as many as
15420        // it was asked for, whatever the size of the set.
15421        assert!(
15422            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
15423            "five draws with replacement"
15424        );
15425        assert!(
15426            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
15427                .starts_with("*4\r\n"),
15428            "two pairs, flat on RESP2"
15429        );
15430        f.out = Out::new(Proto::Resp3);
15431        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
15432        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
15433        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
15434        f.out = Out::new(Proto::Resp2);
15435        assert_eq!(
15436            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
15437            "-ERR syntax error\r\n"
15438        );
15439        assert_eq!(
15440            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
15441            "-ERR value is not an integer or out of range\r\n"
15442        );
15443    }
15444
15445    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
15446    #[test]
15447    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
15448        let mut f = Fixture::new();
15449        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15450        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";
15451        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
15452        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
15453        assert_eq!(
15454            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
15455            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
15456        );
15457        assert_eq!(
15458            f.run(&[b"ZSCAN", b"nokey", b"0"]),
15459            "*2\r\n$1\r\n0\r\n*0\r\n"
15460        );
15461        // A score stays a bulk string on RESP3, which is the one place the two
15462        // protocols agree about a score and everywhere else they do not.
15463        f.out = Out::new(Proto::Resp3);
15464        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
15465        f.out = Out::new(Proto::Resp2);
15466        assert_eq!(
15467            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
15468            "-ERR NOVALUES option can only be used in HSCAN\r\n"
15469        );
15470        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
15471        assert_eq!(
15472            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
15473            "-ERR syntax error\r\n"
15474        );
15475    }
15476
15477    /// The count is what decides the shape, and its value is not.
15478    #[test]
15479    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
15480        let mut f = Fixture::new();
15481        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15482        // No count, so one flat pair, and the score is a bulk string on RESP2.
15483        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
15484        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
15485        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
15486        // A count, so pairs, and on RESP2 they are flattened into one run.
15487        assert_eq!(
15488            f.run(&[b"ZPOPMIN", b"z", b"2"]),
15489            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
15490        );
15491        // An empty array rather than a null, which is where a sorted set pop and
15492        // a list pop part company, and the same answer a count of zero gives.
15493        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
15494        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
15495        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
15496        // The last member takes the key with it.
15497        assert_eq!(
15498            f.run(&[b"ZPOPMIN", b"z", b"9"]),
15499            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
15500        );
15501        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
15502
15503        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
15504        f.out = Out::new(Proto::Resp3);
15505        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
15506        assert_eq!(
15507            f.run(&[b"ZPOPMIN", b"z", b"1"]),
15508            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
15509        );
15510        f.out = Out::new(Proto::Resp2);
15511        // Both of these are the range error rather than the usual sentence about
15512        // integers, which is the odd answer and so the one worth copying.
15513        let bad = "-ERR value is out of range, must be positive\r\n";
15514        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
15515        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
15516        assert_eq!(
15517            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
15518            "-ERR syntax error\r\n"
15519        );
15520    }
15521
15522    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
15523    #[test]
15524    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
15525        let mut f = Fixture::new();
15526        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15527        assert_eq!(
15528            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
15529            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
15530        );
15531        // Nested on RESP2 as well, because the key name is already in front of
15532        // the pairs and there is nothing left to flatten into.
15533        assert_eq!(
15534            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
15535            "*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"
15536        );
15537        // A null array and not a null, the same as LMPOP.
15538        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
15539        f.out = Out::new(Proto::Resp3);
15540        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
15541        f.out = Out::new(Proto::Resp2);
15542        let numkeys = "-ERR numkeys should be greater than 0\r\n";
15543        for bad in [
15544            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
15545            &[b"ZMPOP", b"-1", b"z", b"MIN"],
15546            &[b"ZMPOP", b"x", b"z", b"MIN"],
15547        ] {
15548            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
15549        }
15550        let count = "-ERR count should be greater than 0\r\n";
15551        for bad in [
15552            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
15553            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
15554            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
15555        ] {
15556            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
15557        }
15558        let syntax = "-ERR syntax error\r\n";
15559        for bad in [
15560            // Two keys named and one given, so the word that should have been
15561            // the direction is a key and there is no direction left.
15562            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
15563            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
15564            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
15565            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
15566        ] {
15567            assert_eq!(f.run(bad), syntax, "{bad:?}");
15568        }
15569    }
15570
15571    /// The three that wait, when there is something there and they do not have
15572    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
15573    #[test]
15574    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
15575        let mut f = Fixture::new();
15576        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15577        assert_eq!(
15578            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
15579            (
15580                Flow::Continue,
15581                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
15582            )
15583        );
15584        assert_eq!(
15585            f.run(&[b"BZPOPMAX", b"z", b"0"]),
15586            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
15587        );
15588        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
15589        assert_eq!(
15590            f.run(&[
15591                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
15592            ]),
15593            "*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"
15594        );
15595        f.out = Out::new(Proto::Resp3);
15596        assert_eq!(
15597            f.run(&[b"BZPOPMIN", b"z", b"0"]),
15598            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
15599        );
15600        f.out = Out::new(Proto::Resp2);
15601        // Nothing to take, so the client is parked and nothing was written.
15602        assert_eq!(
15603            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
15604            (Flow::Block, String::new())
15605        );
15606        assert_eq!(
15607            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
15608            (Flow::Block, String::new())
15609        );
15610        // The timeout is read before the key count, so this complains about the
15611        // timeout and not about the count.
15612        assert_eq!(
15613            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
15614            "-ERR timeout is not a float or out of range\r\n"
15615        );
15616        assert_eq!(
15617            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
15618            "-ERR numkeys should be greater than 0\r\n"
15619        );
15620        assert_eq!(
15621            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
15622            "-ERR timeout is negative\r\n"
15623        );
15624    }
15625
15626    /// A parked sorted set client is served by whatever puts a member under one
15627    /// of its keys, and is not served by something of another type landing
15628    /// there.
15629    #[test]
15630    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
15631        let mut f = Fixture::new();
15632        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
15633        assert_eq!(f.server.parked(), 1);
15634        // A string under the key is not what it asked for, so it stays parked
15635        // rather than being handed a WRONGTYPE on a command that was accepted.
15636        f.run(&[b"SET", b"z", b"v"]);
15637        let mut out = Out::new(Proto::Resp2);
15638        assert!(!f.server.serve_waiter(7, 0, &mut out));
15639        assert!(out.as_slice().is_empty());
15640        f.run(&[b"DEL", b"z"]);
15641        f.run(&[b"ZADD", b"z", b"5", b"m"]);
15642        assert!(f.server.serve_waiter(7, 0, &mut out));
15643        assert_eq!(
15644            core::str::from_utf8(out.as_slice()).expect("ascii"),
15645            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
15646        );
15647        // And the member is gone, which is what makes a queue of workers on a
15648        // sorted set work at all.
15649        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
15650    }
15651
15652    #[test]
15653    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
15654        let mut f = Fixture::new();
15655        f.run(&[b"SET", b"s", b"v"]);
15656        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
15657        for cmd in [
15658            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
15659            &[b"ZINCRBY", b"s", b"1", b"a"],
15660            &[b"ZCARD", b"s"],
15661            &[b"ZSCORE", b"s", b"a"],
15662            &[b"ZMSCORE", b"s", b"a"],
15663            &[b"ZREM", b"s", b"a"],
15664            &[b"ZRANK", b"s", b"a"],
15665            &[b"ZREVRANK", b"s", b"a"],
15666            &[b"ZCOUNT", b"s", b"1", b"2"],
15667            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
15668            &[b"ZRANGE", b"s", b"0", b"-1"],
15669            &[b"ZREVRANGE", b"s", b"0", b"-1"],
15670            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
15671            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
15672            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
15673            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
15674            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
15675            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
15676            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
15677            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
15678            &[b"ZUNION", b"1", b"s"],
15679            &[b"ZINTER", b"1", b"s"],
15680            &[b"ZDIFF", b"1", b"s"],
15681            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
15682            &[b"ZINTERSTORE", b"d", b"1", b"s"],
15683            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
15684            &[b"ZINTERCARD", b"1", b"s"],
15685            &[b"ZRANDMEMBER", b"s"],
15686            &[b"ZSCAN", b"s", b"0"],
15687            &[b"ZPOPMIN", b"s"],
15688            &[b"ZPOPMAX", b"s", b"2"],
15689            &[b"ZMPOP", b"1", b"s", b"MIN"],
15690            &[b"BZPOPMIN", b"s", b"0"],
15691            &[b"BZPOPMAX", b"s", b"0"],
15692            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
15693        ] {
15694            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
15695        }
15696        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
15697    }
15698
15699    /// The same churn the set, the string and the list get, because a sorted
15700    /// set that leaks a tree node per add looks exactly like one that does not
15701    /// until it has run for an afternoon.
15702    /// Not under Miri, for the reason on `churning_sets_does_not_grow_the_server`.
15703    #[cfg_attr(miri, ignore = "the volume is the claim")]
15704    #[test]
15705    fn churning_sorted_sets_does_not_grow_the_server() {
15706        let mut f = Fixture::new();
15707        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
15708        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
15709        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
15710        for i in 0..200 {
15711            args.push(&scores[i]);
15712            args.push(&members[i]);
15713        }
15714
15715        f.run(&args);
15716        f.run(&[b"DEL", b"z"]);
15717        f.server.compact_step();
15718        let after_first = f.server.memory_bytes();
15719
15720        for _ in 0..200 {
15721            f.run(&args);
15722            f.run(&[b"DEL", b"z"]);
15723            f.server.compact_step();
15724        }
15725        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
15726        assert!(
15727            f.server.memory_bytes() <= after_first * 2,
15728            "held {} after two hundred passes against {after_first} after one",
15729            f.server.memory_bytes()
15730        );
15731    }
15732
15733    // ------------------------------------------------------------------- geo
15734
15735    /// The three places every Redis geo example uses, and one more.
15736    ///
15737    /// Every reply this section asserts on came off a running 8.10.1 with these
15738    /// three loaded, byte for byte, including the number of digits in a
15739    /// coordinate and the four places on a distance.
15740    fn sicily(f: &mut Fixture) {
15741        f.run(&[
15742            b"GEOADD",
15743            b"Sicily",
15744            b"13.361389",
15745            b"38.115556",
15746            b"Palermo",
15747            b"15.087269",
15748            b"37.502669",
15749            b"Catania",
15750        ]);
15751        f.run(&[
15752            b"GEOADD",
15753            b"Sicily",
15754            b"13.583333",
15755            b"37.316667",
15756            b"Agrigento",
15757        ]);
15758    }
15759
15760    #[test]
15761    fn places_go_in_as_scores_and_come_back_as_positions() {
15762        let mut f = Fixture::new();
15763        assert_eq!(
15764            f.run(&[
15765                b"GEOADD",
15766                b"Sicily",
15767                b"13.361389",
15768                b"38.115556",
15769                b"Palermo",
15770                b"15.087269",
15771                b"37.502669",
15772                b"Catania"
15773            ]),
15774            ":2\r\n"
15775        );
15776        // A geo key is a sorted set and says so, which is not an implementation
15777        // detail either: a client removes a place with ZREM and counts them
15778        // with ZCARD, and the score is the number a real server stores.
15779        assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
15780        assert_eq!(
15781            f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
15782            "$16\r\n3479099956230698\r\n"
15783        );
15784        assert_eq!(
15785            f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
15786            "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
15787        );
15788        assert_eq!(
15789            f.run(&[
15790                b"GEOHASH",
15791                b"Sicily",
15792                b"Palermo",
15793                b"Catania",
15794                b"NonExisting"
15795            ]),
15796            "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
15797        );
15798        // A key that is not there is an empty one, and the two nulls are not
15799        // the same null: GEOPOS answers the array one and GEOHASH the string
15800        // one, which a RESP2 client can tell apart.
15801        assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
15802        assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
15803    }
15804
15805    #[test]
15806    fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
15807        let mut f = Fixture::new();
15808        sicily(&mut f);
15809        assert_eq!(
15810            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
15811            "$11\r\n166274.1516\r\n"
15812        );
15813        assert_eq!(
15814            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
15815            "$8\r\n166.2742\r\n"
15816        );
15817        assert_eq!(
15818            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
15819            "$8\r\n103.3182\r\n"
15820        );
15821        // A member that is not there and a key that is not there are the same
15822        // nil, and the unit is read before the key is looked up, so a bad unit
15823        // on a missing key is still an error.
15824        assert_eq!(
15825            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
15826            "$-1\r\n"
15827        );
15828        assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
15829        assert_eq!(
15830            f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
15831            "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
15832        );
15833        assert_eq!(
15834            f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
15835            "-ERR syntax error\r\n"
15836        );
15837    }
15838
15839    #[test]
15840    fn a_search_finds_what_is_inside_it_nearest_first() {
15841        let mut f = Fixture::new();
15842        sicily(&mut f);
15843        let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
15844        assert_eq!(
15845            f.run(&[
15846                b"GEOSEARCH",
15847                b"Sicily",
15848                b"FROMLONLAT",
15849                b"15",
15850                b"37",
15851                b"BYRADIUS",
15852                b"200",
15853                b"km",
15854                b"ASC"
15855            ]),
15856            all
15857        );
15858        // The older spelling of the same search, which is the same nine boxes
15859        // and the same order.
15860        assert_eq!(
15861            f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
15862            all
15863        );
15864        assert_eq!(
15865            f.run(&[
15866                b"GEORADIUS_RO",
15867                b"Sicily",
15868                b"15",
15869                b"37",
15870                b"200",
15871                b"km",
15872                b"ASC"
15873            ]),
15874            all
15875        );
15876        // A count with no ordering means the nearest ones, so DESC has to be
15877        // asked for to get the far end.
15878        assert_eq!(
15879            f.run(&[
15880                b"GEORADIUS",
15881                b"Sicily",
15882                b"15",
15883                b"37",
15884                b"200",
15885                b"km",
15886                b"DESC",
15887                b"COUNT",
15888                b"1"
15889            ]),
15890            "*1\r\n$7\r\nPalermo\r\n"
15891        );
15892        assert_eq!(
15893            f.run(&[
15894                b"GEORADIUS",
15895                b"Sicily",
15896                b"15",
15897                b"37",
15898                b"200",
15899                b"km",
15900                b"COUNT",
15901                b"1"
15902            ]),
15903            "*1\r\n$7\r\nCatania\r\n"
15904        );
15905        // Nothing inside a kilometre of that point, and nothing in a key that
15906        // is not there, and both are the empty array rather than an error.
15907        let empty = "*0\r\n";
15908        assert_eq!(
15909            f.run(&[
15910                b"GEOSEARCH",
15911                b"Sicily",
15912                b"FROMLONLAT",
15913                b"15",
15914                b"37",
15915                b"BYRADIUS",
15916                b"1",
15917                b"km"
15918            ]),
15919            empty
15920        );
15921        assert_eq!(
15922            f.run(&[
15923                b"GEOSEARCH",
15924                b"nokey",
15925                b"FROMLONLAT",
15926                b"15",
15927                b"37",
15928                b"BYRADIUS",
15929                b"1",
15930                b"km"
15931            ]),
15932            empty
15933        );
15934        assert_eq!(
15935            f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
15936            empty
15937        );
15938    }
15939
15940    #[test]
15941    fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
15942        let mut f = Fixture::new();
15943        sicily(&mut f);
15944        assert_eq!(
15945            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
15946            "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
15947        );
15948        // The member itself is nothing away from itself, which is where the
15949        // fixed point writer's zero shows up on the wire.
15950        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";
15951        assert_eq!(
15952            f.run(&[
15953                b"GEORADIUSBYMEMBER_RO",
15954                b"Sicily",
15955                b"Agrigento",
15956                b"100",
15957                b"km",
15958                b"WITHDIST"
15959            ]),
15960            with_dist
15961        );
15962        assert_eq!(
15963            f.run(&[
15964                b"GEOSEARCH",
15965                b"Sicily",
15966                b"FROMMEMBER",
15967                b"Agrigento",
15968                b"BYRADIUS",
15969                b"100",
15970                b"km",
15971                b"ASC",
15972                b"WITHDIST"
15973            ]),
15974            with_dist
15975        );
15976        assert_eq!(
15977            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
15978            "-ERR could not decode requested zset member\r\n"
15979        );
15980    }
15981
15982    #[test]
15983    fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
15984        let mut f = Fixture::new();
15985        sicily(&mut f);
15986        // Three options asked for, so each result is a four element array of
15987        // the member, the distance, the hash and a pair. The order of the three
15988        // is Redis's and not the order they were written in the command.
15989        assert_eq!(
15990            f.run(&[
15991                b"GEOSEARCH",
15992                b"Sicily",
15993                b"FROMLONLAT",
15994                b"15",
15995                b"37",
15996                b"BYBOX",
15997                b"400",
15998                b"400",
15999                b"km",
16000                b"ASC",
16001                b"WITHCOORD",
16002                b"WITHDIST",
16003                b"WITHHASH"
16004            ]),
16005            "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
16006             $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
16007             *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
16008             $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
16009             *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
16010             $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
16011        );
16012    }
16013
16014    #[test]
16015    fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
16016        let mut f = Fixture::new();
16017        sicily(&mut f);
16018        let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
16019                      $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
16020                      $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
16021        assert_eq!(
16022            f.run(&[
16023                b"GEOSEARCHSTORE",
16024                b"dst",
16025                b"Sicily",
16026                b"FROMLONLAT",
16027                b"15",
16028                b"37",
16029                b"BYRADIUS",
16030                b"200",
16031                b"km",
16032                b"ASC"
16033            ]),
16034            ":3\r\n"
16035        );
16036        assert_eq!(
16037            f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
16038            hashes
16039        );
16040        // The same again through the older spelling, which stores the same
16041        // scores, so a key written by either is a geo key.
16042        assert_eq!(
16043            f.run(&[
16044                b"GEORADIUS",
16045                b"Sicily",
16046                b"15",
16047                b"37",
16048                b"200",
16049                b"km",
16050                b"STORE",
16051                b"dst3"
16052            ]),
16053            ":3\r\n"
16054        );
16055        assert_eq!(
16056            f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
16057            hashes
16058        );
16059        // STOREDIST stores the distance in the search unit instead, and those
16060        // are full doubles rather than the four places WITHDIST writes. The
16061        // numbers on the right are what 8.10.1 stored for this search, and they
16062        // are compared with a tolerance rather than byte for byte because the
16063        // last bit of a haversine is the platform's sin, cos and asin: this
16064        // machine and that one disagree in the sixteenth digit, and so do two
16065        // Redis builds. Everything a client actually reads back is four places
16066        // and is asserted exactly above.
16067        assert_eq!(
16068            f.run(&[
16069                b"GEOSEARCHSTORE",
16070                b"dst2",
16071                b"Sicily",
16072                b"FROMLONLAT",
16073                b"15",
16074                b"37",
16075                b"BYRADIUS",
16076                b"200",
16077                b"km",
16078                b"ASC",
16079                b"STOREDIST"
16080            ]),
16081            ":3\r\n"
16082        );
16083        for (member, want) in [
16084            ("Catania", 56.441_257_870_158_19),
16085            ("Agrigento", 130.423_487_067_147_14),
16086            ("Palermo", 190.442_429_847_757_92),
16087        ] {
16088            let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
16089            let got: f64 = reply
16090                .trim_start_matches(|c: char| c != '\n')
16091                .trim()
16092                .parse()
16093                .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
16094            assert!(
16095                (got - want).abs() < 1e-9,
16096                "{member} scored {got} not {want}"
16097            );
16098        }
16099        // The order they went in is the order the scores put them in, which is
16100        // the point of storing the distance rather than the hash.
16101        assert_eq!(
16102            f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
16103            "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
16104        );
16105        // A search that finds nothing takes the destination with it rather than
16106        // leaving what was there, and a source key that is not there is a
16107        // search that finds nothing.
16108        assert_eq!(
16109            f.run(&[
16110                b"GEOSEARCHSTORE",
16111                b"dst",
16112                b"nokey",
16113                b"FROMLONLAT",
16114                b"15",
16115                b"37",
16116                b"BYRADIUS",
16117                b"200",
16118                b"km"
16119            ]),
16120            ":0\r\n"
16121        );
16122        assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
16123    }
16124
16125    #[test]
16126    fn the_gates_on_geoadd_are_the_ones_zadd_has() {
16127        let mut f = Fixture::new();
16128        sicily(&mut f);
16129        // XX on a member that is already where it is changes nothing, and NX on
16130        // one that is there refuses to move it.
16131        assert_eq!(
16132            f.run(&[
16133                b"GEOADD",
16134                b"Sicily",
16135                b"XX",
16136                b"CH",
16137                b"13.361389",
16138                b"38.115556",
16139                b"Palermo"
16140            ]),
16141            ":0\r\n"
16142        );
16143        assert_eq!(
16144            f.run(&[
16145                b"GEOADD",
16146                b"Sicily",
16147                b"NX",
16148                b"13.361389",
16149                b"38.9",
16150                b"Palermo"
16151            ]),
16152            ":0\r\n"
16153        );
16154        assert_eq!(
16155            f.run(&[
16156                b"GEOADD",
16157                b"Sicily",
16158                b"CH",
16159                b"13.361389",
16160                b"38.9",
16161                b"Palermo"
16162            ]),
16163            ":1\r\n"
16164        );
16165        // Out of range, and nothing is stored: the whole call is refused rather
16166        // than the good pairs going in and the bad one stopping it.
16167        assert_eq!(
16168            f.run(&[
16169                b"GEOADD",
16170                b"new",
16171                b"13.361389",
16172                b"38.115556",
16173                b"here",
16174                b"181",
16175                b"38",
16176                b"there"
16177            ]),
16178            "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
16179        );
16180        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
16181        assert_eq!(
16182            f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
16183            "-ERR value is not a valid float\r\n"
16184        );
16185        // The count of triples is checked before the two gates are, and a call
16186        // with no triples at all reaches the same sentence.
16187        assert_eq!(
16188            f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
16189            "-ERR syntax error\r\n"
16190        );
16191        assert_eq!(
16192            f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
16193            "-ERR syntax error\r\n"
16194        );
16195        assert_eq!(
16196            f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
16197            "-ERR syntax error\r\n"
16198        );
16199        assert_eq!(
16200            f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
16201            "-ERR wrong number of arguments for 'geoadd' command\r\n"
16202        );
16203    }
16204
16205    /// The sentences a search answers, which are its contract as much as the
16206    /// results are.
16207    #[test]
16208    fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
16209        let mut f = Fixture::new();
16210        sicily(&mut f);
16211        let cases: &[(&[&[u8]], &str)] = &[
16212            (
16213                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
16214                "-ERR need numeric radius\r\n",
16215            ),
16216            (
16217                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
16218                "-ERR radius cannot be negative\r\n",
16219            ),
16220            (
16221                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
16222                "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
16223            ),
16224            (
16225                &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
16226                "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
16227            ),
16228            (
16229                &[
16230                    b"GEOSEARCH",
16231                    b"Sicily",
16232                    b"FROMLONLAT",
16233                    b"15",
16234                    b"37",
16235                    b"BYBOX",
16236                    b"x",
16237                    b"1",
16238                    b"km",
16239                ],
16240                "-ERR need numeric width\r\n",
16241            ),
16242            (
16243                &[
16244                    b"GEOSEARCH",
16245                    b"Sicily",
16246                    b"FROMLONLAT",
16247                    b"15",
16248                    b"37",
16249                    b"BYBOX",
16250                    b"1",
16251                    b"y",
16252                    b"km",
16253                ],
16254                "-ERR need numeric height\r\n",
16255            ),
16256            (
16257                &[
16258                    b"GEOSEARCH",
16259                    b"Sicily",
16260                    b"FROMLONLAT",
16261                    b"15",
16262                    b"37",
16263                    b"BYBOX",
16264                    b"-1",
16265                    b"1",
16266                    b"km",
16267                ],
16268                "-ERR height or width cannot be negative\r\n",
16269            ),
16270            (
16271                &[
16272                    b"GEOSEARCH",
16273                    b"Sicily",
16274                    b"FROMLONLAT",
16275                    b"15",
16276                    b"37",
16277                    b"BYRADIUS",
16278                    b"1",
16279                    b"km",
16280                    b"ANY",
16281                ],
16282                "-ERR the ANY argument requires COUNT argument\r\n",
16283            ),
16284            (
16285                &[
16286                    b"GEOSEARCH",
16287                    b"Sicily",
16288                    b"FROMLONLAT",
16289                    b"15",
16290                    b"37",
16291                    b"BYRADIUS",
16292                    b"1",
16293                    b"km",
16294                    b"COUNT",
16295                    b"0",
16296                ],
16297                "-ERR COUNT must be > 0\r\n",
16298            ),
16299            (
16300                &[
16301                    b"GEOSEARCH",
16302                    b"Sicily",
16303                    b"BYRADIUS",
16304                    b"1",
16305                    b"km",
16306                    b"BYBOX",
16307                    b"1",
16308                    b"1",
16309                    b"km",
16310                ],
16311                "-ERR syntax error\r\n",
16312            ),
16313            (
16314                &[
16315                    b"GEOSEARCH",
16316                    b"Sicily",
16317                    b"FROMMEMBER",
16318                    b"Palermo",
16319                    b"FROMLONLAT",
16320                    b"1",
16321                    b"2",
16322                    b"BYRADIUS",
16323                    b"1",
16324                    b"km",
16325                ],
16326                "-ERR syntax error\r\n",
16327            ),
16328            // The two options a GEOSEARCH cannot leave out, each with its own
16329            // sentence, and the command quoted the way the client spelled it.
16330            (
16331                &[
16332                    b"geosearch",
16333                    b"Sicily",
16334                    b"BYRADIUS",
16335                    b"1",
16336                    b"km",
16337                    b"ASC",
16338                    b"WITHDIST",
16339                ],
16340                "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
16341            ),
16342            (
16343                &[
16344                    b"GEOSEARCH",
16345                    b"Sicily",
16346                    b"FROMLONLAT",
16347                    b"15",
16348                    b"37",
16349                    b"ASC",
16350                    b"WITHDIST",
16351                ],
16352                "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
16353            ),
16354            // A store cannot also be asked for the distance, and the two
16355            // families name themselves differently in the same sentence.
16356            (
16357                &[
16358                    b"GEOSEARCHSTORE",
16359                    b"d",
16360                    b"Sicily",
16361                    b"FROMLONLAT",
16362                    b"15",
16363                    b"37",
16364                    b"BYRADIUS",
16365                    b"1",
16366                    b"km",
16367                    b"WITHCOORD",
16368                ],
16369                "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
16370            ),
16371            (
16372                &[
16373                    b"GEORADIUS",
16374                    b"Sicily",
16375                    b"15",
16376                    b"37",
16377                    b"1",
16378                    b"km",
16379                    b"WITHDIST",
16380                    b"STORE",
16381                    b"d",
16382                ],
16383                "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
16384            ),
16385            // The read only forms have no store at all, so the word is a stray
16386            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
16387            (
16388                &[
16389                    b"GEORADIUS_RO",
16390                    b"Sicily",
16391                    b"15",
16392                    b"37",
16393                    b"1",
16394                    b"km",
16395                    b"STORE",
16396                    b"d",
16397                ],
16398                "-ERR syntax error\r\n",
16399            ),
16400            (
16401                &[
16402                    b"GEOSEARCH",
16403                    b"Sicily",
16404                    b"FROMLONLAT",
16405                    b"15",
16406                    b"37",
16407                    b"BYRADIUS",
16408                    b"1",
16409                    b"km",
16410                    b"STOREDIST",
16411                ],
16412                "-ERR syntax error\r\n",
16413            ),
16414        ];
16415        for (parts, want) in cases {
16416            assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
16417        }
16418    }
16419
16420    /// A wrong type wins over a bad argument, because the key is looked up
16421    /// first, and every one of the ten says the same thing about it.
16422    #[test]
16423    fn every_geo_command_says_wrongtype() {
16424        let mut f = Fixture::new();
16425        f.run(&[b"SET", b"s", b"v"]);
16426        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
16427        let cases: &[&[&[u8]]] = &[
16428            &[b"GEOADD", b"s", b"13", b"38", b"m"],
16429            &[b"GEOPOS", b"s", b"m"],
16430            &[b"GEOHASH", b"s", b"m"],
16431            &[b"GEODIST", b"s", b"a", b"b"],
16432            &[
16433                b"GEOSEARCH",
16434                b"s",
16435                b"FROMLONLAT",
16436                b"15",
16437                b"37",
16438                b"BYRADIUS",
16439                b"1",
16440                b"km",
16441            ],
16442            &[
16443                b"GEOSEARCHSTORE",
16444                b"d",
16445                b"s",
16446                b"FROMLONLAT",
16447                b"15",
16448                b"37",
16449                b"BYRADIUS",
16450                b"1",
16451                b"km",
16452            ],
16453            &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
16454            &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
16455            &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
16456            &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
16457        ];
16458        for case in cases {
16459            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
16460        }
16461        // And it wins over an argument that will not parse, which is the whole
16462        // reason the lookup comes first.
16463        assert_eq!(
16464            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
16465            wrong
16466        );
16467    }
16468
16469    // ----------------------------------------------------------------- array
16470
16471    #[test]
16472    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
16473        let mut f = Fixture::new();
16474        // Three consecutive positions from a high index, and the reply is how
16475        // many of them were empty before rather than how many were written.
16476        assert_eq!(
16477            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
16478            ":3\r\n"
16479        );
16480        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
16481        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
16482        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
16483        // A hole and a key that is not there are the same answer.
16484        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
16485        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
16486        assert_eq!(
16487            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
16488            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
16489        );
16490        // Scattered pairs in one command, last write wins within it.
16491        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
16492        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
16493    }
16494
16495    /// The two numbers an array reports are not the same number, and one of
16496    /// them does not fit a signed integer.
16497    #[test]
16498    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
16499        let mut f = Fixture::new();
16500        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
16501        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
16502        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
16503        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
16504        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
16505        // Deleting in the middle leaves the high water mark where it was.
16506        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
16507        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
16508        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
16509
16510        // The top of the space is addressable, and its length is a number with
16511        // bit sixty three set, so the reply has to be unsigned or it comes back
16512        // negative.
16513        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
16514        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
16515        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
16516        // And one past it does not exist, so a write that would reach it fails
16517        // before any of it lands.
16518        assert_eq!(
16519            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
16520            "-ERR array index overflow\r\n"
16521        );
16522        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
16523    }
16524
16525    /// One reply per position and not one per element, which is the whole
16526    /// reason the range is capped.
16527    #[test]
16528    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
16529        let mut f = Fixture::new();
16530        f.run(&[b"ARSET", b"a", b"1", b"x"]);
16531        assert_eq!(
16532            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
16533            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
16534        );
16535        // The two ends may come in either order, and the answer is reversed
16536        // rather than empty.
16537        assert_eq!(
16538            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
16539            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
16540        );
16541        // A key that is not there reads like an array of nothing but holes.
16542        assert_eq!(
16543            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
16544            "*2\r\n$-1\r\n$-1\r\n"
16545        );
16546        // A range wider than a million positions is refused and not trimmed,
16547        // because against a missing key it is a request for as many nulls as
16548        // the range is wide.
16549        assert_eq!(
16550            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
16551            "-ERR range exceeds maximum of 1000000 items\r\n"
16552        );
16553    }
16554
16555    /// Every index in the argument list is read before the key is touched, so
16556    /// a bad one at the end leaves nothing half written.
16557    #[test]
16558    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
16559        let mut f = Fixture::new();
16560        assert_eq!(
16561            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
16562            "-ERR invalid array index\r\n"
16563        );
16564        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
16565        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
16566        assert_eq!(
16567            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
16568            "-ERR invalid array index\r\n"
16569        );
16570        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
16571        // An index is unsigned here, so the numbers a list would take are not
16572        // the last element, they are errors.
16573        assert_eq!(
16574            f.run(&[b"ARGET", b"a", b"-1"]),
16575            "-ERR invalid array index\r\n"
16576        );
16577        // And a pair list with an odd tail is an arity error rather than a
16578        // syntax one.
16579        assert_eq!(
16580            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
16581            "-ERR wrong number of arguments for 'armset' command\r\n"
16582        );
16583        assert_eq!(
16584            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
16585            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
16586        );
16587    }
16588
16589    #[test]
16590    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
16591        let mut f = Fixture::new();
16592        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
16593        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
16594        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
16595        // Two ranges in one command, and the second one covers the whole space
16596        // without walking it.
16597        assert_eq!(
16598            f.run(&[
16599                b"ARDELRANGE",
16600                b"a",
16601                b"100",
16602                b"200",
16603                b"0",
16604                b"18446744073709551614"
16605            ]),
16606            ":2\r\n"
16607        );
16608        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
16609        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
16610        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
16611    }
16612
16613    /// A value goes out as the bytes it came in as, whichever of the three ways
16614    /// the array found to store it.
16615    #[test]
16616    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
16617        let mut f = Fixture::new();
16618        let long = vec![b'v'; 200];
16619        f.run(&[
16620            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
16621            b"short", b"5", &long, b"6", b"-0",
16622        ]);
16623        // 42 is an integer, 007 is not one because it does not print back the
16624        // same, 3.5 survives a double and 3.14 does not, and the last two are a
16625        // word packed string and a blob.
16626        assert_eq!(
16627            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
16628            format!(
16629                "*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",
16630                String::from_utf8_lossy(&long)
16631            )
16632        );
16633    }
16634
16635    #[test]
16636    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
16637        let mut f = Fixture::new();
16638        f.run(&[b"ARSET", b"a", b"0", b"x"]);
16639        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
16640        assert_eq!(
16641            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
16642            "$12\r\nsliced-array\r\n"
16643        );
16644        // And it is a body like any other, so the key commands work on it.
16645        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
16646        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
16647        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
16648        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
16649        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
16650        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
16651    }
16652
16653    #[test]
16654    fn every_array_command_refuses_a_key_holding_something_else() {
16655        let mut f = Fixture::new();
16656        f.run(&[b"SET", b"s", b"v"]);
16657        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
16658        for cmd in [
16659            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
16660            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
16661            &[b"ARGET".as_ref(), b"s", b"0"][..],
16662            &[b"ARMGET".as_ref(), b"s", b"0"][..],
16663            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
16664            &[b"ARLEN".as_ref(), b"s"][..],
16665            &[b"ARCOUNT".as_ref(), b"s"][..],
16666            &[b"ARDEL".as_ref(), b"s", b"0"][..],
16667            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
16668            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
16669            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
16670            &[b"ARNEXT".as_ref(), b"s"][..],
16671            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
16672            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
16673            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
16674            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
16675            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
16676            &[b"ARINFO".as_ref(), b"s"][..],
16677        ] {
16678            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
16679        }
16680    }
16681
16682    /// Two of the array commands look the key up before they read the index and
16683    /// the rest read the index first, so the same broken argument gets two
16684    /// different errors depending on which command it went to.
16685    #[test]
16686    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
16687        let mut f = Fixture::new();
16688        f.run(&[b"SET", b"s", b"v"]);
16689        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
16690        let bad = "-ERR invalid array index\r\n";
16691        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
16692        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
16693        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
16694        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
16695        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
16696        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
16697        // And on a key that is an array the index is just an index.
16698        f.run(&[b"ARSET", b"a", b"0", b"x"]);
16699        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
16700        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
16701    }
16702
16703    #[test]
16704    fn an_append_follows_a_cursor_the_client_can_move() {
16705        let mut f = Fixture::new();
16706        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
16707        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
16708        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
16709        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
16710        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
16711
16712        // A seek says where the next one goes, and a missing key has no cursor
16713        // to move and is not created by the asking.
16714        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
16715        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
16716        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
16717        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
16718        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
16719        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
16720        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
16721
16722        // The top of the space is the one index only ARSEEK will take, and it
16723        // leaves the cursor with nowhere to go.
16724        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
16725        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
16726        assert_eq!(
16727            f.run(&[b"ARINSERT", b"a", b"x"]),
16728            "-ERR insert index overflow\r\n"
16729        );
16730        assert_eq!(
16731            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
16732            "-ERR invalid array index\r\n"
16733        );
16734    }
16735
16736    #[test]
16737    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
16738        let mut f = Fixture::new();
16739        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
16740        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
16741        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
16742        assert_eq!(
16743            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
16744            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
16745        );
16746        // Growing it after it has wrapped puts the survivors back in the order
16747        // they arrived, which is the whole point of paying for the rebuild.
16748        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
16749        assert_eq!(
16750            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
16751            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
16752        );
16753        // The size is read before the key, so a bad one is a bad size wherever
16754        // it is sent.
16755        assert_eq!(
16756            f.run(&[b"ARRING", b"r", b"0", b"x"]),
16757            "-ERR size must be positive\r\n"
16758        );
16759        assert_eq!(
16760            f.run(&[b"ARRING", b"r", b"big", b"x"]),
16761            "-ERR invalid size\r\n"
16762        );
16763    }
16764
16765    #[test]
16766    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
16767        let mut f = Fixture::new();
16768        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
16769        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
16770        assert_eq!(
16771            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
16772            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
16773        );
16774        assert_eq!(
16775            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
16776            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
16777        );
16778        assert_eq!(
16779            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
16780            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
16781            "more than there is gets what there is"
16782        );
16783        // Nothing asked for is an empty reply, and Redis answers that before it
16784        // has read the option or looked at the key.
16785        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
16786        assert_eq!(
16787            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
16788            "-ERR syntax error\r\n"
16789        );
16790        assert_eq!(
16791            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
16792            "-ERR invalid COUNT\r\n"
16793        );
16794
16795        // With no cursor the tail of the array is the anchor, and a hole inside
16796        // the window is reported as one.
16797        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
16798        assert_eq!(
16799            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
16800            "*2\r\n$-1\r\n$1\r\nz\r\n"
16801        );
16802    }
16803
16804    #[test]
16805    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
16806        let mut f = Fixture::new();
16807        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
16808        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
16809        // The whole index space, which ARGETRANGE refuses and this one answers
16810        // in three visits because holes cost nothing.
16811        assert_eq!(
16812            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
16813            "*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"
16814        );
16815        assert_eq!(
16816            f.run(&[
16817                b"ARSCAN",
16818                b"a",
16819                b"18446744073709551614",
16820                b"0",
16821                b"LIMIT",
16822                b"1"
16823            ]),
16824            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
16825        );
16826        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
16827        assert_eq!(
16828            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
16829            "-ERR LIMIT must be positive\r\n"
16830        );
16831        assert_eq!(
16832            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
16833            "-ERR syntax error\r\n"
16834        );
16835        assert_eq!(
16836            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
16837            "-ERR wrong number of arguments for 'arscan' command\r\n"
16838        );
16839    }
16840
16841    #[test]
16842    fn a_grep_answers_the_indexes_whose_elements_match() {
16843        let mut f = Fixture::new();
16844        assert_eq!(
16845            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
16846            "*0\r\n"
16847        );
16848        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
16849
16850        // The two bounds take the ends of the array as well as an index, and a
16851        // reversed range is walked backwards the way ARSCAN walks one.
16852        assert_eq!(
16853            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
16854            "*3\r\n:0\r\n:1\r\n:2\r\n"
16855        );
16856        assert_eq!(
16857            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
16858            "*3\r\n:2\r\n:1\r\n:0\r\n"
16859        );
16860        assert_eq!(
16861            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
16862            "*2\r\n:1\r\n:2\r\n"
16863        );
16864
16865        // One test each. NOCASE reaches all four of them and it may be written
16866        // after the pattern it applies to.
16867        assert_eq!(
16868            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
16869            "*1\r\n:0\r\n"
16870        );
16871        assert_eq!(
16872            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
16873            "*2\r\n:0\r\n:3\r\n"
16874        );
16875        assert_eq!(
16876            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
16877            "*1\r\n:2\r\n"
16878        );
16879        assert_eq!(
16880            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
16881            "*2\r\n:1\r\n:2\r\n"
16882        );
16883
16884        // OR is the default and AND has to be asked for, and either way the
16885        // last of a repeated option wins.
16886        let both: &[&[u8]] = &[
16887            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
16888        ];
16889        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
16890        assert_eq!(
16891            f.run(&[
16892                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
16893            ]),
16894            "*0\r\n"
16895        );
16896        assert_eq!(
16897            f.run(&[
16898                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
16899            ]),
16900            "*2\r\n:0\r\n:1\r\n"
16901        );
16902
16903        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
16904        // not the positions it had to look at.
16905        assert_eq!(
16906            f.run(&[
16907                b"ARGREP",
16908                b"a",
16909                b"-",
16910                b"+",
16911                b"MATCH",
16912                b"a",
16913                b"WITHVALUES",
16914                b"LIMIT",
16915                b"2"
16916            ]),
16917            "*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"
16918        );
16919        assert_eq!(
16920            f.run(&[
16921                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
16922            ]),
16923            "*1\r\n:3\r\n"
16924        );
16925    }
16926
16927    /// Everything ARGREP refuses, in the order it refuses it.
16928    #[test]
16929    fn a_grep_reports_a_broken_command_the_way_redis_does() {
16930        let mut f = Fixture::new();
16931        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
16932        let syntax = "-ERR syntax error\r\n";
16933
16934        // The bounds are read before the plan, so a bad index beats a bad
16935        // predicate whichever way round the two are written.
16936        assert_eq!(
16937            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
16938            "-ERR invalid array index\r\n"
16939        );
16940        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
16941        // A keyword with nothing after it, and a command that asks for nothing.
16942        assert_eq!(
16943            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
16944            syntax
16945        );
16946        assert_eq!(
16947            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
16948            syntax
16949        );
16950        assert_eq!(
16951            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
16952            syntax,
16953            "a command with no predicate in it at all"
16954        );
16955        assert_eq!(
16956            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
16957            "-ERR LIMIT must be positive\r\n"
16958        );
16959        assert_eq!(
16960            f.run(&[
16961                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
16962            ]),
16963            "-ERR value is not an integer or out of range\r\n"
16964        );
16965        assert_eq!(
16966            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
16967            "-ERR regular expression is empty\r\n"
16968        );
16969        assert_eq!(
16970            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
16971            "-ERR invalid regular expression: Missing ')'\r\n"
16972        );
16973        assert_eq!(
16974            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
16975            "-ERR regular expression backreferences are not supported\r\n"
16976        );
16977        // The arity is minus six, so a predicate keyword with no pattern after
16978        // it is short by one and never reaches the parser.
16979        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
16980        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
16981        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
16982    }
16983
16984    #[test]
16985    fn an_op_reduces_a_range_to_one_number() {
16986        let mut f = Fixture::new();
16987        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
16988        assert_eq!(
16989            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
16990            "$4\r\n-0.5\r\n"
16991        );
16992        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
16993        assert_eq!(
16994            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
16995            "$3\r\n2.5\r\n"
16996        );
16997        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
16998        assert_eq!(
16999            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
17000            ":1\r\n"
17001        );
17002        // An aggregate is written with seventeen significant digits, which is
17003        // Redis's own choice and not what a score comes back as.
17004        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
17005        assert_eq!(
17006            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
17007            "$19\r\n0.30000000000000004\r\n"
17008        );
17009        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
17010        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
17011
17012        // Nothing to work with is a null, and a missing key is a null for the
17013        // aggregates and a zero for the two that count.
17014        f.run(&[b"ARSET", b"w", b"0", b"word"]);
17015        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
17016        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
17017        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
17018
17019        assert_eq!(
17020            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
17021            "-ERR unknown operation\r\n"
17022        );
17023        assert_eq!(
17024            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
17025            "-ERR MATCH requires a value argument\r\n"
17026        );
17027        assert_eq!(
17028            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
17029            "-ERR wrong number of arguments for 'arop' command\r\n"
17030        );
17031    }
17032
17033    #[test]
17034    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
17035        let mut f = Fixture::new();
17036        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
17037        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
17038        let short = f.run(&[b"ARINFO", b"a"]);
17039        assert!(
17040            short.starts_with("*14\r\n"),
17041            "seven pairs on RESP2: {short}"
17042        );
17043        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
17044        assert!(
17045            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
17046            "{short}"
17047        );
17048        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
17049        let full = f.run(&[b"ARINFO", b"a", b"full"]);
17050        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
17051        // Two values one apart are held sparsely, so the dense count is zero and
17052        // the two dense averages have nothing to average.
17053        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
17054        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
17055        assert!(
17056            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
17057            "{full}"
17058        );
17059        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
17060
17061        // On RESP3 the same reply is a map and the averages are doubles.
17062        let mut g = Fixture::new();
17063        g.run(&[b"HELLO", b"3"]);
17064        g.run(&[b"ARINSERT", b"a", b"x"]);
17065        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
17066        assert!(map.starts_with("%12\r\n"), "{map}");
17067        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
17068        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
17069    }
17070
17071    #[test]
17072    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
17073        let mut f = Fixture::new();
17074        // Whole numbers up to two to the sixty second come back as integers,
17075        // and past that the digit generator takes over and uses an exponent.
17076        for (score, want) in [
17077            ("3", "3"),
17078            ("3.5", "3.5"),
17079            ("0.3", "0.3"),
17080            ("1e30", "1e+30"),
17081            ("1e19", "1e+19"),
17082            ("1e-7", "1e-7"),
17083            ("0.000001", "0.000001"),
17084            ("4611686018427387904", "4611686018427387904"),
17085            ("-0", "-0"),
17086        ] {
17087            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
17088            assert_eq!(
17089                f.run(&[b"ZSCORE", b"z", b"m"]),
17090                format!("${}\r\n{want}\r\n", want.len()),
17091                "score {score}"
17092            );
17093        }
17094
17095        // The same bytes on RESP3, where the reply is a double rather than a
17096        // bulk string.
17097        let mut g = Fixture::new();
17098        g.run(&[b"HELLO", b"3"]);
17099        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
17100        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
17101        // The two float increments are not this printer. They go through
17102        // ld2string in its human mode, which is a fixed point conversion with
17103        // the trailing zeros taken off, so they never write an exponent, and
17104        // they reply with a bulk string on both protocols.
17105        assert_eq!(
17106            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
17107            "$31\r\n1000000000000000000000000000000\r\n"
17108        );
17109        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
17110        assert_eq!(
17111            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
17112            "$20\r\n10000000000000000000\r\n"
17113        );
17114    }
17115
17116    // ----------------------------------------------------------------- graph
17117
17118    #[test]
17119    fn a_node_comes_back_with_the_fields_it_went_in_with() {
17120        let mut f = Fixture::new();
17121        assert_eq!(
17122            f.run(&[
17123                b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
17124            ]),
17125            ":1\r\n"
17126        );
17127        // The year comes back as the four bytes that were sent and not as a
17128        // number, because every property is text and there is nothing on the
17129        // wire that says which of `1815` and `"1815"` the client meant. The
17130        // fields are in the document's order, which is sorted by name, because
17131        // that is what makes a field lookup a binary search.
17132        assert_eq!(
17133            f.run(&[b"G.NGET", b"social", b"ada"]),
17134            "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
17135        );
17136        // A second write to the same id replaces the document and says so with
17137        // a zero, so an ingest can count what it created.
17138        assert_eq!(
17139            f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
17140            ":0\r\n"
17141        );
17142        assert_eq!(
17143            f.run(&[b"G.NGET", b"social", b"ada"]),
17144            "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
17145        );
17146        // A node with no properties is an empty map and not a null, which is
17147        // how a client tells an isolated node from one that is not there.
17148        assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
17149        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
17150        assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
17151        assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
17152
17153        // A field with no value creates nothing, because the pairs are checked
17154        // before the key is touched.
17155        assert_eq!(
17156            f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
17157            "-ERR syntax error\r\n"
17158        );
17159        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
17160
17161        // On RESP3 the same reply is a map.
17162        let mut g = Fixture::new();
17163        g.run(&[b"HELLO", b"3"]);
17164        g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
17165        assert_eq!(
17166            g.run(&[b"G.NGET", b"social", b"ada"]),
17167            "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
17168        );
17169    }
17170
17171    #[test]
17172    fn an_edge_creates_the_ends_it_needs() {
17173        let mut f = Fixture::new();
17174        assert_eq!(
17175            f.run(&[
17176                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
17177            ]),
17178            ":1\r\n"
17179        );
17180        // Neither end was written first and both are there, as empty nodes.
17181        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
17182        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
17183        assert_eq!(
17184            f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
17185            "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
17186        );
17187        assert_eq!(
17188            f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
17189            "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
17190        );
17191        // The same pair under the same label again updates the edge rather than
17192        // making a second one.
17193        assert_eq!(
17194            f.run(&[
17195                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
17196            ]),
17197            ":0\r\n"
17198        );
17199        assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
17200        // A different label between the same pair is a different edge.
17201        assert_eq!(
17202            f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
17203            ":1\r\n"
17204        );
17205        assert_eq!(
17206            f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
17207            ":1\r\n"
17208        );
17209
17210        assert_eq!(
17211            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
17212            ":1\r\n"
17213        );
17214        assert_eq!(
17215            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
17216            ":0\r\n"
17217        );
17218        // A label nothing has used, an end that is not there, and a key that is
17219        // not there are all a zero rather than an error.
17220        assert_eq!(
17221            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
17222            ":0\r\n"
17223        );
17224        assert_eq!(
17225            f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
17226            ":0\r\n"
17227        );
17228        assert_eq!(
17229            f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
17230            ":0\r\n"
17231        );
17232    }
17233
17234    /// A run is paged the way `SCAN` is paged, so a client that can walk one
17235    /// can walk the other.
17236    #[test]
17237    fn a_hop_answers_a_cursor_and_a_page() {
17238        let mut f = Fixture::new();
17239        for i in 0..25u32 {
17240            let dst = format!("n{i}");
17241            f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
17242        }
17243        // Ten without being asked, and the cursor is where to carry on from.
17244        let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
17245        assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
17246
17247        let mut seen = 0;
17248        let mut cursor = String::from("0");
17249        loop {
17250            let page = f.run(&[
17251                b"G.OUT",
17252                b"social",
17253                b"hub",
17254                b"FOLLOWS",
17255                b"COUNT",
17256                b"7",
17257                b"CURSOR",
17258                cursor.as_bytes(),
17259            ]);
17260            let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
17261            cursor = head
17262                .rsplit("\r\n")
17263                .next()
17264                .expect("the cursor line")
17265                .to_string();
17266            seen += rest
17267                .split_once("\r\n")
17268                .expect("the page length")
17269                .0
17270                .parse::<usize>()
17271                .expect("a length");
17272            if cursor == "0" {
17273                break;
17274            }
17275        }
17276        assert_eq!(seen, 25, "every neighbour once across the pages");
17277
17278        // A cursor past the end is an empty page and not an error, and so is a
17279        // key or a label that is not there.
17280        assert_eq!(
17281            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
17282            "*2\r\n$1\r\n0\r\n*0\r\n"
17283        );
17284        assert_eq!(
17285            f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
17286            "*2\r\n$1\r\n0\r\n*0\r\n"
17287        );
17288        assert_eq!(
17289            f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
17290            "*2\r\n$1\r\n0\r\n*0\r\n"
17291        );
17292        assert_eq!(
17293            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
17294            "-ERR COUNT must be a positive integer\r\n"
17295        );
17296        assert_eq!(
17297            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
17298            "-ERR syntax error\r\n"
17299        );
17300    }
17301
17302    #[test]
17303    fn a_degree_counts_one_way_or_both() {
17304        let mut f = Fixture::new();
17305        f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
17306        f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
17307        f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
17308        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
17309        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
17310        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
17311        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
17312        assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
17313        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
17314        assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
17315        assert_eq!(
17316            f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
17317            "-ERR syntax error\r\n"
17318        );
17319    }
17320
17321    /// A walk answers which nodes it can reach and not by how many routes, so a
17322    /// node two ways out is in the frontier once.
17323    #[test]
17324    fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
17325        let mut f = Fixture::new();
17326        for (src, dst) in [
17327            ("ada", "grace"),
17328            ("ada", "alan"),
17329            ("grace", "edsger"),
17330            ("alan", "edsger"),
17331            ("edsger", "barbara"),
17332        ] {
17333            f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
17334        }
17335        // Two hops without being asked, the start left out, and edsger once
17336        // even though both of the first hop's nodes point at it.
17337        assert_eq!(
17338            f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
17339            "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
17340        );
17341        assert_eq!(
17342            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
17343            "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
17344        );
17345        let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
17346        assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
17347        assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
17348        // COUNT stops the walk rather than trimming what it found.
17349        assert_eq!(
17350            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
17351            "*1\r\n$5\r\ngrace\r\n"
17352        );
17353        // A node nothing leaves is an empty array and not an error.
17354        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
17355        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
17356        assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
17357        assert_eq!(
17358            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
17359            "-ERR DEPTH must be a positive integer\r\n"
17360        );
17361        assert_eq!(
17362            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
17363            "-ERR syntax error\r\n"
17364        );
17365    }
17366
17367    /// The two sided search, which is the whole reason `G.PATH` is a command
17368    /// and not something a client builds out of `G.OUT`.
17369    #[test]
17370    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
17371        let mut f = Fixture::new();
17372        // A chain of six, and a shortcut that makes a shorter way round under a
17373        // second label so the search has to take either kind of hop.
17374        for i in 0..6u32 {
17375            let src = format!("n{i}");
17376            let dst = format!("n{}", i + 1);
17377            f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
17378        }
17379        assert_eq!(
17380            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
17381            "*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"
17382        );
17383        f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
17384        assert_eq!(
17385            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
17386            "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
17387        );
17388        // A node to itself is a path of one, and a depth too short to reach is
17389        // no path at all.
17390        assert_eq!(
17391            f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
17392            "*1\r\n$2\r\nn2\r\n"
17393        );
17394        assert_eq!(
17395            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
17396            "*0\r\n"
17397        );
17398        // Direction counts: the chain only goes one way.
17399        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
17400        // An unreachable node, a node that is not there, and a key that is not
17401        // there are the same empty answer.
17402        f.run(&[b"G.NADD", b"road", b"island"]);
17403        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
17404        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
17405        assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
17406        assert_eq!(
17407            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
17408            "-ERR syntax error\r\n"
17409        );
17410    }
17411
17412    /// The point of the escape in the record tag: the keyspace owns a graph key
17413    /// the way it owns every other key, and none of these commands know a graph
17414    /// exists.
17415    #[test]
17416    fn the_keyspace_sees_a_graph_key_like_any_other() {
17417        let mut f = Fixture::new();
17418        f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
17419        assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
17420        assert_eq!(
17421            f.run(&[b"OBJECT", b"ENCODING", b"social"]),
17422            "$9\r\nadjacency\r\n"
17423        );
17424        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
17425        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
17426        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
17427        // A graph is counted against the server the way every other body is,
17428        // which is what `maxmemory` will read when this key is a million nodes.
17429        // There is no `MEMORY USAGE` command yet, so this asks the server.
17430        let held = f.server.memory_bytes();
17431        for i in 0..200u32 {
17432            let dst = format!("n{i}");
17433            f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
17434        }
17435        assert!(
17436            f.server.memory_bytes() > held,
17437            "two hundred edges cost something: {held} then {}",
17438            f.server.memory_bytes()
17439        );
17440        f.run(&[b"DEL", b"big"]);
17441
17442        // An expiry, then a rename, then a move to another database, all of
17443        // which are the keyspace moving a record it cannot look inside.
17444        assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
17445        assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
17446        assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
17447        assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
17448        assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
17449        f.run(&[b"SELECT", b"1"]);
17450        assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
17451
17452        assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
17453        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
17454        f.run(&[b"G.NADD", b"g", b"n"]);
17455        assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
17456        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
17457    }
17458
17459    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
17460    /// rather than answering the way they answer for a key that is not there.
17461    #[test]
17462    fn a_graph_cannot_be_copied_or_dumped() {
17463        let mut f = Fixture::new();
17464        f.run(&[b"G.NADD", b"social", b"ada"]);
17465        assert_eq!(
17466            f.run(&[b"COPY", b"social", b"other"]),
17467            "-ERR COPY is not supported for a graph\r\n"
17468        );
17469        assert_eq!(
17470            f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
17471            "-ERR COPY is not supported for a graph\r\n"
17472        );
17473        assert_eq!(
17474            f.run(&[b"DUMP", b"social"]),
17475            "-ERR DUMP is not supported for a graph\r\n"
17476        );
17477        // A refused copy leaves both keys exactly as they were.
17478        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
17479    }
17480
17481    /// A graph key is a key, so the commands for the other types refuse it and
17482    /// the graph commands refuse theirs.
17483    #[test]
17484    fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
17485        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
17486        let mut f = Fixture::new();
17487        f.run(&[b"G.NADD", b"social", b"ada"]);
17488        assert_eq!(f.run(&[b"GET", b"social"]), wrong);
17489        assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
17490        assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
17491
17492        f.run(&[b"SET", b"str", b"v"]);
17493        for cmd in [
17494            vec![b"G.NADD".as_ref(), b"str", b"n"],
17495            vec![b"G.NGET".as_ref(), b"str", b"n"],
17496            vec![b"G.NDEL".as_ref(), b"str", b"n"],
17497            vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
17498            vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
17499            vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
17500            vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
17501            vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
17502            vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
17503            vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
17504        ] {
17505            assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
17506        }
17507    }
17508
17509    /// Every other collection here takes its key with it when its last member
17510    /// goes, and a graph is no different.
17511    #[test]
17512    fn a_graph_goes_when_its_last_node_does() {
17513        let mut f = Fixture::new();
17514        f.run(&[
17515            b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
17516        ]);
17517        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
17518        // The node and the edges that hung off it are both gone.
17519        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
17520        assert_eq!(
17521            f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
17522            ":0\r\n"
17523        );
17524        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
17525        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
17526
17527        assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
17528        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
17529        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
17530        assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
17531
17532        // The id the removed node had is not handed out again, so a client
17533        // holding an id from an earlier reply cannot have it mean another node.
17534        f.run(&[b"G.NADD", b"social", b"first"]);
17535        f.run(&[b"G.NADD", b"social", b"second"]);
17536        f.run(&[b"G.NDEL", b"social", b"first"]);
17537        f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
17538        assert_eq!(
17539            f.run(&[b"G.OUT", b"social", b"third", b"F"]),
17540            "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
17541        );
17542    }
17543
17544    // ------------------------------------------------------------------ json
17545
17546    /// The two path syntaxes answer different shapes, which is the thing a
17547    /// client is most likely to be broken by and so the thing to pin first.
17548    #[test]
17549    fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
17550        let mut f = Fixture::new();
17551        let doc = br#"{"a":1,"b":{"c":true}}"#;
17552        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
17553        // No path at all is the legacy root and not `$`, so the document comes
17554        // back as itself rather than wrapped.
17555        assert_eq!(
17556            f.run(&[b"JSON.GET", b"doc"]),
17557            bulk(r#"{"a":1,"b":{"c":true}}"#)
17558        );
17559        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
17560        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
17561        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
17562        // A path that matched nothing is an empty set on one syntax and an
17563        // error on the other, and the error does not quote the path.
17564        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
17565        assert_eq!(
17566            f.run(&[b"JSON.GET", b"doc", b".nope"]),
17567            "-ERR Path does not exist\r\n"
17568        );
17569        assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
17570        // The key is a document to the rest of the keyspace, under the name
17571        // RedisJSON registers, and every generic command works on it.
17572        assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
17573        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
17574        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
17575        assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
17576        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
17577    }
17578
17579    /// The two error lines RedisJSON sends without a prefix in front of them.
17580    ///
17581    /// Every other error this server writes starts `ERR` or `WRONGTYPE`. These
17582    /// two do not, on a real server, and a differential harness compares the
17583    /// whole line.
17584    #[test]
17585    fn the_two_json_errors_that_carry_no_prefix() {
17586        let mut f = Fixture::new();
17587        f.run(&[b"SET", b"plain", b"x"]);
17588        let wrong = "-Existing key has wrong Redis type\r\n";
17589        assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
17590        assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
17591        assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
17592        assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
17593        assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
17594
17595        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
17596        // A wildcard that matched something writes to all of it. A wildcard
17597        // that matched nothing would have to invent a place, and that is the
17598        // other unprefixed line.
17599        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
17600        assert_eq!(
17601            f.run(&[b"JSON.GET", b"doc"]),
17602            bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
17603        );
17604        assert_eq!(
17605            f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
17606            "-Err wrong static path\r\n"
17607        );
17608    }
17609
17610    /// What `JSON.SET` does with a path that named nowhere.
17611    #[test]
17612    fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
17613        let mut f = Fixture::new();
17614        // A key that is not there can only be written whole.
17615        assert_eq!(
17616            f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
17617            "-ERR new objects must be created at the root\r\n"
17618        );
17619        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
17620        // The root check comes before NX and XX, which is the order a real
17621        // server checks them in.
17622        assert_eq!(
17623            f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
17624            "-ERR new objects must be created at the root\r\n"
17625        );
17626        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
17627        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
17628
17629        f.run(&[
17630            b"JSON.SET",
17631            b"doc",
17632            b"$",
17633            br#"{"o":{},"arr":[1,2],"s":"x"}"#,
17634        ]);
17635        // One step past a container that is there is a place to write.
17636        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
17637        // One step past something that is not, or past something that is not an
17638        // object, is not an error and is not a write either.
17639        assert_eq!(
17640            f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
17641            "$-1\r\n"
17642        );
17643        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
17644        // An index past the end does not append. JSON.ARRAPPEND appends.
17645        assert_eq!(
17646            f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
17647            "-ERR array index out of range\r\n"
17648        );
17649        assert_eq!(
17650            f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
17651            "-ERR array index out of range\r\n"
17652        );
17653        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
17654        // NX on a path that is there and XX on a path that is not are both a
17655        // nil and neither changes anything.
17656        assert_eq!(
17657            f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
17658            "$-1\r\n"
17659        );
17660        assert_eq!(
17661            f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
17662            "$-1\r\n"
17663        );
17664        assert_eq!(
17665            f.run(&[b"JSON.GET", b"doc"]),
17666            bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
17667        );
17668        // Text that is not JSON is refused before the key is touched. The
17669        // line has no `ERR` in front of it, which is this command's and not
17670        // every command's, and is in D-37.
17671        assert!(
17672            f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
17673                .starts_with("-this is not the start of a value")
17674        );
17675        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
17676    }
17677
17678    /// `JSON.DEL`, `JSON.TYPE`, `JSON.TOGGLE` and `JSON.CLEAR`, each of which
17679    /// answers a count or a word rather than text.
17680    #[test]
17681    fn the_json_commands_that_do_not_answer_text() {
17682        let mut f = Fixture::new();
17683        let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
17684        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
17685
17686        assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
17687        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
17688        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
17689        assert_eq!(
17690            f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
17691            format!("*1\r\n{}", bulk("integer"))
17692        );
17693        // The one place a legacy path that matched nothing is a nil rather than
17694        // an error, which lines up with a key that is not there.
17695        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
17696        assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
17697
17698        // A boolean flips and answers the value it now has, as an integer on
17699        // one syntax and as the word on the other.
17700        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
17701        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
17702        // Something that is not a boolean is a hole on one syntax and one
17703        // sentence covering both cases on the other.
17704        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
17705        assert_eq!(
17706            f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
17707            "-ERR Path does not exist or not a bool\r\n"
17708        );
17709        assert_eq!(
17710            f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
17711            "-ERR Path does not exist or not a bool\r\n"
17712        );
17713        assert_eq!(
17714            f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
17715            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17716        );
17717
17718        // Clearing empties containers and zeroes numbers and leaves everything
17719        // else alone, and counts only what it changed.
17720        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
17721        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
17722        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
17723        assert_eq!(
17724            f.run(&[b"JSON.GET", b"doc"]),
17725            bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
17726        );
17727
17728        // Deleting counts what it removed, and deleting the root is deleting
17729        // the key.
17730        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
17731        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
17732        // Deleting the last member of the root container deletes the key, the
17733        // same way popping the last element off a list does. It is a rule about
17734        // deleting and not about shape: a document written as an empty object
17735        // by JSON.SET stays, because nothing was removed from it.
17736        assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
17737        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
17738        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
17739        assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
17740        assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
17741        assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
17742        assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
17743        assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
17744    }
17745
17746    /// `JSON.GET` with more than one path, and with a layout.
17747    ///
17748    /// The wrapper the reply is built in is laid out too, so what a path
17749    /// matched starts one level in for a single JSONPath and two for one of
17750    /// several, and getting that wrong is the kind of thing only a byte for
17751    /// byte comparison catches.
17752    #[test]
17753    fn json_get_lays_out_the_wrapper_it_builds() {
17754        let mut f = Fixture::new();
17755        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
17756
17757        assert_eq!(
17758            f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
17759            bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
17760        );
17761        // Legacy paths are not wrapped, even when there are several of them.
17762        assert_eq!(
17763            f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
17764            bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
17765        );
17766        let fmt: &[&[u8]] = &[b"INDENT", b"  ", b"NEWLINE", b"\n", b"SPACE", b" "];
17767        let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
17768        one.extend_from_slice(fmt);
17769        one.push(b"$.b");
17770        assert_eq!(
17771            f.run(&one),
17772            bulk("[\n  [\n    1,\n    {\n      \"c\": 2\n    }\n  ]\n]")
17773        );
17774        let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
17775        two.extend_from_slice(fmt);
17776        two.push(b"$.a");
17777        two.push(b"$.nope");
17778        assert_eq!(
17779            f.run(&two),
17780            bulk("{\n  \"$.a\": [\n    1\n  ],\n  \"$.nope\": []\n}")
17781        );
17782        // The options are read before the paths and in any order, and a
17783        // document with nothing to lay out is the same either way.
17784        let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
17785        root.push(b".a");
17786        assert_eq!(f.run(&root), bulk("1"));
17787    }
17788
17789    /// `JSON.MGET`, which is the only command here that reads more than one key
17790    /// and so the only one whose answer has holes in it.
17791    #[test]
17792    fn json_mget_answers_once_per_key_whatever_is_under_them() {
17793        let mut f = Fixture::new();
17794        f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
17795        f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
17796        f.run(&[b"SET", b"plain", b"x"]);
17797        assert_eq!(
17798            f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
17799            format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
17800        );
17801        // A key that is not there and a key holding something else are both a
17802        // hole rather than an error, the way MGET treats a hash.
17803        assert_eq!(
17804            f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
17805            format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
17806        );
17807        // A legacy path that matched nothing is a hole too, because one bad
17808        // answer should not lose the others.
17809        assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
17810    }
17811
17812    /// The four commands that ask how big something is, and the four different
17813    /// sets of answers they give for the same three failures.
17814    ///
17815    /// There is no pattern in this and there is no reading it off the
17816    /// documentation either. It was read off a running RedisJSON one line at a
17817    /// time, and it is written down here because the error text is what a client
17818    /// library branches on.
17819    #[test]
17820    fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
17821        let mut f = Fixture::new();
17822        let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
17823        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
17824
17825        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
17826        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
17827        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
17828        assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
17829        assert_eq!(
17830            f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
17831            format!("*2\r\n{}{}", bulk("x"), bulk("y"))
17832        );
17833        // A JSONPath answers one entry per match and a hole for a match of the
17834        // wrong kind, which is the one shape all four agree on.
17835        assert_eq!(
17836            f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
17837            "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
17838        );
17839
17840        // A legacy path that matched nothing. Two of them are an error and two
17841        // of them are a nil, and the two errors do not use the same sentence.
17842        assert_eq!(
17843            f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
17844            "-ERR Path does not exist\r\n"
17845        );
17846        assert_eq!(
17847            f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
17848            "-ERR Path does not exist\r\n"
17849        );
17850        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
17851        // A nil bulk and not an empty array, even though the answer would have
17852        // been an array, which is what RedisJSON sends here too.
17853        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
17854        // The JSONPath spelling of the same question is an empty array, since
17855        // no match is not a failure on that syntax.
17856        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
17857
17858        // A legacy path that matched the wrong kind of value. Now two of them
17859        // are an ERR and two of them are a WRONGTYPE, and it is not the same
17860        // two.
17861        assert_eq!(
17862            f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
17863            "-ERR Path does not exist or not an array\r\n"
17864        );
17865        assert_eq!(
17866            f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
17867            "-ERR Path does not exist or not an object\r\n"
17868        );
17869        assert_eq!(
17870            f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
17871            "-WRONGTYPE wrong type of path value - expected object\r\n"
17872        );
17873        assert_eq!(
17874            f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
17875            "-WRONGTYPE wrong type of path value - expected string\r\n"
17876        );
17877
17878        // A key that is not there, where the two syntaxes swap over: the legacy
17879        // path is the quiet answer and the JSONPath is the error.
17880        assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
17881        assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
17882        assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
17883        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
17884        assert_eq!(
17885            f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
17886            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17887        );
17888        // Except this one, which answers about the path instead.
17889        assert_eq!(
17890            f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
17891            "-ERR Path does not exist or not an object\r\n"
17892        );
17893    }
17894
17895    /// `JSON.ARRAPPEND`, `JSON.ARRINSERT`, `JSON.ARRTRIM` and `JSON.ARRPOP`.
17896    ///
17897    /// The four of them share one error line for a path that named something
17898    /// that is not an array, and they disagree about what an index outside the
17899    /// array means: insert refuses it and the other two clamp.
17900    #[test]
17901    fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
17902        let mut f = Fixture::new();
17903        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
17904
17905        assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
17906        assert_eq!(
17907            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
17908            "*1\r\n:6\r\n"
17909        );
17910        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
17911
17912        // A negative index counts back from the end, and the end itself is a
17913        // place to insert at, so an insert at the length is an append.
17914        assert_eq!(
17915            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
17916            ":7\r\n"
17917        );
17918        assert_eq!(
17919            f.run(&[b"JSON.GET", b"doc", b".a"]),
17920            bulk("[1,2,3,4,5,0,6]")
17921        );
17922        assert_eq!(
17923            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
17924            ":8\r\n"
17925        );
17926        // One past the end is not, and neither is one before the front.
17927        assert_eq!(
17928            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
17929            "-ERR index out of bounds\r\n"
17930        );
17931        assert_eq!(
17932            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
17933            "-ERR index out of bounds\r\n"
17934        );
17935
17936        // Trim takes both ends inclusive and clamps both of them, so a start
17937        // past the end leaves an empty array rather than an error.
17938        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
17939        assert_eq!(
17940            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
17941            ":3\r\n"
17942        );
17943        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
17944        assert_eq!(
17945            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
17946            ":2\r\n"
17947        );
17948        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
17949        assert_eq!(
17950            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
17951            ":0\r\n"
17952        );
17953        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
17954
17955        // Pop clamps as well, its default is the last element, and an empty
17956        // array pops a nil rather than failing.
17957        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
17958        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
17959        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
17960        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
17961        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
17962
17963        // One sentence covers a path that matched nothing and a path that
17964        // matched the wrong kind of value, for all four of them.
17965        for call in [
17966            &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
17967            &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
17968            &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
17969            &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
17970        ] {
17971            for path in [&b".n"[..], &b".nope"[..]] {
17972                let args: Vec<&[u8]> = call
17973                    .iter()
17974                    .map(|a| if *a == b"PATH" { path } else { *a })
17975                    .collect();
17976                assert_eq!(
17977                    f.run(&args),
17978                    "-ERR Path does not exist or not an array\r\n",
17979                    "{} {}",
17980                    String::from_utf8_lossy(call[0]),
17981                    String::from_utf8_lossy(path)
17982                );
17983            }
17984        }
17985
17986        // A key that is not there is the same sentence for all four, on either
17987        // syntax, and it is about the key and not about the path.
17988        assert_eq!(
17989            f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
17990            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17991        );
17992        assert_eq!(
17993            f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
17994            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17995        );
17996
17997        // The values are parsed before the key is touched, so text that is not
17998        // JSON leaves the document alone.
17999        // Text that is not JSON is refused before the key is touched, and
18000        // the line has no `ERR` in front of it, which is D-37.
18001        assert!(
18002            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
18003                .starts_with("-this is not the start of a value")
18004        );
18005        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
18006    }
18007
18008    /// `JSON.ARRINSERT` refuses the whole command when any one of the arrays a
18009    /// path matched cannot take the index, which is D-36.
18010    ///
18011    /// RedisJSON walks the matches, inserts into each one it can, and returns
18012    /// the error on the first one it cannot, leaving the earlier inserts in the
18013    /// document. A write here is one list of edits applied together, so either
18014    /// all of them happen or none of them do.
18015    #[test]
18016    fn json_arrinsert_is_all_or_nothing_across_the_matches() {
18017        let mut f = Fixture::new();
18018        let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
18019        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
18020        assert_eq!(
18021            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
18022            "-ERR index out of bounds\r\n"
18023        );
18024        assert_eq!(
18025            f.run(&[b"JSON.GET", b"doc"]),
18026            bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
18027        );
18028        // Every match can take the index, so every match gets it.
18029        assert_eq!(
18030            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
18031            "*3\r\n:4\r\n:3\r\n:2\r\n"
18032        );
18033        assert_eq!(
18034            f.run(&[b"JSON.GET", b"doc"]),
18035            bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
18036        );
18037    }
18038
18039    /// `JSON.ARRINDEX`, whose stop is exclusive and whose start clamps to the
18040    /// last element rather than to one past it.
18041    ///
18042    /// Both of those read like mistakes and both are what RedisJSON does. The
18043    /// start is the one that bites: a start of five into an array of four still
18044    /// looks at the fourth, so a search that should have run out of array comes
18045    /// back with an answer.
18046    #[test]
18047    fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
18048        let mut f = Fixture::new();
18049        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
18050
18051        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
18052        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
18053        assert_eq!(
18054            f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
18055            "*1\r\n:1\r\n"
18056        );
18057
18058        // Zero as the stop means the end rather than the front, so leaving it
18059        // off and passing it are the same thing.
18060        assert_eq!(
18061            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
18062            ":3\r\n"
18063        );
18064        // The stop is exclusive, so a stop of three does not look at index
18065        // three.
18066        assert_eq!(
18067            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
18068            ":-1\r\n"
18069        );
18070
18071        // The start clamps to the last element in both directions, which is why
18072        // a start of four, five or minus one all find the 1 at index three.
18073        for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
18074            assert_eq!(
18075                f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
18076                ":3\r\n",
18077                "{}",
18078                String::from_utf8_lossy(start)
18079            );
18080        }
18081        assert_eq!(
18082            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
18083            ":0\r\n"
18084        );
18085        // An empty array is the one case that comes back with nothing, since
18086        // the stop is zero and the loop never starts.
18087        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
18088        assert_eq!(
18089            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
18090            ":-1\r\n"
18091        );
18092
18093        // The comparison is structural rather than one of the encoded bytes,
18094        // because an object in a stored document holds its keys as intern table
18095        // ids where one parsed off the wire holds them as bytes.
18096        f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
18097        assert_eq!(
18098            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
18099            ":0\r\n"
18100        );
18101        assert_eq!(
18102            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
18103            ":1\r\n"
18104        );
18105        assert_eq!(
18106            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
18107            ":-1\r\n"
18108        );
18109
18110        // Its errors are a third set again: a missing legacy path is the short
18111        // sentence, the wrong kind of value is a WRONGTYPE, and a key that is
18112        // not there is about the path on either syntax.
18113        assert_eq!(
18114            f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
18115            "-ERR Path does not exist\r\n"
18116        );
18117        assert_eq!(
18118            f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
18119            "-WRONGTYPE wrong type of path value - expected array\r\n"
18120        );
18121        assert_eq!(
18122            f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
18123            "-ERR Path does not exist\r\n"
18124        );
18125        assert_eq!(
18126            f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
18127            "-ERR Path does not exist\r\n"
18128        );
18129    }
18130
18131    /// The number family answers text and keeps an integer an integer until
18132    /// something in the sum is not one.
18133    #[test]
18134    fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
18135        let mut f = Fixture::new();
18136        let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
18137        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
18138
18139        // A legacy path answers the new value as JSON text in a bulk string,
18140        // not as a number, which is the shape all three of them use.
18141        assert_eq!(
18142            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
18143            bulk("9").as_str()
18144        );
18145        // A JSONPath answers a bulk string holding a JSON array.
18146        assert_eq!(
18147            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
18148            bulk("[11]").as_str()
18149        );
18150        // Two integers stay an integer and a double anywhere in it makes the
18151        // answer a double, which the document then holds.
18152        assert_eq!(
18153            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
18154            bulk("13.0").as_str()
18155        );
18156        assert_eq!(
18157            f.run(&[b"JSON.TYPE", b"doc", b".i"]),
18158            bulk("number").as_str()
18159        );
18160        assert_eq!(
18161            f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
18162            bulk("3.0").as_str()
18163        );
18164        assert_eq!(
18165            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
18166            bulk("-8").as_str()
18167        );
18168        // A power of a half is a square root, and the square root of a negative
18169        // number is the error that says the answer is not a number.
18170        f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
18171        assert_eq!(
18172            f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
18173            bulk("1.224744871391589").as_str()
18174        );
18175        assert_eq!(
18176            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
18177            "-ERR result is not a number\r\n"
18178        );
18179        // An integer answer that does not fit is refused rather than promoted,
18180        // and a negative exponent lands in the same error because there is no
18181        // integer answer to two to the minus one.
18182        f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
18183        assert_eq!(
18184            f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
18185            "-ERR numeric overflow\r\n"
18186        );
18187        f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
18188        assert_eq!(
18189            f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
18190            "-ERR numeric overflow\r\n"
18191        );
18192        // A double that leaves the finite numbers is the other error.
18193        f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
18194        assert_eq!(
18195            f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
18196            "-ERR result is not a number\r\n"
18197        );
18198
18199        // A match that is not a number is a null inside the array on a
18200        // JSONPath, and a legacy path that found no number at all is the error
18201        // with the module's own typo in it.
18202        assert_eq!(
18203            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
18204            bulk("[null]").as_str()
18205        );
18206        assert_eq!(
18207            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
18208            bulk("[]").as_str()
18209        );
18210        assert_eq!(
18211            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
18212            "-ERR Path does not exist or does not contains a number\r\n"
18213        );
18214        assert_eq!(
18215            f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
18216            "-ERR Path does not exist or does not contains a number\r\n"
18217        );
18218        // The operand is JSON and has to be a number. Valid JSON that is not
18219        // one is a line of its own, and it goes out without a prefix.
18220        assert_eq!(
18221            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
18222            "-bad input number\r\n"
18223        );
18224        assert_eq!(
18225            f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
18226            "-ERR could not perform this operation on a key that doesn't exist\r\n"
18227        );
18228        assert_eq!(
18229            f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
18230            "-ERR could not perform this operation on a key that doesn't exist\r\n"
18231        );
18232    }
18233
18234    /// `JSON.STRAPPEND` puts its path in the middle and makes it optional,
18235    /// which nothing else in the group does.
18236    #[test]
18237    fn json_strappend_reads_its_shape_off_the_argument_count() {
18238        let mut f = Fixture::new();
18239        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
18240
18241        assert_eq!(
18242            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
18243            ":3\r\n"
18244        );
18245        assert_eq!(
18246            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
18247            "*1\r\n:4\r\n"
18248        );
18249        // The length is in bytes and not in characters, so one two byte letter
18250        // takes it up by two.
18251        assert_eq!(
18252            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
18253            ":6\r\n"
18254        );
18255        // Three arguments means the value is the last one and the path is the
18256        // root, so this appends to a document that is a string on its own.
18257        f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
18258        assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
18259        assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
18260
18261        // The value is JSON and has to be a JSON string. A number is a
18262        // WRONGTYPE about a path value even though it was the value that was
18263        // wrong, which is the module's wording and not a slip here.
18264        assert_eq!(
18265            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
18266            "-WRONGTYPE wrong type of path value - expected string\r\n"
18267        );
18268        assert_eq!(
18269            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
18270            "*1\r\n$-1\r\n"
18271        );
18272        assert_eq!(
18273            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
18274            "-ERR Path does not exist or not a string\r\n"
18275        );
18276        assert_eq!(
18277            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
18278            "*0\r\n"
18279        );
18280        assert_eq!(
18281            f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
18282            "-ERR could not perform this operation on a key that doesn't exist\r\n"
18283        );
18284    }
18285
18286    /// A legacy path can match more than one value, and which of them the one
18287    /// answer comes from is not the same choice twice.
18288    #[test]
18289    fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
18290        let mut f = Fixture::new();
18291        // Three arrays of one, two and three elements, which tells the first
18292        // match and the last match apart in a single command.
18293        let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
18294
18295        f.run(&[b"JSON.SET", b"doc", b"$", three]);
18296        assert_eq!(
18297            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
18298            ":4\r\n"
18299        );
18300        f.run(&[b"JSON.SET", b"doc", b"$", three]);
18301        assert_eq!(
18302            f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
18303            ":2\r\n"
18304        );
18305        f.run(&[b"JSON.SET", b"doc", b"$", three]);
18306        assert_eq!(
18307            f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
18308            ":1\r\n"
18309        );
18310        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
18311        assert_eq!(
18312            f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
18313            bulk("1").as_str()
18314        );
18315        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
18316        assert_eq!(
18317            f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
18318            bulk("13").as_str()
18319        );
18320        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
18321        assert_eq!(
18322            f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
18323            ":4\r\n"
18324        );
18325        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
18326        assert_eq!(
18327            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
18328            bulk("false").as_str()
18329        );
18330        // Every one of them wrote to all three matches, whichever one it chose
18331        // to answer about.
18332        assert_eq!(
18333            f.run(&[b"JSON.GET", b"doc", b".a"]),
18334            bulk("[false,true,false]").as_str()
18335        );
18336
18337        // A match of the wrong kind is skipped rather than being the answer, so
18338        // a path that found a string and then two arrays still answers.
18339        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
18340        assert_eq!(
18341            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
18342            ":3\r\n"
18343        );
18344        assert_eq!(
18345            f.run(&[b"JSON.GET", b"doc", b".a"]),
18346            bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
18347        );
18348        // Nothing of the right kind anywhere is the error, and that is the only
18349        // case that is.
18350        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
18351        assert_eq!(
18352            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
18353            "-ERR Path does not exist or not an array\r\n"
18354        );
18355        assert_eq!(
18356            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
18357            "-ERR Path does not exist or not a bool\r\n"
18358        );
18359        // The one array that was there and had nothing in it is an answer and
18360        // not a skip, so the pop answers about it rather than about the array
18361        // after it.
18362        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
18363        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
18364        assert_eq!(
18365            f.run(&[b"JSON.GET", b"doc", b".a"]),
18366            bulk("[[],[2]]").as_str()
18367        );
18368    }
18369
18370    /// A path that matched a value and something inside that value writes to
18371    /// both, which is what `$..` and a nested wildcard are for.
18372    #[test]
18373    fn a_write_reaches_a_match_that_sits_inside_another_match() {
18374        let mut f = Fixture::new();
18375        let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
18376
18377        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
18378        assert_eq!(
18379            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
18380            "*3\r\n:3\r\n:2\r\n:3\r\n"
18381        );
18382        assert_eq!(
18383            f.run(&[b"JSON.GET", b"doc", b"$"]),
18384            bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
18385        );
18386
18387        // The same for a trim, where the outer array keeps the two elements the
18388        // inner writes landed in.
18389        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
18390        assert_eq!(
18391            f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
18392            "*3\r\n:1\r\n:1\r\n:1\r\n"
18393        );
18394        assert_eq!(
18395            f.run(&[b"JSON.GET", b"doc", b"$"]),
18396            bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
18397        );
18398
18399        // And for a number, where the first match is the object the outer array
18400        // holds and only the two inside it are numbers.
18401        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
18402        assert_eq!(
18403            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
18404            bulk("[null,8,8]").as_str()
18405        );
18406    }
18407
18408    /// The value a write is given is looked at only once the path has found
18409    /// something of the right kind to use it on.
18410    #[test]
18411    fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
18412        let mut f = Fixture::new();
18413        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
18414
18415        // A string is not a number, so the path answers first and the `"x"` is
18416        // never looked at. Same for the value that is not JSON at all.
18417        assert_eq!(
18418            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
18419            bulk("[null]").as_str()
18420        );
18421        assert_eq!(
18422            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
18423            bulk("[null]").as_str()
18424        );
18425        assert_eq!(
18426            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
18427            bulk("[]").as_str()
18428        );
18429        assert_eq!(
18430            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
18431            "-ERR Path does not exist or does not contains a number\r\n"
18432        );
18433        // A number match anywhere and the value is looked at after all.
18434        assert_eq!(
18435            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
18436            "-bad input number\r\n"
18437        );
18438
18439        // JSON.STRAPPEND follows the same order with its own two answers.
18440        assert_eq!(
18441            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
18442            "*1\r\n$-1\r\n"
18443        );
18444        assert_eq!(
18445            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
18446            "-ERR Path does not exist or not a string\r\n"
18447        );
18448        assert_eq!(
18449            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
18450            "-WRONGTYPE wrong type of path value - expected string\r\n"
18451        );
18452
18453        // A key that is not there still comes before either of them.
18454        assert_eq!(
18455            f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
18456            "-ERR could not perform this operation on a key that doesn't exist\r\n"
18457        );
18458        assert_eq!(
18459            f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
18460            "-ERR could not perform this operation on a key that doesn't exist\r\n"
18461        );
18462    }
18463
18464    /// RFC 7386 in one test: a null deletes, everything else merges, and a
18465    /// patch that is not an object replaces what it lands on.
18466    #[test]
18467    fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
18468        let mut f = Fixture::new();
18469
18470        // A key that is not there is created at the root, nulls and all,
18471        // because a deletion with nothing to delete is still what the client
18472        // sent.
18473        assert_eq!(
18474            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
18475            "+OK\r\n"
18476        );
18477        assert_eq!(
18478            f.run(&[b"JSON.GET", b"doc", b"$"]),
18479            bulk(r#"[{"x":null,"y":1}]"#).as_str()
18480        );
18481
18482        // Onto something that is there, a null deletes the member of that name
18483        // and the rest is merged one level at a time.
18484        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
18485        assert_eq!(
18486            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
18487            "+OK\r\n"
18488        );
18489        assert_eq!(
18490            f.run(&[b"JSON.GET", b"doc", b"$"]),
18491            bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
18492        );
18493
18494        // A patch that is not an object replaces what it is merged onto.
18495        assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
18496        assert_eq!(
18497            f.run(&[b"JSON.GET", b"doc", b"$"]),
18498            bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
18499        );
18500
18501        // A patch object onto a value that is not an object starts from an
18502        // empty object, so this time the null has nothing to delete and is
18503        // dropped rather than stored.
18504        assert_eq!(
18505            f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
18506            "+OK\r\n"
18507        );
18508        assert_eq!(
18509            f.run(&[b"JSON.GET", b"doc", b"$"]),
18510            bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
18511        );
18512
18513        // A member one level past the end of the document is created and keeps
18514        // its nulls, two levels past it is a write that did not happen, and a
18515        // path that would have to invent where it goes is the unprefixed line.
18516        assert_eq!(
18517            f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
18518            "+OK\r\n"
18519        );
18520        assert_eq!(
18521            f.run(&[b"JSON.GET", b"doc", b"$.new"]),
18522            bulk(r#"[{"z":null}]"#).as_str()
18523        );
18524        assert_eq!(
18525            f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
18526            "$-1\r\n"
18527        );
18528        assert_eq!(
18529            f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
18530            "-Err wrong static path\r\n"
18531        );
18532
18533        // A wildcard merges every match.
18534        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
18535        assert_eq!(
18536            f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
18537            "+OK\r\n"
18538        );
18539        assert_eq!(
18540            f.run(&[b"JSON.GET", b"doc", b"$"]),
18541            bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
18542        );
18543
18544        // The three ways to get it wrong.
18545        assert_eq!(
18546            f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
18547            "-ERR syntax error\r\n"
18548        );
18549        assert_eq!(
18550            f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
18551            "-ERR new objects must be created at the root\r\n"
18552        );
18553        f.run(&[b"SET", b"str", b"x"]);
18554        assert_eq!(
18555            f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
18556            "-Existing key has wrong Redis type\r\n"
18557        );
18558    }
18559
18560    /// A descent is the one path that matches a value and something inside that
18561    /// same value, and the inner merge has to survive the outer one.
18562    #[test]
18563    fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
18564        let mut f = Fixture::new();
18565        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
18566        assert_eq!(
18567            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
18568            "+OK\r\n"
18569        );
18570        // `a`, `a.b`, `c` and `c[0]` all match. `a.b` is merged first and `a` is
18571        // merged onto the result, so the `{"m":1}` written into `a.b` is still
18572        // there. Doing it the other way round would leave `{"a":{"b":1,"m":1}}`.
18573        assert_eq!(
18574            f.run(&[b"JSON.GET", b"doc", b"$"]),
18575            bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
18576        );
18577
18578        // A deletion down the same path, which is the case where the inner
18579        // merge empties the object the outer one then copies.
18580        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
18581        assert_eq!(
18582            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
18583            "+OK\r\n"
18584        );
18585        assert_eq!(
18586            f.run(&[b"JSON.GET", b"doc", b"$"]),
18587            bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
18588        );
18589    }
18590
18591    /// A filter is a selector like any other, so every command that takes a path
18592    /// takes one, reads and writes alike.
18593    #[test]
18594    fn a_filter_path_reads_and_writes_the_members_it_keeps() {
18595        let mut f = Fixture::new();
18596        let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
18597        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
18598
18599        assert_eq!(
18600            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
18601            bulk(r#"["a","c"]"#).as_str()
18602        );
18603        // `$` inside the expression is the document, so a member can be measured
18604        // against something that is not inside it.
18605        assert_eq!(
18606            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
18607            bulk(r#"["a","c"]"#).as_str()
18608        );
18609        // The legacy syntax takes one too, and answers the first match.
18610        assert_eq!(
18611            f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
18612            bulk(r#""a""#).as_str()
18613        );
18614        assert_eq!(
18615            f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
18616            "*1\r\n$6\r\nobject\r\n"
18617        );
18618
18619        // A write goes through it as far as a value that is already there. A
18620        // field that is not there yet has nowhere definite to go, which is the
18621        // same refusal a wildcard gets.
18622        assert_eq!(
18623            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
18624            bulk("[9,10]").as_str()
18625        );
18626        assert_eq!(
18627            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
18628            "+OK\r\n"
18629        );
18630        assert_eq!(
18631            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
18632            "-Err wrong static path\r\n"
18633        );
18634        assert_eq!(
18635            f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
18636            ":2\r\n"
18637        );
18638        assert_eq!(
18639            f.run(&[b"JSON.GET", b"doc", b"$"]),
18640            bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
18641        );
18642
18643        // A path that does not parse is refused before the document is read, so
18644        // a key that is not there answers the same way.
18645        assert!(
18646            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
18647                .starts_with("-ERR")
18648        );
18649        assert!(
18650            f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
18651                .starts_with("-ERR")
18652        );
18653    }
18654
18655    /// The operators past the comparisons, over the wire rather than in the
18656    /// parser's own tests, so that a client can reach all of them.
18657    #[test]
18658    fn a_filter_takes_the_membership_operators_and_the_methods_too() {
18659        let mut f = Fixture::new();
18660        let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
18661        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
18662
18663        for (path, want) in [
18664            (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
18665            (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
18666            (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
18667            (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
18668            (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
18669            (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
18670            (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
18671            (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
18672            (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
18673            (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
18674            (b"$.box[?(@.n~)].t", "[]"),
18675            (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
18676            (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
18677            (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
18678            (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
18679        ] {
18680            assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
18681        }
18682
18683        // A write goes through one of these the same way it goes through a
18684        // comparison.
18685        assert_eq!(
18686            f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
18687            "+OK\r\n"
18688        );
18689        assert_eq!(
18690            f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
18691            bulk(r#"["b"]"#).as_str()
18692        );
18693    }
18694
18695    /// D-41. RedisJSON refuses this one, and which document it refuses is
18696    /// decided by how it happens to hold an array of numbers.
18697    #[test]
18698    fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
18699        let mut f = Fixture::new();
18700        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
18701        assert_eq!(
18702            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
18703            "+OK\r\n"
18704        );
18705        assert_eq!(
18706            f.run(&[b"JSON.GET", b"doc", b"$"]),
18707            bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
18708        );
18709        // The same document with one element that is not an integer is the one
18710        // RedisJSON is happy with, and it goes the same way here.
18711        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
18712        assert_eq!(
18713            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
18714            "+OK\r\n"
18715        );
18716        assert_eq!(
18717            f.run(&[b"JSON.GET", b"doc", b"$"]),
18718            bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
18719        );
18720    }
18721
18722    /// `JSON.MSET` checks what it can before it writes anything and skips the
18723    /// one thing it cannot, which is a path with nowhere to put its value.
18724    #[test]
18725    fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
18726        let mut f = Fixture::new();
18727        assert_eq!(
18728            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
18729            "+OK\r\n"
18730        );
18731        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
18732        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
18733
18734        // A repeated key takes the last write.
18735        assert_eq!(
18736            f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
18737            "+OK\r\n"
18738        );
18739        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
18740
18741        // A triple whose path names nowhere is skipped, the others are still
18742        // written and the reply turns into a nil. Both ways round, because a
18743        // loop that gave up at the first skip would agree with this on one
18744        // order and not on the other.
18745        f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
18746        assert_eq!(
18747            f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
18748            "$-1\r\n"
18749        );
18750        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
18751        assert_eq!(
18752            f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
18753            "$-1\r\n"
18754        );
18755        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
18756
18757        // A value that is not JSON, a key holding something else and a path
18758        // that would have to create a document below its own root are all
18759        // checked before anything is written, so the good triple next to them
18760        // does not happen either.
18761        f.run(&[b"SET", b"str", b"x"]);
18762        assert_eq!(
18763            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
18764            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
18765        );
18766        assert_eq!(
18767            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
18768            "-Existing key has wrong Redis type\r\n"
18769        );
18770        assert_eq!(
18771            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
18772            "-ERR new objects must be created at the root\r\n"
18773        );
18774        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
18775
18776        // The two errors a path can be are checked up front as well, so the
18777        // triple before them is not written either. A wildcard that matched
18778        // nothing has nowhere to invent, and an index that is not in the array
18779        // is out of range, and both of them stop the whole command.
18780        assert_eq!(
18781            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
18782            "-Err wrong static path\r\n"
18783        );
18784        assert_eq!(
18785            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
18786            "-ERR array index out of range\r\n"
18787        );
18788        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
18789
18790        // Every triple is worked out against the keyspace as the command found
18791        // it, so a second triple on the same key does not see the first one and
18792        // the last write is the one that stays.
18793        f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
18794        assert_eq!(
18795            f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
18796            "+OK\r\n"
18797        );
18798        assert_eq!(
18799            f.run(&[b"JSON.GET", b"c", b"$"]),
18800            bulk(r#"[{"n":3}]"#).as_str()
18801        );
18802
18803        // An argument count that is not a run of key, path and value is the
18804        // arity error rather than a syntax one.
18805        assert_eq!(
18806            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
18807            "-ERR wrong number of arguments for 'json.mset' command\r\n"
18808        );
18809    }
18810
18811    /// `JSON.RESP` hands back RESP types, and the marker element is what tells
18812    /// an empty array and an empty object apart.
18813    #[test]
18814    fn json_resp_answers_the_document_as_resp_types() {
18815        let mut f = Fixture::new();
18816        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
18817        assert_eq!(
18818            f.run(&[b"JSON.RESP", b"doc"]),
18819            "*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"
18820        );
18821        // A JSONPath wraps the same answer in one more array.
18822        assert_eq!(
18823            f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
18824            "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
18825        );
18826
18827        f.run(&[
18828            b"JSON.SET",
18829            b"doc",
18830            b"$",
18831            br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
18832        ]);
18833        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
18834        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
18835        // A double goes out as its text, so a client reads the same digits
18836        // `JSON.GET` would have given it.
18837        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
18838        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
18839        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
18840
18841        // A missing legacy path is an error, a missing JSONPath is an empty
18842        // array, and a key that is not there is a nil on either.
18843        assert_eq!(
18844            f.run(&[b"JSON.RESP", b"doc", b".nope"]),
18845            "-ERR Path does not exist\r\n"
18846        );
18847        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
18848        assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
18849        assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
18850    }
18851
18852    /// `JSON.DEBUG` answers a byte count that is this encoding's, so the test
18853    /// pins the shapes and that the two syntaxes agree rather than a number
18854    /// read off another server. That is D-42.
18855    #[test]
18856    fn json_debug_answers_a_byte_count_and_its_own_help() {
18857        let mut f = Fixture::new();
18858        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
18859        let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
18860        assert!(one.starts_with(':'), "{one}");
18861        assert_eq!(
18862            f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
18863            format!("*1\r\n{one}")
18864        );
18865        let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
18866        assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
18867
18868        // A key that is not there is a zero on a legacy path and an empty set
18869        // on a JSONPath, which is the one reader here that does not answer nil
18870        // for it.
18871        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
18872        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
18873        assert_eq!(
18874            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
18875            "-ERR Path does not exist\r\n"
18876        );
18877        assert_eq!(
18878            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
18879            "*0\r\n"
18880        );
18881
18882        assert_eq!(
18883            f.run(&[b"JSON.DEBUG", b"HELP"]),
18884            "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
18885             $34\r\nHELP                - this message\r\n"
18886        );
18887        assert_eq!(
18888            f.run(&[b"JSON.DEBUG", b"NOPE"]),
18889            "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
18890        );
18891        assert_eq!(
18892            f.run(&[b"JSON.DEBUG", b"MEMORY"]),
18893            "-ERR wrong number of arguments for 'json.debug' command\r\n"
18894        );
18895    }
18896
18897    // ---------------------------------------------------------------- vector
18898
18899    /// The first `VADD` fixes the dimension and every one after it has to
18900    /// agree, because there is no create command to say it earlier.
18901    #[test]
18902    fn the_first_vadd_decides_how_wide_the_set_is() {
18903        let mut f = Fixture::new();
18904        assert_eq!(
18905            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
18906            ":1\r\n"
18907        );
18908        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
18909        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
18910        // A second vector under the same name replaces it and says so with a
18911        // zero, so an ingest can count what it created.
18912        assert_eq!(
18913            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
18914            ":0\r\n"
18915        );
18916        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
18917        // Three dimensions into a two dimensional set names both numbers, since
18918        // a client that gets this wrong needs to know which end is which.
18919        assert_eq!(
18920            f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
18921            "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
18922        );
18923        // A vector of zeros has no direction, and it is taken anyway and comes
18924        // back as the origin, because that is what a real server does with it.
18925        assert_eq!(
18926            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
18927            ":1\r\n"
18928        );
18929        assert_eq!(
18930            f.run(&[b"VEMB", b"v", b"nowhere"]),
18931            "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
18932        );
18933        // A set is made with one quantisation and keeps it, and a `VADD` that
18934        // names another is refused. Naming none names `Q8`, which is why this
18935        // set is a `Q8` one.
18936        assert_eq!(
18937            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
18938            "-ERR asked quantization mismatch with existing vector set\r\n"
18939        );
18940        // Nothing above created a key, and a set that never took a vector has
18941        // no dimension to report.
18942        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
18943        assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
18944        assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
18945    }
18946
18947    /// What a client sent comes back out, and what a client asked for is a
18948    /// similarity and not the distance underneath it.
18949    #[test]
18950    fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
18951        let mut f = Fixture::new();
18952        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
18953        // The set stored the direction and the length is multiplied back on the
18954        // way out, so this is `3 4` and not `0.6 0.8`. It is not quite `3 4`
18955        // either, because nobody named a quantisation and that means `Q8`: the
18956        // wider coordinate lands on a code exactly and the other one does not.
18957        // Both numbers are a real server's answers for the same input.
18958        assert_eq!(
18959            f.run(&[b"VEMB", b"v", b"a"]),
18960            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
18961        );
18962        // NOQUANT is the way to ask for what went in to come back out.
18963        f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
18964        assert_eq!(
18965            f.run(&[b"VEMB", b"n", b"a"]),
18966            "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
18967        );
18968        // BIN keeps the signs and nothing else, and does not multiply the
18969        // length back on, since a sign has no length in it to scale.
18970        f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
18971        assert_eq!(
18972            f.run(&[b"VEMB", b"b", b"a"]),
18973            "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
18974        );
18975        assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
18976        assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
18977
18978        // On the axes, where the unit vector is exact and so is the dot
18979        // product, both ends of the scale come out exact: the same direction is
18980        // 1 and the opposite one is 0, with a right angle at a half.
18981        let mut f = Fixture::new();
18982        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
18983        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
18984        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
18985        assert_eq!(
18986            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
18987            "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
18988             $8\r\nopposite\r\n$1\r\n0\r\n"
18989        );
18990        // A search from an element leaves that element out, since it is always
18991        // its own nearest neighbour.
18992        assert_eq!(
18993            f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
18994            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
18995        );
18996        // An element that is not there is an empty answer and not an error,
18997        // which is what a missing key gives too.
18998        assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
18999        assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
19000        // COUNT bounds it and TRUTH reads every vector rather than the codes,
19001        // which has to agree with the index on a set this small.
19002        assert_eq!(
19003            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
19004            "*1\r\n$6\r\nacross\r\n"
19005        );
19006        assert_eq!(
19007            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
19008            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
19009        );
19010        // EF widens how much of the index is read and does not change how many
19011        // answers come back, so a wide search still returns what COUNT asked
19012        // for.
19013        assert_eq!(
19014            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
19015            "*1\r\n$6\r\nacross\r\n"
19016        );
19017
19018        // On RESP3 a scored search is a map, which is what the vector set
19019        // module replies and is not what ZRANGE does here.
19020        let mut g = Fixture::new();
19021        g.run(&[b"HELLO", b"3"]);
19022        g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
19023        assert_eq!(
19024            g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
19025            "%1\r\n$4\r\neast\r\n,1\r\n"
19026        );
19027    }
19028
19029    /// The attribute pair, and the one reply that means two things.
19030    #[test]
19031    fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
19032        let mut f = Fixture::new();
19033        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
19034        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
19035        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
19036        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
19037        // Not parsed as JSON, because nothing reads into it yet and refusing a
19038        // write for a rule nothing enforces would be the wrong trade.
19039        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
19040        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
19041        // An empty string clears it, which is Redis's spelling of the removal.
19042        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
19043        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
19044        // An element that is not there answers zero rather than being created,
19045        // since an attribute with no vector under it is not a thing this holds.
19046        assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
19047        assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
19048        assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
19049        // A null for an element with no attribute and a null for one that is
19050        // not there. VISMEMBER is how a client tells the two apart.
19051        assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
19052        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
19053        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
19054        assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
19055
19056        // WITHATTRIBS carries it alongside the answers.
19057        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
19058        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
19059        assert_eq!(
19060            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
19061            "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
19062        );
19063    }
19064
19065    /// The slot a removed element had is reused, and nothing that was beside it
19066    /// comes back with the next element to get it.
19067    #[test]
19068    fn vrem_takes_the_attribute_with_it() {
19069        let mut f = Fixture::new();
19070        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
19071        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
19072        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
19073        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
19074        assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
19075        // The key went with the last element, the way every other collection
19076        // here works.
19077        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
19078
19079        // The next element is given the slot the removed one had, and it comes
19080        // with no attribute on it.
19081        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
19082        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
19083        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
19084        f.run(&[b"VREM", b"v", b"east"]);
19085        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
19086        assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
19087    }
19088
19089    /// `VINFO` says what the index is before it says anything a client could
19090    /// mistake for a graph.
19091    #[test]
19092    fn vinfo_says_partition_first() {
19093        let mut f = Fixture::new();
19094        f.run(&[
19095            b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
19096        ]);
19097        f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
19098        let info = f.run(&[b"VINFO", b"v"]);
19099        assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
19100        // What the client asked for and not what happened to the tuning, which
19101        // is `10` section 7: M is recorded and changes nothing.
19102        assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
19103        assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
19104        assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
19105        // Nobody named a quantisation, so this set is a `Q8` one and every
19106        // element in it is stored that way.
19107        assert!(
19108            info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
19109            "{info}"
19110        );
19111        let mut f = Fixture::new();
19112        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
19113        assert!(
19114            f.run(&[b"VINFO", b"v"])
19115                .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
19116        );
19117        assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
19118    }
19119
19120    /// A set to read ranges of names out of.
19121    fn named() -> Fixture {
19122        let mut f = Fixture::new();
19123        for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
19124            .iter()
19125            .enumerate()
19126        {
19127            let x = (i + 1).to_string();
19128            f.run(&[
19129                b"VADD",
19130                b"r",
19131                b"VALUES",
19132                b"2",
19133                x.as_bytes(),
19134                b"1",
19135                name.as_bytes(),
19136            ]);
19137        }
19138        f
19139    }
19140
19141    /// `VRANGE` reads the names in the order bytes come in and pays no
19142    /// attention to where the vectors point.
19143    #[test]
19144    fn vrange_walks_the_names_and_not_the_vectors() {
19145        let mut f = named();
19146        assert_eq!(
19147            f.run(&[b"VRANGE", b"r", b"-", b"+"]),
19148            "*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"
19149        );
19150        assert_eq!(
19151            f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
19152            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
19153            "the high end is a name and not a prefix, so delta is past it"
19154        );
19155        assert_eq!(
19156            f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
19157            "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
19158        );
19159        assert_eq!(
19160            f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
19161            "*1\r\n$4\r\nbeta\r\n"
19162        );
19163        assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
19164        // Bytes and not letters, so an upper case name sorts before every lower
19165        // case one rather than beside its own spelling.
19166        f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
19167        assert_eq!(
19168            f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
19169            "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
19170        );
19171        assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
19172    }
19173
19174    /// The count cuts the answer after the range is decided, and zero is not
19175    /// the same as leaving it out.
19176    #[test]
19177    fn a_vrange_count_of_zero_asks_for_nothing() {
19178        let mut f = named();
19179        assert_eq!(
19180            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
19181            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
19182        );
19183        assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
19184        assert!(
19185            f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
19186                .starts_with("*5\r\n"),
19187            "a negative count is no limit at all"
19188        );
19189    }
19190
19191    /// Both ends are read before either is placed, and the count is read before
19192    /// either end.
19193    #[test]
19194    fn vrange_says_which_end_it_could_not_read() {
19195        let mut f = named();
19196        assert_eq!(
19197            f.run(&[b"VRANGE", b"r", b"x", b"y"]),
19198            "-ERR invalid start range format\r\n"
19199        );
19200        assert_eq!(
19201            f.run(&[b"VRANGE", b"r", b"+", b"x"]),
19202            "-ERR invalid end range format\r\n",
19203            "the high end is spelled wrong, which is worth saying before the \
19204             low end being on the wrong side"
19205        );
19206        assert_eq!(
19207            f.run(&[b"VRANGE", b"r", b"+", b"-"]),
19208            "-ERR '-' can only be used as first argument, '+' only as second\r\n"
19209        );
19210        // A bracket with nothing after it is not the empty name here, though an
19211        // element really can be called that.
19212        assert_eq!(
19213            f.run(&[b"VRANGE", b"r", b"[", b"+"]),
19214            "-ERR invalid start range format\r\n"
19215        );
19216        assert_eq!(
19217            f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
19218            "-ERR invalid COUNT value\r\n"
19219        );
19220        assert_eq!(
19221            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
19222            "-ERR wrong number of arguments for 'VRANGE' command\r\n"
19223        );
19224        f.run(&[b"SET", b"s", b"x"]);
19225        assert!(
19226            f.run(&[b"VRANGE", b"s", b"-", b"+"])
19227                .starts_with("-WRONGTYPE")
19228        );
19229    }
19230
19231    /// The option that asks for something this index does not have says so
19232    /// rather than doing something else quietly.
19233    #[test]
19234    fn reduce_is_refused_and_not_ignored() {
19235        let mut f = Fixture::new();
19236        let reduce = f.run(&[
19237            b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
19238        ]);
19239        assert!(
19240            reduce.starts_with("-ERR REDUCE is not supported."),
19241            "{reduce}"
19242        );
19243        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
19244    }
19245
19246    /// A filtered search answers with the nearest elements that match, and an
19247    /// expression that is not one is an error before the key is looked at.
19248    #[test]
19249    fn vsim_filter_reads_the_attributes() {
19250        let mut f = Fixture::new();
19251        for (name, x, y, attr) in [
19252            ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
19253            ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
19254            ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
19255            ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
19256        ] {
19257            f.run(&[
19258                b"VADD",
19259                b"v",
19260                b"VALUES",
19261                b"2",
19262                x.as_bytes(),
19263                y.as_bytes(),
19264                name.as_bytes(),
19265                b"SETATTR",
19266                attr.as_bytes(),
19267            ]);
19268        }
19269        // `b` is the nearest to the query and is the one the filter drops, so
19270        // this is the answer a filter applied afterwards would have got wrong.
19271        assert_eq!(
19272            f.run(&[
19273                b"VSIM",
19274                b"v",
19275                b"VALUES",
19276                b"2",
19277                b"9",
19278                b"1",
19279                b"COUNT",
19280                b"2",
19281                b"FILTER",
19282                b".lang == \"en\"",
19283            ]),
19284            "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
19285        );
19286        // A number is compared as a number, and the two halves of an `and` both
19287        // have to hold.
19288        assert_eq!(
19289            f.run(&[
19290                b"VSIM",
19291                b"v",
19292                b"VALUES",
19293                b"2",
19294                b"9",
19295                b"1",
19296                b"FILTER",
19297                b".lang == 'en' and .year > 1980",
19298            ]),
19299            "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
19300        );
19301        // A list, and a field an element does not have.
19302        assert_eq!(
19303            f.run(&[
19304                b"VSIM",
19305                b"v",
19306                b"VALUES",
19307                b"2",
19308                b"9",
19309                b"1",
19310                b"FILTER",
19311                b".lang in ['fr', 'de']",
19312            ]),
19313            "*1\r\n$1\r\nb\r\n"
19314        );
19315        assert_eq!(
19316            f.run(&[
19317                b"VSIM",
19318                b"v",
19319                b"VALUES",
19320                b"2",
19321                b"9",
19322                b"1",
19323                b"FILTER",
19324                b".rating > 3"
19325            ]),
19326            "*0\r\n"
19327        );
19328        // TRUTH measures every vector, and the filter still decides which ones
19329        // are measured.
19330        assert_eq!(
19331            f.run(&[
19332                b"VSIM",
19333                b"v",
19334                b"VALUES",
19335                b"2",
19336                b"9",
19337                b"1",
19338                b"TRUTH",
19339                b"FILTER",
19340                b".year < 1980",
19341            ]),
19342            "*1\r\n$1\r\nc\r\n"
19343        );
19344        // VSETATTR moves an element in and out of a filter, which means the tag
19345        // beside its code was rewritten and not just the string.
19346        f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
19347        assert_eq!(
19348            f.run(&[
19349                b"VSIM",
19350                b"v",
19351                b"VALUES",
19352                b"2",
19353                b"9",
19354                b"1",
19355                b"COUNT",
19356                b"1",
19357                b"FILTER",
19358                b".lang == \"en\"",
19359            ]),
19360            "*1\r\n$1\r\nb\r\n"
19361        );
19362        // And a VADD that replaces the vector keeps the attribute and the tag,
19363        // which is the same rewrite from the other end.
19364        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
19365        assert_eq!(
19366            f.run(&[
19367                b"VSIM",
19368                b"v",
19369                b"VALUES",
19370                b"2",
19371                b"9",
19372                b"1",
19373                b"COUNT",
19374                b"1",
19375                b"FILTER",
19376                b".lang == \"en\"",
19377            ]),
19378            "*1\r\n$1\r\nb\r\n"
19379        );
19380
19381        // The expression is parsed before the key is read, so a bad one is an
19382        // error whether or not the key is there.
19383        let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
19384        assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
19385        assert_eq!(
19386            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
19387            "-ERR invalid FILTER expression\r\n"
19388        );
19389        // FILTER-EF raises the effort rather than capping it, and zero is
19390        // Redis's word for no limit, so neither is an error.
19391        assert_eq!(
19392            f.run(&[
19393                b"VSIM",
19394                b"v",
19395                b"VALUES",
19396                b"2",
19397                b"9",
19398                b"1",
19399                b"COUNT",
19400                b"1",
19401                b"FILTER-EF",
19402                b"500",
19403                b"FILTER",
19404                b".lang == 'en'",
19405            ]),
19406            "*1\r\n$1\r\nb\r\n"
19407        );
19408        assert_eq!(
19409            f.run(&[
19410                b"VSIM",
19411                b"v",
19412                b"VALUES",
19413                b"2",
19414                b"9",
19415                b"1",
19416                b"COUNT",
19417                b"1",
19418                b"FILTER-EF",
19419                b"0"
19420            ]),
19421            "*1\r\n$1\r\nb\r\n"
19422        );
19423        assert_eq!(
19424            f.run(&[
19425                b"VSIM",
19426                b"v",
19427                b"VALUES",
19428                b"2",
19429                b"9",
19430                b"1",
19431                b"FILTER-EF",
19432                b"lots"
19433            ]),
19434            "-ERR EF must be a positive integer\r\n"
19435        );
19436    }
19437
19438    /// A vector set key is a key, so the keyspace owns it the way it owns every
19439    /// other one and none of those commands know what is inside it.
19440    #[test]
19441    fn the_keyspace_sees_a_vector_set_key_like_any_other() {
19442        let mut f = Fixture::new();
19443        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
19444        assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
19445        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
19446        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
19447        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
19448        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
19449        assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
19450        assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
19451        assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
19452        assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
19453        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
19454
19455        // And the wrong type is the wrong type in both directions.
19456        f.run(&[b"SET", b"s", b"1"]);
19457        assert_eq!(
19458            f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
19459            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19460        );
19461        assert_eq!(
19462            f.run(&[b"VCARD", b"s"]),
19463            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19464        );
19465        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
19466        assert_eq!(
19467            f.run(&[b"GET", b"v"]),
19468            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19469        );
19470        // A graph and a vector set share the escape in the record tag and are
19471        // still two different types, which is the case the tag alone cannot
19472        // decide.
19473        f.run(&[b"G.NADD", b"social", b"ada"]);
19474        assert_eq!(
19475            f.run(&[b"VCARD", b"social"]),
19476            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19477        );
19478        assert_eq!(
19479            f.run(&[b"G.NGET", b"v", b"ada"]),
19480            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19481        );
19482    }
19483
19484    /// `VRANDMEMBER` is `SRANDMEMBER` over the element names, in both of its
19485    /// shapes, off the database's own generator.
19486    #[test]
19487    fn vrandmember_has_the_two_shapes_srandmember_has() {
19488        let mut f = Fixture::new();
19489        for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
19490            let x = (i + 1).to_string();
19491            f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
19492        }
19493        // One element is a bulk string and not an array of one.
19494        let one = f.run(&[b"VRANDMEMBER", b"v"]);
19495        assert!(one.starts_with("$1\r\n"), "{one}");
19496        // A positive count is distinct and stops at the size of the set.
19497        let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
19498        assert!(all.starts_with("*3\r\n"), "{all}");
19499        for name in ["a", "b", "c"] {
19500            assert!(all.contains(name), "{all} is missing {name}");
19501        }
19502        all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
19503        assert!(all.starts_with("*2\r\n"), "{all}");
19504        // A negative one draws that many and allows repeats.
19505        let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
19506        assert!(many.starts_with("*5\r\n"), "{many}");
19507        // A key that is not there answers the shape that was asked for.
19508        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
19509        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
19510    }
19511
19512    /// `VLINKS` answers about the index that is here rather than the graph that
19513    /// is not, which is D-2.
19514    #[test]
19515    fn vlinks_reports_one_layer_of_partition_neighbours() {
19516        let mut f = Fixture::new();
19517        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
19518        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
19519        // One layer deep, because the index is one layer deep, so a client
19520        // walking layers gets a short list and not a shape it cannot parse.
19521        assert_eq!(
19522            f.run(&[b"VLINKS", b"v", b"east"]),
19523            "*1\r\n*1\r\n$5\r\nnorth\r\n"
19524        );
19525        assert_eq!(
19526            f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
19527            "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
19528        );
19529        assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
19530        assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
19531    }
19532
19533    /// A vector arrives either as digits or as bytes, and the two have to mean
19534    /// the same thing.
19535    #[test]
19536    fn fp32_and_values_are_the_same_vector() {
19537        let mut f = Fixture::new();
19538        let mut blob = Vec::new();
19539        for x in [3.0f32, 4.0] {
19540            blob.extend_from_slice(&x.to_le_bytes());
19541        }
19542        assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
19543        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
19544        assert_eq!(
19545            f.run(&[b"VEMB", b"v", b"a"]),
19546            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
19547        );
19548        // RAW is the stored bytes and the numbers that turn them back into the
19549        // client's vector, which for `Q8` is a code a coordinate, the length the
19550        // vector arrived with and the scale the codes are measured against. The
19551        // name of the form is a simple string, which is a real server's shape,
19552        // and all four of these are a real server's answers.
19553        assert_eq!(
19554            f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
19555            "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
19556        );
19557        // A blob that is not a whole number of floats is not a vector.
19558        assert_eq!(
19559            f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
19560            "-ERR invalid vector specification\r\n"
19561        );
19562        // Neither is a count that promises more than arrived.
19563        assert_eq!(
19564            f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
19565            "-ERR syntax error\r\n"
19566        );
19567        assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
19568    }
19569
19570    // ----------------------------------------------------------------- bloom
19571
19572    /// The filter a client gets when it does not describe one, and the two
19573    /// answers an add can give.
19574    #[test]
19575    fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
19576        let mut f = Fixture::new();
19577        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
19578        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
19579        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
19580        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
19581        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
19582        // The defaults are the module's configs and not anything the command
19583        // said, which is 100 entries at a hundredth and a growth of 2.
19584        assert_eq!(
19585            f.run(&[b"BF.INFO", b"b"]),
19586            "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
19587             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
19588             +Expansion rate\r\n:2\r\n"
19589        );
19590        assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
19591        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
19592        // A key that is not there has no filter to report on, and answers two
19593        // different ways about it depending on which command asked.
19594        assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
19595        assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
19596    }
19597
19598    /// `BF.EXISTS` on a key holding something else answers a miss, and
19599    /// everything else in the family answers `WRONGTYPE`.
19600    ///
19601    /// The two halves of a check and set disagree about what that key is, which
19602    /// is the module's behaviour and not a decision taken here.
19603    #[test]
19604    fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
19605        let mut f = Fixture::new();
19606        f.run(&[b"SET", b"s", b"text"]);
19607        assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
19608        assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
19609        for cmd in [
19610            vec![&b"BF.ADD"[..], b"s", b"x"],
19611            vec![&b"BF.MADD"[..], b"s", b"x"],
19612            vec![&b"BF.CARD"[..], b"s"],
19613            vec![&b"BF.INFO"[..], b"s"],
19614            vec![&b"BF.DEBUG"[..], b"s"],
19615            vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
19616        ] {
19617            let name = String::from_utf8_lossy(cmd[0]).into_owned();
19618            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
19619        }
19620        // The arguments are read before the key is, so a reserve with a bad
19621        // error rate complains about the rate and never learns about the string.
19622        assert_eq!(
19623            f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
19624            "-ERR bad error rate\r\n"
19625        );
19626        assert!(
19627            f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
19628                .starts_with("-WRONGTYPE")
19629        );
19630    }
19631
19632    /// A chain grows by its expansion factor and each link is half as wrong as
19633    /// the one before, which is what makes the whole filter hold its rate.
19634    #[test]
19635    fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
19636        let mut f = Fixture::new();
19637        assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
19638        for i in 0..10u32 {
19639            assert_eq!(
19640                f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
19641                ":1\r\n"
19642            );
19643        }
19644        assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
19645        assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
19646        assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
19647        // Capacity is the sum of every link and not the number that was asked
19648        // for, so it is 10 and then 10 plus 20.
19649        assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
19650        assert_eq!(
19651            f.run(&[b"BF.DEBUG", b"g"]),
19652            "*3\r\n$7\r\nsize:11\r\n\
19653             $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
19654             $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
19655        );
19656
19657        // The same filter told not to grow fills instead.
19658        assert_eq!(
19659            f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
19660            "+OK\r\n"
19661        );
19662        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
19663        assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
19664        assert_eq!(
19665            f.run(&[b"BF.ADD", b"n", b"c"]),
19666            "-ERR non scaling filter is full\r\n"
19667        );
19668        // And an item that is already in it still answers, because membership
19669        // is checked before fullness.
19670        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
19671        // A filter that will not grow has no expansion rate to report, in
19672        // either of the two spellings that make one.
19673        assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
19674        f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
19675        assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
19676        // Asking for both at once is refused, which is one of the module's
19677        // errors that carries no prefix at all.
19678        assert_eq!(
19679            f.run(&[
19680                b"BF.RESERVE",
19681                b"q",
19682                b"0.01",
19683                b"2",
19684                b"NONSCALING",
19685                b"EXPANSION",
19686                b"2"
19687            ]),
19688            "-Nonscaling filters cannot expand\r\n"
19689        );
19690    }
19691
19692    /// A multi add stops where the filter did, so the reply can be shorter than
19693    /// the argument list.
19694    #[test]
19695    fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
19696        let mut f = Fixture::new();
19697        f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
19698        assert_eq!(
19699            f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
19700            "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
19701        );
19702        assert_eq!(
19703            f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
19704            "*2\r\n:1\r\n:0\r\n"
19705        );
19706    }
19707
19708    /// `BF.INSERT` describes a filter and fills it in one command, with its own
19709    /// spelling of every complaint.
19710    #[test]
19711    fn insert_is_a_reserve_and_a_madd_with_different_errors() {
19712        let mut f = Fixture::new();
19713        assert_eq!(
19714            f.run(&[
19715                b"BF.INSERT",
19716                b"i",
19717                b"CAPACITY",
19718                b"50",
19719                b"ERROR",
19720                b"0.001",
19721                b"ITEMS",
19722                b"a",
19723                b"b"
19724            ]),
19725            "*2\r\n:1\r\n:1\r\n"
19726        );
19727        assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
19728        // NOCREATE is the only way to add without making the key.
19729        assert_eq!(
19730            f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
19731            "-ERR not found\r\n"
19732        );
19733        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
19734        // The same mistakes as BF.RESERVE, in the sentences this command uses
19735        // for them, and one sentence where BF.RESERVE has two.
19736        assert_eq!(
19737            f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
19738            "-Bad capacity\r\n"
19739        );
19740        assert_eq!(
19741            f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
19742            "-Bad error rate\r\n"
19743        );
19744        assert_eq!(
19745            f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
19746            "-Bad expansion\r\n"
19747        );
19748        // An option is matched on its first letter and not on the word, so a
19749        // token nobody meant as an option is one anyway if it starts with the
19750        // right letter. NOSUCHTHING is NONSCALING here, and the filter it
19751        // builds says so.
19752        assert_eq!(
19753            f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
19754            "*1\r\n:1\r\n"
19755        );
19756        assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
19757        // Only E and N need a second look, one for ERROR against EXPANSION and
19758        // the other for NOCREATE against NONSCALING, and both stop as soon as
19759        // they can tell the two apart.
19760        assert_eq!(
19761            f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
19762            "*1\r\n:1\r\n"
19763        );
19764        assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
19765        assert_eq!(
19766            f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
19767            "*1\r\n:1\r\n"
19768        );
19769        assert_eq!(
19770            f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
19771            "-ERR not found\r\n"
19772        );
19773        // A letter that starts nothing is the one case that is refused.
19774        assert_eq!(
19775            f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
19776            "-Unknown argument received\r\n"
19777        );
19778        // Everything after ITEMS is an item, even when it spells an option.
19779        assert_eq!(
19780            f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
19781            "*1\r\n:1\r\n"
19782        );
19783        // And ITEMS with nothing after it is the same as leaving it out.
19784        assert!(
19785            f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
19786                .contains("wrong number of arguments")
19787        );
19788    }
19789
19790    /// A filter dumped a chunk at a time and put back into another key is the
19791    /// same filter.
19792    #[test]
19793    fn a_dump_replays_into_a_filter_that_answers_the_same() {
19794        let mut f = Fixture::new();
19795        f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
19796        for i in 0..25u32 {
19797            f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
19798        }
19799        assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
19800
19801        // Iterator zero asks for the header and every one after it is a running
19802        // byte offset, and a chunk never spans two links.
19803        let mut iter = b"0".to_vec();
19804        let mut chunks = 0;
19805        loop {
19806            let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
19807            let text = String::from_utf8_lossy(&raw).into_owned();
19808            let next = text
19809                .split("\r\n")
19810                .nth(1)
19811                .and_then(|n| n.strip_prefix(':'))
19812                .expect("a two element reply of an iterator and a chunk")
19813                .to_owned();
19814            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
19815            let data = &body[body
19816                .windows(2)
19817                .position(|w| w == b"\r\n")
19818                .expect("a length line")
19819                + 2..body.len() - 2];
19820            if next == "0" {
19821                assert!(data.is_empty(), "the last chunk is empty");
19822                break;
19823            }
19824            let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
19825            assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
19826            iter = next.into_bytes();
19827            chunks += 1;
19828        }
19829        assert_eq!(chunks, 3, "a header and one chunk per link");
19830
19831        assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
19832        assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
19833        for i in 0..25u32 {
19834            assert_eq!(
19835                f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
19836                ":1\r\n"
19837            );
19838        }
19839
19840        // A header on top of a filter is refused rather than merged, and so is
19841        // one that no filter wrote.
19842        assert_eq!(
19843            f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
19844            "-ERR received bad data\r\n"
19845        );
19846        assert_eq!(
19847            f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
19848            "-ERR received bad data\r\n"
19849        );
19850        // An offset past the end of the filter names itself.
19851        assert_eq!(
19852            f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
19853            "-ERR invalid offset - no link found\r\n"
19854        );
19855        assert_eq!(
19856            f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
19857            "-ERR Second argument must be numeric\r\n"
19858        );
19859        // The same complaint without the prefix on the way out, which is the
19860        // module's inconsistency and not a slip here.
19861        assert_eq!(
19862            f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
19863            "-Second argument must be numeric\r\n"
19864        );
19865    }
19866
19867    /// The argument checks, which have a sentence each and read numbers the way
19868    /// Redis reads them everywhere else.
19869    #[test]
19870    fn reserve_reads_its_numbers_the_way_string2ll_does() {
19871        let mut f = Fixture::new();
19872        for (args, want) in [
19873            (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
19874            (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
19875            (
19876                vec![&b"0"[..], b"10"],
19877                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
19878            ),
19879            (
19880                vec![&b"1"[..], b"10"],
19881                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
19882            ),
19883            (
19884                vec![&b"inf"[..], b"10"],
19885                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
19886            ),
19887            (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
19888            (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
19889            (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
19890            (
19891                vec![&b"0.01"[..], b"0"],
19892                "-ERR capacity must be in the range [1, 1073741824]\r\n",
19893            ),
19894            (
19895                vec![&b"0.01"[..], b"1073741825"],
19896                "-ERR capacity must be in the range [1, 1073741824]\r\n",
19897            ),
19898        ] {
19899            let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
19900            cmd.extend(args.iter().copied());
19901            assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
19902        }
19903        assert_eq!(
19904            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
19905            "-ERR no expansion\r\n"
19906        );
19907        assert_eq!(
19908            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
19909            "-ERR bad expansion\r\n"
19910        );
19911        assert_eq!(
19912            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
19913            "-ERR expansion must be in the range [0, 32768]\r\n"
19914        );
19915        // Trailing rubbish after the capacity is ignored rather than refused.
19916        assert_eq!(
19917            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
19918            "+OK\r\n"
19919        );
19920        assert_eq!(
19921            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
19922            "-ERR item exists\r\n"
19923        );
19924        assert_eq!(
19925            f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
19926            "-Invalid information value\r\n"
19927        );
19928        assert!(
19929            f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
19930                .contains("wrong number of arguments")
19931        );
19932    }
19933
19934    /// The RESP3 shapes, which are where this family differs most from RESP2.
19935    #[test]
19936    fn the_bloom_family_answers_in_resp3_spelling_too() {
19937        let mut f = Fixture::new();
19938        f.out.set_proto(Proto::Resp3);
19939        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
19940        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
19941        assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
19942        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
19943        assert_eq!(
19944            f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
19945            "*2\r\n#t\r\n#f\r\n"
19946        );
19947        // The count stays an integer, because it counts rather than answers.
19948        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
19949        assert_eq!(
19950            f.run(&[b"BF.INFO", b"b"]),
19951            "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
19952             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
19953             +Expansion rate\r\n:2\r\n"
19954        );
19955        // One field is a map of one here and a bare array of one on RESP2, so
19956        // this is the reply where the two protocols carry different facts.
19957        assert_eq!(
19958            f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
19959            "%1\r\n+Capacity\r\n:100\r\n"
19960        );
19961    }
19962
19963    // ---------------------------------------------------------------- cuckoo
19964
19965    /// A dump header, which is the four counts and the three widths a filter
19966    /// writes in front of its fingerprints.
19967    ///
19968    /// Written by hand rather than taken from a `CF.SCANDUMP`, because what the
19969    /// tests below want out of it is the states a filter cannot be put into
19970    /// from the wire.
19971    fn cf_header(
19972        items: u64,
19973        buckets: u64,
19974        deletes: u64,
19975        filters: u64,
19976        geometry: [u16; 3],
19977    ) -> Vec<u8> {
19978        let mut out = Vec::with_capacity(38);
19979        for n in [items, buckets, deletes, filters] {
19980            out.extend_from_slice(&n.to_le_bytes());
19981        }
19982        for n in geometry {
19983            out.extend_from_slice(&n.to_le_bytes());
19984        }
19985        out
19986    }
19987
19988    /// The filter a client gets when it does not describe one, and the thing a
19989    /// cuckoo filter does that a Bloom filter cannot, which is count copies and
19990    /// take them out again.
19991    #[test]
19992    fn cf_add_makes_the_filter_and_counts_the_copies() {
19993        let mut f = Fixture::new();
19994        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
19995        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
19996        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
19997        // The NX form is the one that looks first, which is why it is a command
19998        // of its own rather than an option.
19999        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
20000        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
20001        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
20002        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
20003        assert_eq!(
20004            f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
20005            "*2\r\n:1\r\n:0\r\n"
20006        );
20007        // The defaults are the module's configs: 1024 entries over buckets of
20008        // two, twenty kicks and a chain that grows by one.
20009        assert_eq!(
20010            f.run(&[b"CF.INFO", b"d"]),
20011            "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
20012             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
20013             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
20014             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
20015        );
20016        assert_eq!(
20017            f.run(&[b"CF.DEBUG", b"d"]),
20018            "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
20019             max_iterations:20 expansion:1\r\n"
20020        );
20021        assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
20022        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
20023
20024        // A delete takes one copy, so the same item goes twice and then stops.
20025        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
20026        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
20027        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
20028        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
20029        assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
20030
20031        // A key with no filter under it gets three different sentences and one
20032        // plain miss, depending on which command asked.
20033        assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
20034        assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
20035        assert_eq!(
20036            f.run(&[b"CF.COMPACT", b"gone"]),
20037            "-Cuckoo filter was not found\r\n"
20038        );
20039        assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
20040        // And `CF.COMPACT` is declared as taking any number of keys and takes
20041        // exactly one, which is the module's own arity being wrong rather than
20042        // this table's.
20043        assert!(
20044            f.run(&[b"CF.COMPACT", b"a", b"b"])
20045                .contains("wrong number of arguments")
20046        );
20047    }
20048
20049    /// The four that only read fingerprints treat a key holding something else
20050    /// as a key with no filter, and everything else answers `WRONGTYPE`.
20051    #[test]
20052    fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
20053        let mut f = Fixture::new();
20054        f.run(&[b"SET", b"s", b"text"]);
20055        assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
20056        assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
20057        assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
20058        // `CF.DEL` writes and is still in that group, and `CF.COMPACT` writes
20059        // and is declared read only, so neither of the two halves of the family
20060        // is the same set as the flags say.
20061        assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
20062        assert_eq!(
20063            f.run(&[b"CF.COMPACT", b"s"]),
20064            "-Cuckoo filter was not found\r\n"
20065        );
20066        for cmd in [
20067            vec![&b"CF.ADD"[..], b"s", b"x"],
20068            vec![&b"CF.ADDNX"[..], b"s", b"x"],
20069            vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
20070            vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
20071            vec![&b"CF.INFO"[..], b"s"],
20072            vec![&b"CF.DEBUG"[..], b"s"],
20073            vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
20074            vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
20075            vec![&b"CF.RESERVE"[..], b"s", b"64"],
20076        ] {
20077            let name = String::from_utf8_lossy(cmd[0]).into_owned();
20078            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
20079        }
20080    }
20081
20082    /// `CF.RESERVE` reads its options by name in an order of its own, and the
20083    /// first pair with a given name is the only one it looks at.
20084    #[test]
20085    fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
20086        let mut f = Fixture::new();
20087        assert_eq!(
20088            f.run(&[
20089                b"CF.RESERVE",
20090                b"r",
20091                b"64",
20092                b"BUCKETSIZE",
20093                b"1",
20094                b"MAXITERATIONS",
20095                b"7",
20096                b"EXPANSION",
20097                b"4"
20098            ]),
20099            "+OK\r\n"
20100        );
20101        assert_eq!(
20102            f.run(&[b"CF.DEBUG", b"r"]),
20103            "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
20104             max_iterations:7 expansion:4\r\n"
20105        );
20106        assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
20107
20108        assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
20109        assert_eq!(
20110            f.run(&[b"CF.RESERVE", b"q", b"1"]),
20111            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
20112        );
20113        // The range is the bucket size's and not a constant, so a capacity that
20114        // was fine at two slots a bucket is not at four.
20115        assert_eq!(
20116            f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
20117            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
20118        );
20119        assert_eq!(
20120            f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
20121            "+OK\r\n"
20122        );
20123
20124        // The capacity is checked last, so a command that is wrong twice
20125        // answers about the option. Which option it answers about is the order
20126        // the module looks for them in and not the order they were written, so
20127        // a bad kick budget wins over a bad bucket size wherever the two sit.
20128        assert_eq!(
20129            f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
20130            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
20131        );
20132        assert_eq!(
20133            f.run(&[
20134                b"CF.RESERVE",
20135                b"q2",
20136                b"64",
20137                b"EXPANSION",
20138                b"xx",
20139                b"BUCKETSIZE",
20140                b"0"
20141            ]),
20142            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
20143        );
20144        assert_eq!(
20145            f.run(&[
20146                b"CF.RESERVE",
20147                b"q2",
20148                b"64",
20149                b"MAXITERATIONS",
20150                b"0",
20151                b"BUCKETSIZE",
20152                b"0"
20153            ]),
20154            "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
20155        );
20156        // A second pair with a name that has already been read is not looked at
20157        // at all, so this one is a filter with buckets of one rather than an
20158        // error about a bucket size of zero.
20159        assert_eq!(
20160            f.run(&[
20161                b"CF.RESERVE",
20162                b"q3",
20163                b"64",
20164                b"BUCKETSIZE",
20165                b"1",
20166                b"BUCKETSIZE",
20167                b"0"
20168            ]),
20169            "+OK\r\n"
20170        );
20171        // A pair nobody knows is dropped, which is the opposite of what
20172        // `CF.INSERT` does with the same mistake.
20173        assert_eq!(
20174            f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
20175            "+OK\r\n"
20176        );
20177        assert_eq!(
20178            f.run(&[b"CF.DEBUG", b"q4"]),
20179            "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
20180             max_iterations:20 expansion:1\r\n"
20181        );
20182        // And an option with nothing after it leaves an odd number of them,
20183        // which is an arity error rather than a complaint about the option.
20184        assert!(
20185            f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
20186                .contains("wrong number of arguments")
20187        );
20188    }
20189
20190    /// `CF.INSERT` is a reserve and a multi add, with a grammar that agrees
20191    /// with `CF.RESERVE` about nothing.
20192    #[test]
20193    fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
20194        let mut f = Fixture::new();
20195        assert_eq!(
20196            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
20197            "*2\r\n:1\r\n:1\r\n"
20198        );
20199        assert_eq!(
20200            f.run(&[b"CF.DEBUG", b"i"]),
20201            "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
20202             max_iterations:20 expansion:1\r\n"
20203        );
20204        // The NX form has three answers rather than two, which is why it stays
20205        // integers on both protocols.
20206        assert_eq!(
20207            f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
20208            "*2\r\n:0\r\n:1\r\n"
20209        );
20210        assert_eq!(
20211            f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
20212            "-ERR not found\r\n"
20213        );
20214        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
20215
20216        assert_eq!(
20217            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
20218            "-Bad capacity\r\n"
20219        );
20220        // The bucket size cannot be given here, so the range names the config
20221        // that holds it instead of the option `CF.RESERVE` names.
20222        assert_eq!(
20223            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
20224            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
20225        );
20226        // Every occurrence is checked, which is where this differs from
20227        // `CF.RESERVE`: the second `CAPACITY` is an error even though the first
20228        // one is the one that would have been used.
20229        assert_eq!(
20230            f.run(&[
20231                b"CF.INSERT",
20232                b"i",
20233                b"CAPACITY",
20234                b"8",
20235                b"CAPACITY",
20236                b"2",
20237                b"ITEMS",
20238                b"a"
20239            ]),
20240            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
20241        );
20242        // An option is one letter and not a word, so `NOSUCH` is `NOCREATE` and
20243        // `ITEMSXYZ` is `ITEMS`, and only a letter that starts nothing is
20244        // refused.
20245        assert_eq!(
20246            f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
20247            "*1\r\n:1\r\n"
20248        );
20249        assert_eq!(
20250            f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
20251            "*1\r\n:1\r\n"
20252        );
20253        assert_eq!(
20254            f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
20255            "-Unknown argument received\r\n"
20256        );
20257        // Everything after ITEMS is an item, even when it spells an option.
20258        assert_eq!(
20259            f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
20260            "*1\r\n:1\r\n"
20261        );
20262        // And the two ways of sending no items at all are the same complaint.
20263        assert!(
20264            f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
20265                .contains("wrong number of arguments")
20266        );
20267        assert!(
20268            f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
20269                .contains("wrong number of arguments")
20270        );
20271    }
20272
20273    /// The two walls a filter can hit, which say different things and are not
20274    /// the same wall.
20275    #[test]
20276    fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
20277        let mut f = Fixture::new();
20278        f.run(&[
20279            b"CF.RESERVE",
20280            b"s",
20281            b"4",
20282            b"BUCKETSIZE",
20283            b"1",
20284            b"EXPANSION",
20285            b"0",
20286        ]);
20287        for i in 0..4u32 {
20288            assert_eq!(
20289                f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
20290                ":1\r\n"
20291            );
20292        }
20293        assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
20294        assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
20295        // The add commands say it in a sentence and the insert commands say it
20296        // in the array, one value per item, and the array is never short.
20297        assert_eq!(
20298            f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
20299            "*2\r\n:-1\r\n:-1\r\n"
20300        );
20301        assert_eq!(
20302            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
20303            "*2\r\n:0\r\n:-1\r\n"
20304        );
20305
20306        // A chain that is allowed to grow stops for a different reason, and the
20307        // count it stops at is the filter limit rather than the room: this one
20308        // gives up with three slots free. Loading a chain that already has
20309        // every filter it is allowed shows why, since it refuses an item
20310        // straight into an empty one.
20311        let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
20312        assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
20313        assert_eq!(
20314            f.run(&[b"CF.ADD", b"g", b"q"]),
20315            "-Maximum expansions reached\r\n"
20316        );
20317        assert_eq!(
20318            f.run(&[b"CF.INFO", b"g"]),
20319            "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
20320             +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
20321             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
20322             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
20323        );
20324    }
20325
20326    /// A filter dumped a chunk at a time and put back under another key is the
20327    /// same filter, and the headers that describe one nobody could build are
20328    /// refused on the way in.
20329    #[test]
20330    fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
20331        let mut f = Fixture::new();
20332        f.run(&[
20333            b"CF.RESERVE",
20334            b"src",
20335            b"8",
20336            b"BUCKETSIZE",
20337            b"2",
20338            b"EXPANSION",
20339            b"2",
20340        ]);
20341        for i in 0..40u32 {
20342            f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
20343        }
20344        // Position zero asks for the header and every one after it is a byte
20345        // offset across every filter laid end to end, and the walk ends on a
20346        // zero and a nil rather than an empty chunk.
20347        let mut pos = b"0".to_vec();
20348        let mut chunks = 0;
20349        loop {
20350            let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
20351            let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
20352            let next = head
20353                .split("\r\n")
20354                .nth(1)
20355                .and_then(|n| n.strip_prefix(':'))
20356                .expect("a two element reply of a position and a chunk")
20357                .to_owned();
20358            if next == "0" {
20359                assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
20360                break;
20361            }
20362            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
20363            let at = body
20364                .windows(2)
20365                .position(|w| w == b"\r\n")
20366                .expect("a length line")
20367                + 2;
20368            let data = &body[at..body.len() - 2];
20369            assert_eq!(
20370                f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
20371                "+OK\r\n",
20372                "loading chunk {chunks}"
20373            );
20374            pos = next.into_bytes();
20375            chunks += 1;
20376        }
20377        assert!(chunks >= 2, "a header and at least one chunk");
20378
20379        assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
20380        assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
20381        for i in 0..40u32 {
20382            assert_eq!(
20383                f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
20384                ":1\r\n"
20385            );
20386        }
20387
20388        // A filter with nothing in it hands out no header at all, so a client
20389        // that dumps one has nothing to load back.
20390        f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
20391        assert_eq!(
20392            f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
20393            "*2\r\n:0\r\n$-1\r\n"
20394        );
20395
20396        // The positions this end will not take, which are not the same set at
20397        // both ends: a dump refuses a negative one and a load takes it as an
20398        // offset and fails to find anything there.
20399        assert_eq!(
20400            f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
20401            "-Invalid position\r\n"
20402        );
20403        assert_eq!(
20404            f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
20405            "-Invalid position\r\n"
20406        );
20407        assert_eq!(
20408            f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
20409            "-Invalid position\r\n"
20410        );
20411        assert_eq!(
20412            f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
20413            "-Couldn't load chunk!\r\n"
20414        );
20415        // A header on top of a filter is refused rather than merged.
20416        let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
20417        assert_eq!(
20418            f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
20419            "-ERR item exists\r\n"
20420        );
20421        // A chunk that is not the size of a header where a header should have
20422        // been is one sentence, and one that is the size of a header and
20423        // describes a filter nobody could build is another.
20424        assert_eq!(
20425            f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
20426            "-Invalid header\r\n"
20427        );
20428        for (why, bad) in [
20429            ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
20430            ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
20431            (
20432                "a bucket count that is not a power of two",
20433                cf_header(0, 3, 0, 1, [2, 20, 1]),
20434            ),
20435            ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
20436            ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
20437            (
20438                "a growth nobody could reach",
20439                cf_header(0, 8, 0, 1, [2, 20, 32769]),
20440            ),
20441            (
20442                "a chain that cannot grow and did",
20443                cf_header(0, 8, 0, 2, [2, 20, 0]),
20444            ),
20445            // The count is written in eight bytes and read into two, so a
20446            // number that is a multiple of the second arrives as none.
20447            (
20448                "a filter count that wraps",
20449                cf_header(0, 8, 0, 65_536, [2, 20, 1]),
20450            ),
20451        ] {
20452            assert_eq!(
20453                f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
20454                "-Couldn't create filter!\r\n",
20455                "{why}"
20456            );
20457        }
20458    }
20459
20460    /// The RESP3 shapes, which are where this family differs most from RESP2
20461    /// and where one of its answers stops being readable.
20462    #[test]
20463    fn the_cuckoo_family_answers_in_resp3_spelling_too() {
20464        let mut f = Fixture::new();
20465        f.out.set_proto(Proto::Resp3);
20466        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
20467        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
20468        assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
20469        assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
20470        assert_eq!(
20471            f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
20472            "*2\r\n#t\r\n#f\r\n"
20473        );
20474        assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
20475        assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
20476        // The count stays an integer, because it counts rather than answers.
20477        assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
20478        assert_eq!(
20479            f.run(&[b"CF.INFO", b"c"]),
20480            "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
20481             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
20482             +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
20483             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
20484        );
20485
20486        // `CF.INSERT` writes a boolean per item here and an integer per item on
20487        // RESP2, and minus one has nowhere to go in a boolean, so a RESP3
20488        // client cannot tell an item that did not fit from one that is already
20489        // there. `CF.INSERTNX` keeps its integers for exactly that reason.
20490        f.run(&[
20491            b"CF.RESERVE",
20492            b"s",
20493            b"4",
20494            b"BUCKETSIZE",
20495            b"1",
20496            b"EXPANSION",
20497            b"0",
20498        ]);
20499        assert_eq!(
20500            f.run(&[
20501                b"CF.INSERT",
20502                b"s",
20503                b"ITEMS",
20504                b"a",
20505                b"b",
20506                b"c",
20507                b"d",
20508                b"e",
20509                b"f"
20510            ]),
20511            "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
20512        );
20513        assert_eq!(
20514            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
20515            "*2\r\n:0\r\n:-1\r\n"
20516        );
20517        assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
20518        // The end of a dump is a nil and not an empty chunk, which is one
20519        // underscore here and a negative length on RESP2.
20520        assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
20521    }
20522
20523    // ------------------------------------------------------------------- cms
20524
20525    /// A sketch is made from either end, and both constructors look at the key
20526    /// before they look at their arguments.
20527    #[test]
20528    fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
20529        let mut f = Fixture::new();
20530        assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
20531        assert_eq!(
20532            f.run(&[b"CMS.INFO", b"d"]),
20533            "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
20534        );
20535        assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
20536        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
20537        // Two over the error rounded up, and the log of the probability over the
20538        // log of a half rounded up, which for these two is 200 by 6.
20539        assert_eq!(
20540            f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
20541            "+OK\r\n"
20542        );
20543        assert_eq!(
20544            f.run(&[b"CMS.INFO", b"p"]),
20545            "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
20546        );
20547        // The key is checked first, so a width of zero at a key that is already
20548        // there is about the key and not about the width.
20549        assert_eq!(
20550            f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
20551            "-CMS: key already exists\r\n"
20552        );
20553        assert_eq!(
20554            f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
20555            "-CMS: invalid width\r\n"
20556        );
20557        assert_eq!(
20558            f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
20559            "-CMS: invalid depth\r\n"
20560        );
20561        assert_eq!(
20562            f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
20563            "-CMS: invalid overestimation value\r\n"
20564        );
20565        assert_eq!(
20566            f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
20567            "-CMS: invalid prob value\r\n"
20568        );
20569        // A probability whose float conversion is zero has no depth, and a width
20570        // past a signed sixty four bit integer has no width, and both are the
20571        // same sentence.
20572        assert_eq!(
20573            f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
20574            "-CMS: invalid init arguments\r\n"
20575        );
20576        // And a sketch bigger than a gibibyte of counters is refused here where
20577        // the reference reserves address space nobody has touched, which is
20578        // D-47.
20579        assert_eq!(
20580            f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
20581            "-CMS: Insufficient memory to create the key\r\n"
20582        );
20583        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
20584    }
20585
20586    /// Every pair is parsed before any of them lands, the counters saturate,
20587    /// and the count is a signed total of what was asked for.
20588    #[test]
20589    fn increments_are_parsed_whole_and_the_counters_saturate() {
20590        let mut f = Fixture::new();
20591        f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
20592        assert_eq!(
20593            f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
20594            "*2\r\n:3\r\n:4\r\n"
20595        );
20596        // An item that is incremented twice in one call sees its own first
20597        // increment in the reply to the second.
20598        assert_eq!(
20599            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
20600            "*2\r\n:4\r\n:5\r\n"
20601        );
20602        // A bad number anywhere means nothing at all is applied.
20603        assert_eq!(
20604            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
20605            "-CMS: Cannot parse number\r\n"
20606        );
20607        assert_eq!(
20608            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
20609            "-CMS: Number cannot be negative\r\n"
20610        );
20611        assert_eq!(
20612            f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
20613            "*2\r\n:5\r\n:4\r\n"
20614        );
20615        // The counters stop at four billion and the item that stopped says so in
20616        // its own slot while the one beside it answers a number.
20617        f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
20618        assert_eq!(
20619            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
20620            "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
20621        );
20622        assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
20623        // The count is what was asked for rather than what landed, and it is
20624        // signed, so a big enough total comes back negative.
20625        f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
20626        f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
20627        f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
20628        assert_eq!(
20629            f.run(&[b"CMS.INFO", b"w"]),
20630            "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
20631        );
20632        // An odd number of arguments after the key is an arity error and not a
20633        // syntax one.
20634        assert!(
20635            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
20636                .contains("wrong number of arguments")
20637        );
20638        assert_eq!(
20639            f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
20640            "-CMS: key does not exist\r\n"
20641        );
20642        assert_eq!(
20643            f.run(&[b"CMS.QUERY", b"nope", b"a"]),
20644            "-CMS: key does not exist\r\n"
20645        );
20646    }
20647
20648    /// A merge overwrites its destination, and it is worked out in full before
20649    /// any of it is written.
20650    #[test]
20651    fn a_merge_lands_whole_or_not_at_all() {
20652        let mut f = Fixture::new();
20653        for name in [&b"m1"[..], b"m2", b"dst"] {
20654            f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
20655        }
20656        f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
20657        f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
20658        assert_eq!(
20659            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
20660            "+OK\r\n"
20661        );
20662        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
20663        // Overwritten and not added to, so the same merge twice is the same
20664        // answer twice.
20665        assert_eq!(
20666            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
20667            "+OK\r\n"
20668        );
20669        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
20670        assert_eq!(
20671            f.run(&[
20672                b"CMS.MERGE",
20673                b"dst",
20674                b"2",
20675                b"m1",
20676                b"m2",
20677                b"WEIGHTS",
20678                b"2",
20679                b"3"
20680            ]),
20681            "+OK\r\n"
20682        );
20683        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
20684        // A cell times a weight is checked wide rather than wrapped, so this is
20685        // a refusal and the destination is left exactly as it was.
20686        assert_eq!(
20687            f.run(&[
20688                b"CMS.MERGE",
20689                b"dst",
20690                b"1",
20691                b"m1",
20692                b"WEIGHTS",
20693                b"4611686018427387904"
20694            ]),
20695            "-CMS: MERGE overflow\r\n"
20696        );
20697        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
20698        // The destination comes first, then the count, then the layout, then the
20699        // weights, then the sources one at a time.
20700        f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
20701        assert_eq!(
20702            f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
20703            "-CMS: key does not exist\r\n"
20704        );
20705        assert_eq!(
20706            f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
20707            "-CMS: Number of keys must be positive\r\n"
20708        );
20709        assert_eq!(
20710            f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
20711            "-CMS: wrong number of keys\r\n"
20712        );
20713        assert_eq!(
20714            f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
20715            "-CMS: wrong number of keys/weights\r\n"
20716        );
20717        assert_eq!(
20718            f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
20719            "-CMS: width/depth is not equal\r\n"
20720        );
20721        assert_eq!(
20722            f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
20723            "-CMS: key does not exist\r\n"
20724        );
20725    }
20726
20727    /// A key holding anything else is `WRONGTYPE` to all six, and a key holding
20728    /// a sketch is refused by the two commands that would have to serialise it.
20729    #[test]
20730    fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
20731        let mut f = Fixture::new();
20732        f.run(&[b"SET", b"s", b"text"]);
20733        for cmd in [
20734            vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
20735            vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
20736            vec![&b"CMS.QUERY"[..], b"s", b"a"],
20737            vec![&b"CMS.INFO"[..], b"s"],
20738            vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
20739        ] {
20740            let name = String::from_utf8_lossy(cmd[0]).into_owned();
20741            let reply = f.run(&cmd);
20742            // The two constructors see the key before anything else and say so
20743            // in the module's own words, and the rest are `WRONGTYPE`.
20744            assert!(
20745                reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
20746                "{name}: {reply}"
20747            );
20748        }
20749        f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
20750        // Redis refuses to copy a module key that has no copy callback, and
20751        // these are its words rather than ours. `DUMP` is the other half of
20752        // D-48: the reference has a payload for one of these and we do not.
20753        assert_eq!(
20754            f.run(&[b"COPY", b"c", b"c2"]),
20755            "-ERR not supported for this module key\r\n"
20756        );
20757        assert_eq!(
20758            f.run(&[b"DUMP", b"c"]),
20759            "-ERR DUMP is not supported for this module key\r\n"
20760        );
20761        // A graph is nobody's module and keeps its own sentence.
20762        f.run(&[b"G.NADD", b"g", b"a"]);
20763        assert_eq!(
20764            f.run(&[b"COPY", b"g", b"g2"]),
20765            "-ERR COPY is not supported for a graph\r\n"
20766        );
20767        assert_eq!(
20768            f.run(&[b"DUMP", b"g"]),
20769            "-ERR DUMP is not supported for a graph\r\n"
20770        );
20771        // Everything that does not need a byte shape works on a sketch key the
20772        // way it works on any other.
20773        assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
20774        assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
20775        assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
20776        assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
20777        assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
20778    }
20779
20780    // ------------------------------------------------------------------ topk
20781
20782    /// `TOPK.RESERVE` takes three arguments or six, and looks at the key before
20783    /// it looks at any of them.
20784    #[test]
20785    fn a_reserve_takes_three_arguments_or_six() {
20786        let mut f = Fixture::new();
20787        assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
20788        assert_eq!(
20789            f.run(&[b"TOPK.INFO", b"t"]),
20790            "*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"
20791        );
20792        // Four arguments and five are an arity error rather than a defaulted
20793        // depth or decay.
20794        for cmd in [
20795            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
20796            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
20797        ] {
20798            assert!(f.run(&cmd).contains("wrong number of arguments"));
20799        }
20800        assert_eq!(
20801            f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
20802            "+OK\r\n"
20803        );
20804        // The key is checked first, so a reserve with nothing else right at a
20805        // key that is taken still says the key is taken.
20806        assert_eq!(
20807            f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
20808            "-TopK: key already exists\r\n"
20809        );
20810        assert_eq!(
20811            f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
20812            "-TopK: invalid k\r\n"
20813        );
20814        assert_eq!(
20815            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
20816            "-TopK: invalid width\r\n"
20817        );
20818        assert_eq!(
20819            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
20820            "-TopK: invalid depth\r\n"
20821        );
20822        // Zero is out and one is in, which is the module's `> 0` and `<= 1`.
20823        assert_eq!(
20824            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
20825            "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
20826        );
20827        assert_eq!(
20828            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
20829            "+OK\r\n"
20830        );
20831        // Past the cap, with the one sentence in the family that has a prefix.
20832        assert_eq!(
20833            f.run(&[
20834                b"TOPK.RESERVE",
20835                b"w",
20836                b"1",
20837                b"4294967295",
20838                b"4294967295",
20839                b"0.9"
20840            ]),
20841            "-ERR Insufficient memory to create topk data structure\r\n"
20842        );
20843    }
20844
20845    /// What the sketch keeps, and the three ways of asking about it.
20846    #[test]
20847    fn the_kept_set_is_what_query_and_list_answer_from() {
20848        let mut f = Fixture::new();
20849        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
20850        // A null an item while there is room, then the name of whatever was
20851        // pushed out.
20852        assert_eq!(
20853            f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
20854            "*2\r\n$-1\r\n$-1\r\n"
20855        );
20856        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
20857        // Two slots are full and `c` arrives with a count of one, which is not
20858        // under the smallest kept count, so it takes that slot straight away.
20859        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
20860        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
20861        assert_eq!(
20862            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
20863            "*3\r\n:1\r\n:0\r\n:1\r\n"
20864        );
20865        // The table still counts what the kept set let go of.
20866        assert_eq!(
20867            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
20868            "*3\r\n:11\r\n:1\r\n:6\r\n"
20869        );
20870        assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
20871        assert_eq!(
20872            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
20873            "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
20874        );
20875        // Any prefix of the keyword turns the counts on, the empty string
20876        // included, and only a longer word or a different one is refused.
20877        assert_eq!(
20878            f.run(&[b"TOPK.LIST", b"t", b"w"]),
20879            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
20880        );
20881        assert_eq!(
20882            f.run(&[b"TOPK.LIST", b"t", b""]),
20883            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
20884        );
20885        assert_eq!(
20886            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
20887            "-WITHCOUNT keyword expected\r\n"
20888        );
20889        // And the keyword is looked at before the key, so a missing key with a
20890        // bad keyword complains about the keyword.
20891        assert_eq!(
20892            f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
20893            "-WITHCOUNT keyword expected\r\n"
20894        );
20895        assert_eq!(
20896            f.run(&[b"TOPK.LIST", b"missing"]),
20897            "-TopK: key does not exist\r\n"
20898        );
20899        // An item counted zero times is kept and not listed.
20900        f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
20901        assert_eq!(
20902            f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
20903            "*1\r\n$-1\r\n"
20904        );
20905        assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
20906        assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
20907    }
20908
20909    /// `TOPK.INCRBY` applies as it goes, so a bad increment leaves everything
20910    /// before it counted, and the reply counts what it wrote.
20911    #[test]
20912    fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
20913        let mut f = Fixture::new();
20914        f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
20915        // Three pairs, the middle one bad: two elements come back, one of them
20916        // the error, and the array header says two rather than three. That last
20917        // part is D-51 and it is why a client here stays in step.
20918        assert_eq!(
20919            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
20920            format!(
20921                "*2\r\n$-1\r\n-{}\r\n",
20922                "TopK: increment must be an integer greater or equal to 0                            and smaller or equal to 100,000"
20923            )
20924        );
20925        assert_eq!(
20926            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
20927            "*3\r\n:3\r\n:0\r\n:0\r\n"
20928        );
20929        // A hundred thousand is in and one more is out.
20930        assert_eq!(
20931            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
20932            "*1\r\n$-1\r\n"
20933        );
20934        assert!(
20935            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
20936                .contains("smaller or equal to 100,000")
20937        );
20938        // Pairs have to be pairs.
20939        assert!(
20940            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
20941                .contains("wrong number of arguments")
20942        );
20943        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
20944    }
20945
20946    /// The RESP3 shapes, which are the two the protocols disagree about.
20947    #[test]
20948    fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
20949        let mut f = Fixture::new();
20950        f.run(&[b"HELLO", b"3"]);
20951        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
20952        f.run(&[b"TOPK.ADD", b"t", b"a"]);
20953        assert_eq!(
20954            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
20955            "*2\r\n#t\r\n#f\r\n"
20956        );
20957        // The count stays an integer on both protocols.
20958        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
20959        assert_eq!(
20960            f.run(&[b"TOPK.INFO", b"t"]),
20961            "%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"
20962        );
20963        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
20964    }
20965
20966    /// A top k key answers the module sentences the other sketch families
20967    /// answer, and its own word for its type.
20968    #[test]
20969    fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
20970        let mut f = Fixture::new();
20971        f.run(&[b"SET", b"s", b"text"]);
20972        for cmd in [
20973            vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
20974            vec![&b"TOPK.ADD"[..], b"s", b"a"],
20975            vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
20976            vec![&b"TOPK.QUERY"[..], b"s", b"a"],
20977            vec![&b"TOPK.COUNT"[..], b"s", b"a"],
20978            vec![&b"TOPK.LIST"[..], b"s"],
20979            vec![&b"TOPK.INFO"[..], b"s"],
20980        ] {
20981            let name = String::from_utf8_lossy(cmd[0]).into_owned();
20982            let reply = f.run(&cmd);
20983            assert!(
20984                reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
20985                "{name}: {reply}"
20986            );
20987        }
20988        f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
20989        assert_eq!(
20990            f.run(&[b"COPY", b"t", b"t2"]),
20991            "-ERR not supported for this module key\r\n"
20992        );
20993        assert_eq!(
20994            f.run(&[b"DUMP", b"t"]),
20995            "-ERR DUMP is not supported for this module key\r\n"
20996        );
20997        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
20998        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
20999        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
21000        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
21001        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
21002        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
21003        // Every one of the six that is not the constructor says the same thing
21004        // about a key that is not there.
21005        assert_eq!(
21006            f.run(&[b"TOPK.INFO", b"t3"]),
21007            "-TopK: key does not exist\r\n"
21008        );
21009    }
21010
21011    // --------------------------------------------------------------- tdigest
21012
21013    /// `TDIGEST.CREATE` takes two arguments or four, and the keyword search is a
21014    /// search rather than a lookup.
21015    #[test]
21016    fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
21017        let mut f = Fixture::new();
21018        assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
21019        // A hundred is the default and the capacity is six times it plus ten.
21020        assert_eq!(
21021            f.run(&[b"TDIGEST.INFO", b"t"]),
21022            "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
21023             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
21024             +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
21025        );
21026        assert_eq!(
21027            f.run(&[b"TDIGEST.CREATE", b"t"]),
21028            "-ERR T-Digest: key already exists\r\n"
21029        );
21030        // Three arguments is an arity error and not a missing keyword.
21031        assert!(
21032            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
21033                .contains("wrong number of arguments")
21034        );
21035        assert_eq!(
21036            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
21037            "+OK\r\n"
21038        );
21039        assert_eq!(
21040            f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
21041            "+OK\r\n"
21042        );
21043        // The word is looked for across both trailing arguments and the number
21044        // is then read out of the last one whatever was found, so this looks for
21045        // a number inside the word `COMPRESSION` and does not find one.
21046        assert_eq!(
21047            f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
21048            "-ERR T-Digest: error parsing compression parameter\r\n"
21049        );
21050        assert_eq!(
21051            f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
21052            "-ERR T-Digest: wrong keyword\r\n"
21053        );
21054        assert_eq!(
21055            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
21056            "-ERR T-Digest: error parsing compression parameter\r\n"
21057        );
21058        assert_eq!(
21059            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
21060            "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
21061        );
21062        // The reference's own ceiling, which is where the capacity stops fitting
21063        // in an int, and one past it.
21064        assert_eq!(
21065            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
21066            "-ERR T-Digest: allocation failed\r\n"
21067        );
21068        // And ours, which is a gibibyte of centroids and is D-52.
21069        assert_eq!(
21070            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
21071            "-ERR T-Digest: allocation failed\r\n"
21072        );
21073        // The key is checked before the arguments, so a bad compression at a key
21074        // that is already a digest still says the key is taken.
21075        assert_eq!(
21076            f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
21077            "-ERR T-Digest: key already exists\r\n"
21078        );
21079    }
21080
21081    /// The four samples every note about this family is written against, and the
21082    /// answers a real 8.10.1 gives for them.
21083    #[test]
21084    fn the_quantile_family_answers_what_the_module_answers() {
21085        let mut f = Fixture::new();
21086        f.run(&[b"TDIGEST.CREATE", b"s"]);
21087        assert_eq!(
21088            f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
21089            "+OK\r\n"
21090        );
21091        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
21092        assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
21093        // The cdf of a sample is the weight below it plus half its own.
21094        assert_eq!(
21095            f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
21096            "*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"
21097        );
21098        assert_eq!(
21099            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
21100            "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
21101        );
21102        // Out of order, the walk restarts, and 0.5 answers 3 either way while
21103        // the two after it are read from the front again.
21104        assert_eq!(
21105            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
21106            "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
21107        );
21108        assert_eq!(
21109            f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
21110            "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
21111        );
21112        assert_eq!(
21113            f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
21114            "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
21115        );
21116        assert_eq!(
21117            f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
21118            "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
21119        );
21120        assert_eq!(
21121            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
21122            "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
21123        );
21124        assert_eq!(
21125            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
21126            "$3\r\n2.5\r\n"
21127        );
21128        assert_eq!(
21129            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
21130            "$3\r\n2.5\r\n"
21131        );
21132        // The ranges, which are separate sentences from the parse failures.
21133        assert_eq!(
21134            f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
21135            "-ERR T-Digest: quantile should be in [0,1]\r\n"
21136        );
21137        assert_eq!(
21138            f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
21139            "-ERR T-Digest: error parsing quantile\r\n"
21140        );
21141        assert_eq!(
21142            f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
21143            "-ERR T-Digest: error parsing cdf\r\n"
21144        );
21145        assert_eq!(
21146            f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
21147            "-ERR T-Digest: error parsing value\r\n"
21148        );
21149        assert_eq!(
21150            f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
21151            "-ERR T-Digest: rank needs to be non negative\r\n"
21152        );
21153        assert_eq!(
21154            f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
21155            "-ERR T-Digest: error parsing rank\r\n"
21156        );
21157        // Both cuts have their own parse sentence and share the range one, and
21158        // equal cuts are refused rather than answering nothing.
21159        assert_eq!(
21160            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
21161            "-ERR T-Digest: error parsing low_cut_percentile\r\n"
21162        );
21163        assert_eq!(
21164            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
21165            "-ERR T-Digest: error parsing high_cut_percentile\r\n"
21166        );
21167        assert_eq!(
21168            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
21169            "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
21170        );
21171        assert_eq!(
21172            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
21173            "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
21174        );
21175    }
21176
21177    /// An empty digest answers every question, and answers most of them with
21178    /// something that is not a number.
21179    #[test]
21180    fn an_empty_digest_has_an_answer_for_everything() {
21181        let mut f = Fixture::new();
21182        f.run(&[b"TDIGEST.CREATE", b"e"]);
21183        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
21184        assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
21185        assert_eq!(
21186            f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
21187            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
21188        );
21189        assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
21190        assert_eq!(
21191            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
21192            "$3\r\nnan\r\n"
21193        );
21194        // Minus two, which is a number no rank on a digest with samples in it
21195        // can ever be.
21196        assert_eq!(
21197            f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
21198            "*2\r\n:-2\r\n:-2\r\n"
21199        );
21200        assert_eq!(
21201            f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
21202            "*2\r\n:-2\r\n:-2\r\n"
21203        );
21204        assert_eq!(
21205            f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
21206            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
21207        );
21208        // A reset puts a digest with samples back into exactly this state.
21209        f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
21210        assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
21211        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
21212        // Down to the compression count, so a reset digest and a fresh one of
21213        // the same compression report the same nine numbers.
21214        f.run(&[b"TDIGEST.CREATE", b"e2"]);
21215        assert_eq!(
21216            f.run(&[b"TDIGEST.INFO", b"e"]),
21217            f.run(&[b"TDIGEST.INFO", b"e2"])
21218        );
21219    }
21220
21221    /// The double parser is Redis's and not this engine's, and the two disagree
21222    /// at both ends of the range.
21223    #[test]
21224    fn a_sample_is_read_the_way_redis_reads_a_double() {
21225        let mut f = Fixture::new();
21226        f.run(&[b"TDIGEST.CREATE", b"a"]);
21227        // Overflow and underflow are parse failures rather than an infinity and
21228        // a zero, which is where this parts company with the rest of the engine.
21229        for bad in [
21230            &b"nan"[..],
21231            b"1e400",
21232            b"-1e400",
21233            b"1e309",
21234            b"1e-400",
21235            b"",
21236            b" 1",
21237            b"1 ",
21238            b"1e",
21239            b"--1",
21240        ] {
21241            assert_eq!(
21242                f.run(&[b"TDIGEST.ADD", b"a", bad]),
21243                "-ERR T-Digest: error parsing val parameter\r\n",
21244                "{}",
21245                String::from_utf8_lossy(bad)
21246            );
21247        }
21248        // An infinity spelled out parses and is then refused for being one, with
21249        // a different sentence.
21250        for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
21251            assert_eq!(
21252                f.run(&[b"TDIGEST.ADD", b"a", word]),
21253                "-ERR T-Digest: val parameter needs to be a finite number\r\n",
21254                "{}",
21255                String::from_utf8_lossy(word)
21256            );
21257        }
21258        // These all parse: hex, a bare point either side, and the smallest
21259        // subnormal the reference will take.
21260        for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
21261            assert_eq!(
21262                f.run(&[b"TDIGEST.ADD", b"a", good]),
21263                "+OK\r\n",
21264                "{}",
21265                String::from_utf8_lossy(good)
21266            );
21267        }
21268        // Nothing landed from the failures, so six samples is what there is.
21269        assert!(
21270            f.run(&[b"TDIGEST.INFO", b"a"])
21271                .contains("Observations\r\n:6\r\n")
21272        );
21273        // Every value is parsed before any is added, so this whole command is a
21274        // no op.
21275        assert_eq!(
21276            f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
21277            "-ERR T-Digest: error parsing val parameter\r\n"
21278        );
21279        assert!(
21280            f.run(&[b"TDIGEST.INFO", b"a"])
21281                .contains("Observations\r\n:6\r\n")
21282        );
21283    }
21284
21285    /// What a merge does to its destination, to its inputs and to the buffer
21286    /// split `TDIGEST.INFO` reports.
21287    #[test]
21288    fn a_merge_sweeps_the_destination_between_its_inputs() {
21289        let mut f = Fixture::new();
21290        f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
21291        f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
21292        f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
21293        f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
21294        assert_eq!(
21295            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
21296            "+OK\r\n"
21297        );
21298        // The destination did not exist, so the compression is the largest of
21299        // the inputs. The three from the first input were swept in before the
21300        // three from the second arrived, which is the one visible effect of the
21301        // reference folding one input at a time.
21302        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
21303        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
21304        assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
21305        assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
21306        assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
21307        assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
21308        assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
21309        // Reading a source sweeps it too, so a merge writes to keys it only
21310        // reads from.
21311        assert!(
21312            f.run(&[b"TDIGEST.INFO", b"m1"])
21313                .contains("Merged nodes\r\n:3\r\n")
21314        );
21315        // Without OVERRIDE the destination joins its own inputs, so this takes
21316        // it to nine observations and keeps its own compression.
21317        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
21318        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
21319        assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
21320        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
21321        // With OVERRIDE the old destination is dropped and the compression goes
21322        // back to the largest of the inputs.
21323        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
21324        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
21325        assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
21326        assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
21327        // And COMPRESSION beats both.
21328        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
21329        assert!(
21330            f.run(&[b"TDIGEST.INFO", b"d"])
21331                .contains("Compression\r\n:500\r\n")
21332        );
21333        // Naming the destination as a source folds it in twice.
21334        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
21335        assert!(
21336            f.run(&[b"TDIGEST.INFO", b"d"])
21337                .contains("Observations\r\n:12\r\n")
21338        );
21339        // The arguments, in the order the reference checks them.
21340        assert_eq!(
21341            f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
21342            "-ERR T-Digest: error parsing numkeys\r\n"
21343        );
21344        assert_eq!(
21345            f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
21346            "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
21347        );
21348        assert!(
21349            f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
21350                .contains("wrong number of arguments")
21351        );
21352        assert!(
21353            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
21354                .contains("wrong number of arguments")
21355        );
21356        assert_eq!(
21357            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
21358            "-ERR T-Digest: wrong keyword\r\n"
21359        );
21360        // A source that is not there stops the whole thing, and the destination
21361        // is left as it was.
21362        assert_eq!(
21363            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
21364            "-ERR T-Digest: key does not exist\r\n"
21365        );
21366        assert!(
21367            f.run(&[b"TDIGEST.INFO", b"d"])
21368                .contains("Observations\r\n:12\r\n")
21369        );
21370        // A destination that is not there and is also named as a source is the
21371        // same sentence rather than an empty merge.
21372        assert_eq!(
21373            f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
21374            "-ERR T-Digest: key does not exist\r\n"
21375        );
21376    }
21377
21378    /// The RESP3 shapes, which are the two the protocols disagree about.
21379    #[test]
21380    fn a_digest_answers_doubles_and_a_map_on_resp3() {
21381        let mut f = Fixture::new();
21382        f.run(&[b"HELLO", b"3"]);
21383        f.run(&[b"TDIGEST.CREATE", b"s"]);
21384        f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
21385        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
21386        assert_eq!(
21387            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
21388            "*2\r\n,1\r\n,4\r\n"
21389        );
21390        assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
21391        // The two infinities and the NaN go out as the bare words.
21392        assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
21393        assert_eq!(
21394            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
21395            "*1\r\n,-inf\r\n"
21396        );
21397        f.run(&[b"TDIGEST.CREATE", b"e"]);
21398        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
21399        // The ranks stay integers on both protocols.
21400        assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
21401        // Every question above swept the buffer in, so the four samples are all
21402        // merged by now and the compression count says it happened once.
21403        assert_eq!(
21404            f.run(&[b"TDIGEST.INFO", b"s"]),
21405            "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
21406             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
21407             +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
21408        );
21409    }
21410
21411    /// A t digest key answers the module sentences the other sketch families
21412    /// answer, and its own word for its type.
21413    #[test]
21414    fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
21415        let mut f = Fixture::new();
21416        f.run(&[b"SET", b"s", b"text"]);
21417        for cmd in [
21418            vec![&b"TDIGEST.CREATE"[..], b"s"],
21419            vec![&b"TDIGEST.RESET"[..], b"s"],
21420            vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
21421            vec![&b"TDIGEST.MIN"[..], b"s"],
21422            vec![&b"TDIGEST.MAX"[..], b"s"],
21423            vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
21424            vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
21425            vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
21426            vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
21427            vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
21428            vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
21429            vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
21430            vec![&b"TDIGEST.INFO"[..], b"s"],
21431        ] {
21432            let name = String::from_utf8_lossy(cmd[0]).into_owned();
21433            let reply = f.run(&cmd);
21434            assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
21435        }
21436        // The merge checks its destination the same way, and its sources too.
21437        f.run(&[b"TDIGEST.CREATE", b"t"]);
21438        assert!(
21439            f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
21440                .starts_with("-WRONGTYPE")
21441        );
21442        assert!(
21443            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
21444                .starts_with("-WRONGTYPE")
21445        );
21446        assert_eq!(
21447            f.run(&[b"COPY", b"t", b"t2"]),
21448            "-ERR not supported for this module key\r\n"
21449        );
21450        assert_eq!(
21451            f.run(&[b"DUMP", b"t"]),
21452            "-ERR DUMP is not supported for this module key\r\n"
21453        );
21454        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
21455        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
21456        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
21457        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
21458        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
21459        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
21460        // An empty digest is still a key, so the twelve that are not the
21461        // constructor all say the same thing once it is gone.
21462        assert_eq!(
21463            f.run(&[b"TDIGEST.INFO", b"t3"]),
21464            "-ERR T-Digest: key does not exist\r\n"
21465        );
21466        // The key is looked at before the arguments, so a bad argument at a key
21467        // that is not there still says the key is not there.
21468        assert_eq!(
21469            f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
21470            "-ERR T-Digest: key does not exist\r\n"
21471        );
21472    }
21473
21474    // -------------------------------------------------------------------- ts
21475
21476    /// A `TS.INFO` reply with the memory usage taken out of it.
21477    ///
21478    /// That number is what a series costs here rather than what one costs in the
21479    /// module, which is D-53, and it moves whenever the layout of a chunk does.
21480    /// Everything either side of it is the wire contract and is worth pinning
21481    /// down exactly, so the tests below check the whole reply with the one
21482    /// number lifted out.
21483    fn without_memory(reply: &str) -> String {
21484        let head = "+memoryUsage\r\n:";
21485        let at = reply.find(head).expect("every TS.INFO reports memory");
21486        let rest = &reply[at + head.len()..];
21487        let end = rest.find("\r\n").expect("and it is a whole number");
21488        format!("{}{}", &reply[..at + head.len()], &rest[end..])
21489    }
21490
21491    /// A series is made empty and still says it has a chunk, and the options are
21492    /// read before the key is looked at.
21493    #[test]
21494    fn a_series_is_made_empty_and_reports_on_itself() {
21495        let mut f = Fixture::new();
21496        assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
21497        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
21498        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
21499        // Fourteen fields, so twenty eight elements. An empty series reports one
21500        // chunk and zero at both ends, and neither the chunk type nor the
21501        // duplicate policy is ever a nil.
21502        assert_eq!(
21503            without_memory(&f.run(&[b"TS.INFO", b"t"])),
21504            "*28\r\n\
21505             +totalSamples\r\n:0\r\n\
21506             +memoryUsage\r\n:\r\n\
21507             +firstTimestamp\r\n:0\r\n\
21508             +lastTimestamp\r\n:0\r\n\
21509             +retentionTime\r\n:0\r\n\
21510             +chunkCount\r\n:1\r\n\
21511             +chunkSize\r\n:4096\r\n\
21512             +chunkType\r\n+compressed\r\n\
21513             +duplicatePolicy\r\n+block\r\n\
21514             +labels\r\n*0\r\n\
21515             +sourceKey\r\n$-1\r\n\
21516             +rules\r\n*0\r\n\
21517             +ignoreMaxTimeDiff\r\n:0\r\n\
21518             +ignoreMaxValDiff\r\n$1\r\n0\r\n"
21519        );
21520        // A key that is already there is about the key whatever it holds, and
21521        // the existence is what is checked rather than the type.
21522        assert_eq!(
21523            f.run(&[b"TS.CREATE", b"t"]),
21524            "-ERR TSDB: key already exists\r\n"
21525        );
21526        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
21527        assert_eq!(
21528            f.run(&[b"TS.CREATE", b"str"]),
21529            "-ERR TSDB: key already exists\r\n"
21530        );
21531        // But the arguments are read first, so a bad one at a key that is there
21532        // answers about the argument.
21533        assert_eq!(
21534            f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
21535            "-ERR TSDB: Couldn't parse RETENTION\r\n"
21536        );
21537        // The seven that will not make a series say WRONGTYPE about a key
21538        // holding something else, where the two that would say a sentence.
21539        // The word is inside the sentence and not in front of it, because the
21540        // module writes its own error text and Redis puts ERR on the front of
21541        // anything a module writes.
21542        let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
21543        assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
21544        assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
21545        assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
21546        assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
21547        assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
21548        assert_eq!(
21549            f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
21550            "-ERR TSDB: the key is not a TSDB key\r\n"
21551        );
21552        // And the ones that will not make one say so about a key that is gone.
21553        assert_eq!(
21554            f.run(&[b"TS.INFO", b"nope"]),
21555            "-ERR TSDB: the key does not exist\r\n"
21556        );
21557        assert_eq!(
21558            f.run(&[b"TS.GET", b"nope"]),
21559            "-ERR TSDB: the key does not exist\r\n"
21560        );
21561        assert_eq!(
21562            f.run(&[b"TS.ALTER", b"nope"]),
21563            "-ERR TSDB: the key does not exist\r\n"
21564        );
21565        assert_eq!(
21566            f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
21567            "-ERR TSDB: the key does not exist\r\n"
21568        );
21569    }
21570
21571    /// Every option word, including the ones that are wrong, and the scan that
21572    /// finds them.
21573    #[test]
21574    fn the_options_are_a_keyword_scan_and_not_a_grammar() {
21575        let mut f = Fixture::new();
21576        assert_eq!(
21577            f.run(&[
21578                b"TS.CREATE",
21579                b"t",
21580                b"RETENTION",
21581                b"5000",
21582                b"ENCODING",
21583                b"UNCOMPRESSED",
21584                b"CHUNK_SIZE",
21585                b"128",
21586                b"DUPLICATE_POLICY",
21587                b"LAST",
21588                b"IGNORE",
21589                b"10",
21590                b"0.5",
21591                b"LABELS",
21592                b"room",
21593                b"kitchen"
21594            ]),
21595            "+OK\r\n"
21596        );
21597        let info = f.run(&[b"TS.INFO", b"t"]);
21598        assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
21599        assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
21600        assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
21601        assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
21602        assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
21603        // A plain double here, where a sample value out of TS.GET is the
21604        // shortest digits that read back as the same number.
21605        assert!(
21606            info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
21607            "{info}"
21608        );
21609        assert!(
21610            info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
21611            "{info}"
21612        );
21613
21614        // A word that is not an option is read past rather than refused.
21615        assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
21616        // LABELS eats everything after it in pairs, and the later scans still
21617        // look inside what it ate, so this sets a retention and stores a label
21618        // called RETENTION at the same time.
21619        assert_eq!(
21620            f.run(&[
21621                b"TS.CREATE",
21622                b"g",
21623                b"LABELS",
21624                b"a",
21625                b"b",
21626                b"RETENTION",
21627                b"5"
21628            ]),
21629            "+OK\r\n"
21630        );
21631        let greedy = f.run(&[b"TS.INFO", b"g"]);
21632        assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
21633        assert!(
21634            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"),
21635            "{greedy}"
21636        );
21637
21638        // Every way an option can be wrong, in the order the module reads them.
21639        assert_eq!(
21640            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
21641            "-ERR TSDB: Couldn't parse LABELS\r\n"
21642        );
21643        assert_eq!(
21644            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
21645            "-ERR TSDB: Couldn't parse LABELS\r\n"
21646        );
21647        assert_eq!(
21648            f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
21649            "-ERR TSDB: Couldn't parse RETENTION\r\n"
21650        );
21651        // A retention below zero is one of the two the module writes with no
21652        // ERR in front of it, where one that is not a number gets one.
21653        assert_eq!(
21654            f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
21655            "-TSDB: Couldn't parse RETENTION\r\n"
21656        );
21657        assert_eq!(
21658            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
21659            "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
21660        );
21661        assert_eq!(
21662            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
21663            "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
21664        );
21665        assert_eq!(
21666            f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
21667            "-ERR TSDB: unknown ENCODING parameter\r\n"
21668        );
21669        // And an ENCODING with nothing behind it is an arity error where every
21670        // other keyword in the same spot is a sentence.
21671        assert!(
21672            f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
21673                .contains("wrong number of arguments for 'ts.create' command")
21674        );
21675        assert_eq!(
21676            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
21677            "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
21678        );
21679        assert_eq!(
21680            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
21681            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
21682        );
21683        assert_eq!(
21684            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
21685            "-ERR TSDB: Couldn't parse IGNORE\r\n"
21686        );
21687        assert_eq!(
21688            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
21689            "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
21690        );
21691        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
21692
21693        // An alter changes what was named and leaves the rest alone, and reads
21694        // an encoding only far enough to refuse a bad one.
21695        assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
21696        let after = f.run(&[b"TS.INFO", b"t"]);
21697        assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
21698        assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
21699        assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
21700        assert_eq!(
21701            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
21702            "-ERR TSDB: unknown ENCODING parameter\r\n"
21703        );
21704        // An encoding it does take is still not applied.
21705        assert_eq!(
21706            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
21707            "+OK\r\n"
21708        );
21709        assert!(
21710            f.run(&[b"TS.INFO", b"t"])
21711                .contains("+chunkType\r\n+uncompressed\r\n")
21712        );
21713    }
21714
21715    /// Samples go in, come back out and are refused for the reasons the module
21716    /// refuses them.
21717    #[test]
21718    fn samples_land_where_they_are_put_and_the_newest_comes_back() {
21719        let mut f = Fixture::new();
21720        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
21721        // The series was made on the way in.
21722        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
21723        assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
21724        // A sample value goes out as a simple string of the shortest digits
21725        // that read back as the same number.
21726        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
21727        assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
21728        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
21729        // An empty series has no newest sample and answers an empty array
21730        // rather than a nil.
21731        assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
21732        assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
21733
21734        // The value is read before the key, so a bad one against a key holding
21735        // a string is about the value.
21736        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
21737        assert_eq!(
21738            f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
21739            "-ERR TSDB: invalid value\r\n"
21740        );
21741        // The grammar is tighter than the one a number argument usually gets:
21742        // no leading plus, no bare fraction, no infinity and nothing that does
21743        // not fit.
21744        for bad in [
21745            &b".5"[..],
21746            b"1.",
21747            b"+1",
21748            b" 1",
21749            b"0x10",
21750            b"inf",
21751            b"1e400",
21752            b"--1",
21753            b"1e",
21754        ] {
21755            assert_eq!(
21756                f.run(&[b"TS.ADD", b"v", b"1", bad]),
21757                "-ERR TSDB: invalid value\r\n",
21758                "{}",
21759                String::from_utf8_lossy(bad)
21760            );
21761        }
21762        // And a reading that is not a number is one of three words.
21763        assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
21764
21765        // A timestamp that is not a number, and one that is and is below zero,
21766        // are two different sentences.
21767        assert_eq!(
21768            f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
21769            "-ERR TSDB: invalid timestamp\r\n"
21770        );
21771        assert_eq!(
21772            f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
21773            "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
21774        );
21775
21776        // A repeated timestamp is blocked by default, and ON_DUPLICATE on the
21777        // command beats what the series was told.
21778        assert_eq!(
21779            f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
21780            "-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"
21781        );
21782        assert_eq!(
21783            f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
21784            ":300\r\n"
21785        );
21786        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
21787        // ON_DUPLICATE is only read when the key was already there, which is
21788        // why a policy word that is not a policy passes on a fresh key.
21789        assert_eq!(
21790            f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
21791            ":1\r\n"
21792        );
21793        assert_eq!(
21794            f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
21795            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
21796        );
21797
21798        // Retention is exact and it is checked before anything else happens, so
21799        // a sample landing behind the window is refused rather than trimmed.
21800        assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
21801        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
21802        assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
21803        assert_eq!(
21804            f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
21805            "-ERR TSDB: Timestamp is older than retention\r\n"
21806        );
21807        // And the window trims as it moves.
21808        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
21809        assert!(
21810            f.run(&[b"TS.INFO", b"r"])
21811                .contains("+totalSamples\r\n:1\r\n")
21812        );
21813
21814        // An ignore window drops a sample close enough to the newest one to be
21815        // uninteresting, and answers the newest timestamp so a client can tell.
21816        assert_eq!(
21817            f.run(&[
21818                b"TS.CREATE",
21819                b"i",
21820                b"DUPLICATE_POLICY",
21821                b"LAST",
21822                b"IGNORE",
21823                b"10",
21824                b"0.5"
21825            ]),
21826            "+OK\r\n"
21827        );
21828        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
21829        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
21830        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
21831    }
21832
21833    /// Every triple in a `TS.MADD` is answered on its own, and none of them
21834    /// makes a series.
21835    #[test]
21836    fn a_madd_answers_each_triple_and_creates_nothing() {
21837        let mut f = Fixture::new();
21838        assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
21839        assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
21840        assert_eq!(
21841            f.run(&[
21842                b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
21843            ]),
21844            "*3\r\n:100\r\n:100\r\n:200\r\n"
21845        );
21846        // A key that is not a series is an error in its own slot and the ones
21847        // after it still land.
21848        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
21849        assert_eq!(
21850            f.run(&[
21851                b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
21852            ]),
21853            "*3\r\n\
21854             -ERR TSDB: the key is not a TSDB key\r\n\
21855             -ERR TSDB: the key is not a TSDB key\r\n\
21856             :300\r\n"
21857        );
21858        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
21859        // A bad value and a bad timestamp are answered in their slots too.
21860        assert_eq!(
21861            f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
21862            "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
21863        );
21864        // And a list that is not made of triples is an arity error.
21865        assert!(
21866            f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
21867                .contains("wrong number of arguments for 'ts.madd' command")
21868        );
21869    }
21870
21871    /// The two increments, which only ever write forwards.
21872    #[test]
21873    fn an_increment_walks_the_newest_value_up_and_down() {
21874        let mut f = Fixture::new();
21875        assert_eq!(
21876            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
21877            ":100\r\n"
21878        );
21879        assert_eq!(
21880            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
21881            ":100\r\n"
21882        );
21883        // Two on one timestamp add up rather than collide, because the sample
21884        // goes in under the last policy whatever the series says.
21885        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
21886        assert_eq!(
21887            f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
21888            ":200\r\n"
21889        );
21890        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
21891        // A timestamp behind the newest sample is the other of the two errors
21892        // the module writes with no ERR in front of it.
21893        assert_eq!(
21894            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
21895            "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
21896        );
21897        // The increment goes through the ordinary number reader, so it takes
21898        // what a sample value will not and refuses a NaN that a sample value
21899        // takes.
21900        assert_eq!(
21901            f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
21902            ":1\r\n"
21903        );
21904        assert_eq!(
21905            f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
21906            ":1\r\n"
21907        );
21908        assert_eq!(
21909            f.run(&[b"TS.INCRBY", b"t", b"nan"]),
21910            "-ERR TSDB: invalid increase/decrease value\r\n"
21911        );
21912        assert_eq!(
21913            f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
21914            "-ERR TSDB: invalid increase/decrease value\r\n"
21915        );
21916        // A key holding something else is WRONGTYPE and is answered before the
21917        // number is looked at.
21918        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
21919        assert_eq!(
21920            f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
21921            "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
21922        );
21923        // A TIMESTAMP keyword with nothing behind it is about the timestamp.
21924        // The reference reads one past the end of its own arguments here and
21925        // answers whatever was in that memory, so there is nothing to copy and
21926        // this answers the same thing every time.
21927        assert_eq!(
21928            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
21929            "-ERR TSDB: invalid timestamp\r\n"
21930        );
21931        // And one behind a LABELS is a label name rather than the keyword, so
21932        // this lands at the clock rather than at 5.
21933        assert_eq!(
21934            f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
21935            format!(":{}\r\n", f.server.now_ms())
21936        );
21937        // Adding to a series whose newest value is not a number has no answer.
21938        assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
21939        assert_eq!(
21940            f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
21941            "-ERR TSDB: cannot increment/decrement NaN value\r\n"
21942        );
21943    }
21944
21945    /// Deleting a span, both ends included.
21946    #[test]
21947    fn deleting_takes_out_a_span_and_answers_how_many_went() {
21948        let mut f = Fixture::new();
21949        for at in [b"100".as_slice(), b"200", b"300", b"400"] {
21950            f.run(&[b"TS.ADD", b"t", at, b"1"]);
21951        }
21952        assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
21953        assert!(
21954            f.run(&[b"TS.INFO", b"t"])
21955                .contains("+totalSamples\r\n:2\r\n")
21956        );
21957        // Ends the wrong way round take nothing out rather than being an error.
21958        assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
21959        // The two open ends.
21960        assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
21961        // A series everything has been deleted from keeps its chunk and reports
21962        // zero at both ends again.
21963        let empty = f.run(&[b"TS.INFO", b"t"]);
21964        assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
21965        assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
21966        assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
21967        assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
21968        assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
21969        // The two ends have their own sentences.
21970        assert_eq!(
21971            f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
21972            "-ERR TSDB: wrong fromTimestamp\r\n"
21973        );
21974        assert_eq!(
21975            f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
21976            "-ERR TSDB: wrong toTimestamp\r\n"
21977        );
21978        assert_eq!(
21979            f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
21980            "-ERR TSDB: wrong fromTimestamp\r\n"
21981        );
21982    }
21983
21984    /// What RESP3 changes, which is the two places a number is written and the
21985    /// shape of `TS.INFO`.
21986    #[test]
21987    fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
21988        let mut f = Fixture::new();
21989        f.out = Out::new(Proto::Resp3);
21990        assert_eq!(
21991            f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
21992            "+OK\r\n"
21993        );
21994        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
21995        // A double rather than the simple string RESP2 gets.
21996        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
21997        assert_eq!(
21998            without_memory(&f.run(&[b"TS.INFO", b"t"])),
21999            "%14\r\n\
22000             +totalSamples\r\n:1\r\n\
22001             +memoryUsage\r\n:\r\n\
22002             +firstTimestamp\r\n:100\r\n\
22003             +lastTimestamp\r\n:100\r\n\
22004             +retentionTime\r\n:0\r\n\
22005             +chunkCount\r\n:1\r\n\
22006             +chunkSize\r\n:4096\r\n\
22007             +chunkType\r\n+compressed\r\n\
22008             +duplicatePolicy\r\n+block\r\n\
22009             +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
22010             +sourceKey\r\n_\r\n\
22011             +rules\r\n%0\r\n\
22012             +ignoreMaxTimeDiff\r\n:0\r\n\
22013             +ignoreMaxValDiff\r\n,0\r\n"
22014        );
22015    }
22016
22017    /// Reading a span back, both ways round, with the two ends and the three
22018    /// things that trim what comes out.
22019    #[test]
22020    fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
22021        let mut f = Fixture::new();
22022        for (at, v) in [
22023            (b"100".as_slice(), b"1".as_slice()),
22024            (b"200", b"2"),
22025            (b"300", b"3"),
22026            (b"400", b"4"),
22027        ] {
22028            f.run(&[b"TS.ADD", b"t", at, v]);
22029        }
22030        assert_eq!(
22031            f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
22032            "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
22033             *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
22034        );
22035        // Both ends are included.
22036        assert_eq!(
22037            f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
22038            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
22039        );
22040        // Backwards, and the count takes from the front of what comes out, so
22041        // backwards it takes the newest.
22042        assert_eq!(
22043            f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
22044            "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
22045        );
22046        // Ends the wrong way round are empty rather than an error.
22047        assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
22048        // The two filters.
22049        assert_eq!(
22050            f.run(&[
22051                b"TS.RANGE",
22052                b"t",
22053                b"-",
22054                b"+",
22055                b"FILTER_BY_VALUE",
22056                b"2",
22057                b"3"
22058            ]),
22059            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
22060        );
22061        assert_eq!(
22062            f.run(&[
22063                b"TS.RANGE",
22064                b"t",
22065                b"-",
22066                b"+",
22067                b"FILTER_BY_TS",
22068                b"100",
22069                b"400"
22070            ]),
22071            "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
22072        );
22073        // A word that is not an option is ignored wherever it sits.
22074        assert_eq!(
22075            f.run(&[
22076                b"TS.RANGE",
22077                b"t",
22078                b"-",
22079                b"+",
22080                b"ZZZ",
22081                b"FILTER_BY_TS",
22082                b"400"
22083            ]),
22084            "*1\r\n*2\r\n:400\r\n+4\r\n"
22085        );
22086        // `LATEST` means nothing until there is a compaction rule to follow.
22087        assert_eq!(
22088            f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
22089            "*1\r\n*2\r\n:100\r\n+1\r\n"
22090        );
22091    }
22092
22093    /// The bucketing, which is one column a reduction and a flat row.
22094    #[test]
22095    fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
22096        let mut f = Fixture::new();
22097        for (at, v) in [
22098            (b"100".as_slice(), b"1".as_slice()),
22099            (b"200", b"2"),
22100            (b"300", b"3"),
22101            (b"400", b"4"),
22102        ] {
22103            f.run(&[b"TS.ADD", b"t", at, v]);
22104        }
22105        assert_eq!(
22106            f.run(&[
22107                b"TS.RANGE",
22108                b"t",
22109                b"-",
22110                b"+",
22111                b"AGGREGATION",
22112                b"avg",
22113                b"200"
22114            ]),
22115            "*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"
22116        );
22117        // Three reductions is a row of four and not a row of two with a nested
22118        // three in it.
22119        assert_eq!(
22120            f.run(&[
22121                b"TS.RANGE",
22122                b"t",
22123                b"-",
22124                b"+",
22125                b"AGGREGATION",
22126                b"min,max,count",
22127                b"200"
22128            ]),
22129            "*3\r\n\
22130             *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
22131             *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
22132             *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
22133        );
22134        // The timestamp a bucket is reported under.
22135        assert_eq!(
22136            f.run(&[
22137                b"TS.RANGE",
22138                b"t",
22139                b"-",
22140                b"+",
22141                b"AGGREGATION",
22142                b"avg",
22143                b"200",
22144                b"BUCKETTIMESTAMP",
22145                b"+"
22146            ]),
22147            "*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"
22148        );
22149        // An alignment moves where the bucket edges land.
22150        assert_eq!(
22151            f.run(&[
22152                b"TS.RANGE",
22153                b"t",
22154                b"100",
22155                b"400",
22156                b"ALIGN",
22157                b"100",
22158                b"AGGREGATION",
22159                b"sum",
22160                b"200"
22161            ]),
22162            "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
22163        );
22164        // A `COUNT` sitting where the reduction name belongs is that name, and
22165        // the scan for a real one starts again two words later.
22166        assert_eq!(
22167            f.run(&[
22168                b"TS.RANGE",
22169                b"t",
22170                b"-",
22171                b"+",
22172                b"AGGREGATION",
22173                b"count",
22174                b"200"
22175            ]),
22176            "*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"
22177        );
22178        assert_eq!(
22179            f.run(&[
22180                b"TS.RANGE",
22181                b"t",
22182                b"-",
22183                b"+",
22184                b"AGGREGATION",
22185                b"count",
22186                b"200",
22187                b"COUNT",
22188                b"1"
22189            ]),
22190            "*1\r\n*2\r\n:0\r\n+1\r\n"
22191        );
22192    }
22193
22194    /// `EMPTY` fills the gaps between readings and nothing else, and `last`
22195    /// carries two different things depending on which kind of empty it is.
22196    #[test]
22197    fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
22198        let mut f = Fixture::new();
22199        for (at, v) in [
22200            (b"0".as_slice(), b"1".as_slice()),
22201            (b"100", b"2"),
22202            (b"500", b"nan"),
22203            (b"600", b"3"),
22204        ] {
22205            f.run(&[b"TS.ADD", b"g", at, v]);
22206        }
22207        // Without `EMPTY` the buckets with nothing in them are not there at all,
22208        // and neither is the one holding only a reading that is not a number.
22209        assert_eq!(
22210            f.run(&[
22211                b"TS.RANGE",
22212                b"g",
22213                b"-",
22214                b"+",
22215                b"AGGREGATION",
22216                b"avg",
22217                b"100"
22218            ]),
22219            "*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"
22220        );
22221        // The sum of nothing is zero rather than not a number.
22222        assert_eq!(
22223            f.run(&[
22224                b"TS.RANGE",
22225                b"g",
22226                b"-",
22227                b"+",
22228                b"AGGREGATION",
22229                b"sum",
22230                b"100",
22231                b"EMPTY"
22232            ]),
22233            "*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\
22234             *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\
22235             *2\r\n:600\r\n+3\r\n"
22236        );
22237        // Buckets 200 through 400 have no readings at all and carry the reading
22238        // before the gap either way round. Bucket 500 has a reading that is not
22239        // a number, so it carries whatever the bucket before it in the reading
22240        // direction answered, which is 2 forwards and 3 backwards.
22241        assert_eq!(
22242            f.run(&[
22243                b"TS.RANGE",
22244                b"g",
22245                b"-",
22246                b"+",
22247                b"AGGREGATION",
22248                b"last",
22249                b"100",
22250                b"EMPTY"
22251            ]),
22252            "*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\
22253             *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\
22254             *2\r\n:600\r\n+3\r\n"
22255        );
22256        assert_eq!(
22257            f.run(&[
22258                b"TS.REVRANGE",
22259                b"g",
22260                b"-",
22261                b"+",
22262                b"AGGREGATION",
22263                b"last",
22264                b"100",
22265                b"EMPTY"
22266            ]),
22267            "*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\
22268             *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\
22269             *2\r\n:0\r\n+1\r\n"
22270        );
22271        // And a window that opens on that bucket has nothing in range before it
22272        // to carry, so it answers not a number.
22273        assert_eq!(
22274            f.run(&[
22275                b"TS.RANGE",
22276                b"g",
22277                b"500",
22278                b"600",
22279                b"AGGREGATION",
22280                b"last",
22281                b"100",
22282                b"EMPTY"
22283            ]),
22284            "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
22285        );
22286    }
22287
22288    /// The sentences a read answers when its options do not add up, which are
22289    /// the module's own word for word.
22290    #[test]
22291    fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
22292        let mut f = Fixture::new();
22293        f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
22294        f.run(&[b"SET", b"str", b"x"]);
22295        let cases: &[(&[&[u8]], &str)] = &[
22296            (
22297                &[b"TS.RANGE", b"t"],
22298                "-ERR wrong number of arguments for 'ts.range' command\r\n",
22299            ),
22300            // The key is resolved before a single option is read.
22301            (
22302                &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
22303                "-ERR TSDB: the key does not exist\r\n",
22304            ),
22305            (
22306                &[b"TS.RANGE", b"str", b"-", b"+"],
22307                "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
22308            ),
22309            (
22310                &[b"TS.RANGE", b"t", b"abc", b"+"],
22311                "-ERR TSDB: wrong fromTimestamp\r\n",
22312            ),
22313            (
22314                &[b"TS.RANGE", b"t", b"-", b"abc"],
22315                "-ERR TSDB: wrong toTimestamp\r\n",
22316            ),
22317            (
22318                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
22319                "-ERR TSDB: COUNT argument is missing\r\n",
22320            ),
22321            (
22322                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
22323                "-ERR TSDB: Couldn't parse COUNT\r\n",
22324            ),
22325            (
22326                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
22327                "-ERR TSDB: Invalid COUNT value\r\n",
22328            ),
22329            (
22330                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
22331                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
22332            ),
22333            (
22334                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
22335                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
22336            ),
22337            (
22338                &[
22339                    b"TS.RANGE",
22340                    b"t",
22341                    b"-",
22342                    b"+",
22343                    b"AGGREGATION",
22344                    b"nope",
22345                    b"100",
22346                ],
22347                "-ERR TSDB: Unknown aggregation type\r\n",
22348            ),
22349            (
22350                &[
22351                    b"TS.RANGE",
22352                    b"t",
22353                    b"-",
22354                    b"+",
22355                    b"AGGREGATION",
22356                    b"avg,,min",
22357                    b"100",
22358                ],
22359                "-ERR TSDB: Empty aggregation type in list\r\n",
22360            ),
22361            // The list of names is read before the width is looked at.
22362            (
22363                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
22364                "-ERR TSDB: Unknown aggregation type\r\n",
22365            ),
22366            (
22367                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
22368                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
22369            ),
22370            (
22371                &[
22372                    b"TS.RANGE",
22373                    b"t",
22374                    b"-",
22375                    b"+",
22376                    b"AGGREGATION",
22377                    b"avg",
22378                    b"100",
22379                    b"X",
22380                    b"EMPTY",
22381                ],
22382                "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
22383            ),
22384            (
22385                &[
22386                    b"TS.RANGE",
22387                    b"t",
22388                    b"-",
22389                    b"+",
22390                    b"AGGREGATION",
22391                    b"avg",
22392                    b"100",
22393                    b"BUCKETTIMESTAMP",
22394                    b"z",
22395                ],
22396                "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
22397            ),
22398            (
22399                &[
22400                    b"TS.RANGE",
22401                    b"t",
22402                    b"-",
22403                    b"+",
22404                    b"AGGREGATION",
22405                    b"avg",
22406                    b"100",
22407                    b"X",
22408                    b"Y",
22409                    b"BUCKETTIMESTAMP",
22410                    b"-",
22411                ],
22412                "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
22413                 AGGREGATION flag\r\n",
22414            ),
22415            (
22416                &[
22417                    b"TS.RANGE",
22418                    b"t",
22419                    b"-",
22420                    b"+",
22421                    b"ALIGN",
22422                    b"z",
22423                    b"AGGREGATION",
22424                    b"avg",
22425                    b"100",
22426                ],
22427                "-ERR TSDB: unknown ALIGN parameter\r\n",
22428            ),
22429            (
22430                &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
22431                "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
22432            ),
22433            (
22434                &[
22435                    b"TS.RANGE",
22436                    b"t",
22437                    b"-",
22438                    b"+",
22439                    b"ALIGN",
22440                    b"-",
22441                    b"AGGREGATION",
22442                    b"avg",
22443                    b"100",
22444                ],
22445                "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
22446            ),
22447            (
22448                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
22449                "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
22450            ),
22451            (
22452                &[
22453                    b"TS.RANGE",
22454                    b"t",
22455                    b"-",
22456                    b"+",
22457                    b"FILTER_BY_VALUE",
22458                    b"x",
22459                    b"2",
22460                ],
22461                "-ERR TSDB: Couldn't parse MIN\r\n",
22462            ),
22463            (
22464                &[
22465                    b"TS.RANGE",
22466                    b"t",
22467                    b"-",
22468                    b"+",
22469                    b"FILTER_BY_VALUE",
22470                    b"1",
22471                    b"y",
22472                ],
22473                "-ERR TSDB: Couldn't parse MAX\r\n",
22474            ),
22475            (
22476                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
22477                "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
22478            ),
22479        ];
22480        for (argv, want) in cases {
22481            let got = f.run(argv);
22482            assert_eq!(&got, want, "{:?}", argv.last());
22483        }
22484        // The one sentence here that is yo's own rather than the module's, which
22485        // is D-54. A read that would build more rows than yo will build is
22486        // refused instead of attempted.
22487        f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
22488        f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
22489        assert_eq!(
22490            f.run(&[
22491                b"TS.RANGE",
22492                b"wide",
22493                b"-",
22494                b"+",
22495                b"AGGREGATION",
22496                b"avg",
22497                b"1",
22498                b"EMPTY"
22499            ]),
22500            "-ERR TSDB: the requested range holds too many empty buckets\r\n"
22501        );
22502    }
22503
22504    /// What RESP3 changes on a read, which is only how a number is written.
22505    #[test]
22506    fn resp3_writes_a_read_value_as_a_double() {
22507        let mut f = Fixture::new();
22508        f.out = Out::new(Proto::Resp3);
22509        for (at, v) in [
22510            (b"0".as_slice(), b"1".as_slice()),
22511            (b"100", b"2"),
22512            (b"500", b"nan"),
22513            (b"600", b"3"),
22514        ] {
22515            f.run(&[b"TS.ADD", b"g", at, v]);
22516        }
22517        assert_eq!(
22518            f.run(&[
22519                b"TS.RANGE",
22520                b"g",
22521                b"0",
22522                b"100",
22523                b"AGGREGATION",
22524                b"avg,min",
22525                b"200"
22526            ]),
22527            "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
22528        );
22529        assert_eq!(
22530            f.run(&[
22531                b"TS.RANGE",
22532                b"g",
22533                b"500",
22534                b"600",
22535                b"AGGREGATION",
22536                b"last",
22537                b"100",
22538                b"EMPTY"
22539            ]),
22540            "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
22541        );
22542    }
22543
22544    /// Two series with an overlap and a gap each, plus a third holding nothing,
22545    /// which is what the joined reads are measured against.
22546    fn joined() -> Fixture {
22547        let mut f = Fixture::new();
22548        f.run(&[b"TS.CREATE", b"z"]);
22549        for (at, v) in [
22550            (b"10".as_slice(), b"1".as_slice()),
22551            (b"20", b"2"),
22552            (b"40", b"4"),
22553            (b"50", b"5"),
22554        ] {
22555            f.run(&[b"TS.ADD", b"x", at, v]);
22556        }
22557        for (at, v) in [
22558            (b"20".as_slice(), b"20".as_slice()),
22559            (b"30", b"30"),
22560            (b"50", b"50"),
22561            (b"60", b"60"),
22562        ] {
22563            f.run(&[b"TS.ADD", b"y", at, v]);
22564        }
22565        f
22566    }
22567
22568    /// The joined read lines its keys up on the timestamp and writes a row as
22569    /// the timestamp and then a nested array of the columns, which is the one
22570    /// shape in the family that is not the flat pair.
22571    #[test]
22572    fn an_nrange_joins_its_keys_on_the_timestamp() {
22573        let mut f = joined();
22574        // One key still nests, so the shape does not depend on the count.
22575        assert_eq!(
22576            f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
22577            "*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\
22578             *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
22579        );
22580        // A key with no reading where another key has one writes NaN there.
22581        assert_eq!(
22582            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
22583            "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
22584             *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
22585             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
22586             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
22587             *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
22588             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
22589        );
22590        // A series holding nothing is a column of NaN and never a row of its
22591        // own, and the same key twice answers twice.
22592        assert_eq!(
22593            f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
22594            "*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"
22595        );
22596        assert_eq!(
22597            f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
22598            "*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"
22599        );
22600        // COUNT is applied to the joined rows and not to each key, so backwards
22601        // it gives the newest joined row rather than the newest of each.
22602        assert_eq!(
22603            f.run(&[
22604                b"TS.NREVRANGE",
22605                b"2",
22606                b"x",
22607                b"y",
22608                b"-",
22609                b"+",
22610                b"COUNT",
22611                b"1"
22612            ]),
22613            "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
22614        );
22615        assert_eq!(
22616            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
22617            "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
22618        );
22619        // The two sample filters are settled a key at a time, before the join.
22620        assert_eq!(
22621            f.run(&[
22622                b"TS.NRANGE",
22623                b"2",
22624                b"x",
22625                b"y",
22626                b"-",
22627                b"+",
22628                b"FILTER_BY_VALUE",
22629                b"2",
22630                b"30"
22631            ]),
22632            "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
22633             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
22634             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
22635             *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
22636        );
22637    }
22638
22639    /// The aggregation on a joined read names one reduction a key and then the
22640    /// one bucket width, and each name may be a comma list, so a row can be
22641    /// wider than the key count.
22642    #[test]
22643    fn an_nrange_aggregation_names_one_reduction_a_key() {
22644        let mut f = joined();
22645        assert_eq!(
22646            f.run(&[
22647                b"TS.NRANGE",
22648                b"2",
22649                b"x",
22650                b"y",
22651                b"-",
22652                b"+",
22653                b"AGGREGATION",
22654                b"sum",
22655                b"sum",
22656                b"20"
22657            ]),
22658            "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
22659             *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
22660             *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
22661             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
22662        );
22663        // A comma list on the first key widens the row to three columns.
22664        assert_eq!(
22665            f.run(&[
22666                b"TS.NRANGE",
22667                b"2",
22668                b"x",
22669                b"y",
22670                b"-",
22671                b"+",
22672                b"AGGREGATION",
22673                b"sum,count",
22674                b"avg",
22675                b"20"
22676            ]),
22677            "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
22678             *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
22679             *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
22680             *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
22681        );
22682        // Everything behind the width moves along with it, so BUCKETTIMESTAMP
22683        // sits one or two past the width whatever the key count is.
22684        assert_eq!(
22685            f.run(&[
22686                b"TS.NRANGE",
22687                b"2",
22688                b"x",
22689                b"y",
22690                b"-",
22691                b"+",
22692                b"AGGREGATION",
22693                b"avg",
22694                b"sum",
22695                b"100",
22696                b"EMPTY",
22697                b"BUCKETTIMESTAMP",
22698                b"end"
22699            ]),
22700            "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
22701        );
22702        // A COUNT landing in one of the name slots is a reduction name and not
22703        // the keyword, and the read then has no count at all.
22704        assert_eq!(
22705            f.run(&[
22706                b"TS.NRANGE",
22707                b"2",
22708                b"x",
22709                b"y",
22710                b"-",
22711                b"+",
22712                b"AGGREGATION",
22713                b"avg",
22714                b"COUNT",
22715                b"100"
22716            ]),
22717            "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
22718        );
22719    }
22720
22721    /// The sentences a joined read answers when it does not add up, which are
22722    /// the module's own and come out in the module's own order.
22723    #[test]
22724    fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
22725        let mut f = joined();
22726        f.run(&[b"SET", b"str", b"hi"]);
22727        let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
22728        let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
22729                       must be equal to numkeys\r\n";
22730        let cases: &[(&[&[u8]], &str)] = &[
22731            (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
22732            (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
22733            (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
22734            // Not enough words behind the count for the keys and both ends of
22735            // the span, which is an arity error however many keys were named.
22736            (
22737                &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
22738                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
22739            ),
22740            (
22741                &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
22742                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
22743            ),
22744            // The reduction names are read before the two ends of the span,
22745            // which no other option is.
22746            (
22747                &[
22748                    b"TS.NRANGE",
22749                    b"2",
22750                    b"x",
22751                    b"y",
22752                    b"abc",
22753                    b"+",
22754                    b"AGGREGATION",
22755                    b"nope",
22756                    b"sum",
22757                    b"100",
22758                ],
22759                "-ERR TSDB: Unknown aggregation type\r\n",
22760            ),
22761            (
22762                &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
22763                "-ERR TSDB: wrong fromTimestamp\r\n",
22764            ),
22765            (
22766                &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
22767                "-ERR TSDB: wrong toTimestamp\r\n",
22768            ),
22769            // A name slot that is missing or holds a number is the count
22770            // sentence, and a width slot that is itself a reduction name is
22771            // that sentence as well.
22772            (
22773                &[
22774                    b"TS.NRANGE",
22775                    b"2",
22776                    b"x",
22777                    b"y",
22778                    b"-",
22779                    b"+",
22780                    b"AGGREGATION",
22781                    b"avg",
22782                ],
22783                numkeys,
22784            ),
22785            (
22786                &[
22787                    b"TS.NRANGE",
22788                    b"2",
22789                    b"x",
22790                    b"y",
22791                    b"-",
22792                    b"+",
22793                    b"AGGREGATION",
22794                    b"100",
22795                    b"sum",
22796                    b"100",
22797                ],
22798                numkeys,
22799            ),
22800            (
22801                &[
22802                    b"TS.NRANGE",
22803                    b"2",
22804                    b"x",
22805                    b"y",
22806                    b"-",
22807                    b"+",
22808                    b"AGGREGATION",
22809                    b"avg",
22810                    b"sum",
22811                    b"sum",
22812                    b"100",
22813                ],
22814                numkeys,
22815            ),
22816            (
22817                &[
22818                    b"TS.NRANGE",
22819                    b"2",
22820                    b"x",
22821                    b"y",
22822                    b"-",
22823                    b"+",
22824                    b"AGGREGATION",
22825                    b"avg",
22826                    b"sum",
22827                    b"abc",
22828                ],
22829                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
22830            ),
22831            (
22832                &[
22833                    b"TS.NRANGE",
22834                    b"2",
22835                    b"x",
22836                    b"y",
22837                    b"-",
22838                    b"+",
22839                    b"AGGREGATION",
22840                    b"avg",
22841                    b"sum",
22842                    b"0",
22843                ],
22844                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
22845            ),
22846            // With one key none of that applies and the plain parser runs, so a
22847            // lone width is a missing width rather than a count mismatch.
22848            (
22849                &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
22850                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
22851            ),
22852            (
22853                &[
22854                    b"TS.NRANGE",
22855                    b"1",
22856                    b"x",
22857                    b"-",
22858                    b"+",
22859                    b"AGGREGATION",
22860                    b"100",
22861                    b"200",
22862                ],
22863                "-ERR TSDB: Unknown aggregation type\r\n",
22864            ),
22865            // The keys come last and in the order they were named.
22866            (
22867                &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
22868                "-ERR TSDB: the key does not exist\r\n",
22869            ),
22870            (
22871                &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
22872                "-ERR WRONGTYPE Operation against a key \
22873                 holding the wrong kind of value\r\n",
22874            ),
22875        ];
22876        for (argv, want) in cases {
22877            let got = f.run(argv);
22878            assert_eq!(&got, want, "{argv:?}");
22879        }
22880    }
22881
22882    /// `TS.READ`, which is a key, one timestamp and everything from there on.
22883    #[test]
22884    fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
22885        let mut f = joined();
22886        assert_eq!(
22887            f.run(&[b"TS.READ", b"x", b"-"]),
22888            "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
22889             *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
22890        );
22891        // A plus is the last sample on its own, and a timestamp between two
22892        // samples starts at the one behind it.
22893        assert_eq!(
22894            f.run(&[b"TS.READ", b"x", b"+"]),
22895            "*1\r\n*2\r\n:50\r\n+5\r\n"
22896        );
22897        assert_eq!(
22898            f.run(&[b"TS.READ", b"x", b"25"]),
22899            "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
22900        );
22901        // Past the end, a series holding nothing and a key that is not there
22902        // are all the empty array rather than an error.
22903        assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
22904        assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
22905        assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
22906        assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
22907        // The timestamp refusal goes out with nothing in front of it, and a key
22908        // holding something else answers the bare WRONGTYPE rather than the
22909        // module's prefixed one, both unlike the rest of the family.
22910        assert_eq!(
22911            f.run(&[b"TS.READ", b"x", b"abc"]),
22912            "-TSDB: invalid timestamp\r\n"
22913        );
22914        assert_eq!(
22915            f.run(&[b"TS.READ", b"x", b"-1"]),
22916            "-TSDB: invalid timestamp\r\n"
22917        );
22918        f.run(&[b"SET", b"str", b"hi"]);
22919        assert_eq!(
22920            f.run(&[b"TS.READ", b"str", b"-"]),
22921            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
22922        );
22923        // Anything other than exactly three words is an arity error, so there
22924        // is nowhere to put an option even though the table says minus three.
22925        assert_eq!(
22926            f.run(&[b"TS.READ", b"x"]),
22927            "-ERR wrong number of arguments for 'ts.read' command\r\n"
22928        );
22929        assert_eq!(
22930            f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
22931            "-ERR wrong number of arguments for 'ts.read' command\r\n"
22932        );
22933    }
22934
22935    /// The keys of a joined read sit behind a count, so `COMMAND GETKEYS` has
22936    /// to read the count to find them.
22937    #[test]
22938    fn getkeys_reads_the_count_of_a_joined_read() {
22939        let mut f = Fixture::new();
22940        assert_eq!(
22941            f.run(&[
22942                b"COMMAND",
22943                b"GETKEYS",
22944                b"TS.NRANGE",
22945                b"2",
22946                b"a",
22947                b"b",
22948                b"-",
22949                b"+"
22950            ]),
22951            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
22952        );
22953        assert_eq!(
22954            f.run(&[
22955                b"COMMAND",
22956                b"GETKEYS",
22957                b"TS.NREVRANGE",
22958                b"1",
22959                b"a",
22960                b"-",
22961                b"+"
22962            ]),
22963            "*1\r\n$1\r\na\r\n"
22964        );
22965        // A count of zero, or one too large for the words that follow it, is
22966        // the server's own refusal and not the module's.
22967        for n in [b"0".as_slice(), b"9", b"abc"] {
22968            assert_eq!(
22969                f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
22970                "-ERR Invalid arguments specified for command\r\n"
22971            );
22972        }
22973    }
22974
22975    /// The five series every test of the label surface works against.
22976    fn labelled() -> Fixture {
22977        let mut f = Fixture::new();
22978        f.run(&[
22979            b"TS.CREATE",
22980            b"a",
22981            b"LABELS",
22982            b"room",
22983            b"kitchen",
22984            b"x",
22985            b"1",
22986        ]);
22987        f.run(&[
22988            b"TS.CREATE",
22989            b"b",
22990            b"LABELS",
22991            b"room",
22992            b"bedroom",
22993            b"x",
22994            b"2",
22995        ]);
22996        f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
22997        f.run(&[b"TS.CREATE", b"d"]);
22998        f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
22999        f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
23000        f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
23001        f
23002    }
23003
23004    /// The filter grammar, which is four steps and a `strtok` rather than a
23005    /// grammar, and which every command that searches on labels shares.
23006    #[test]
23007    fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
23008        let mut f = labelled();
23009        let cases: &[(&[&[u8]], &str)] = &[
23010            // The plain forms, and the order the answer comes back in, which is
23011            // by key name and not by anything the series remembers.
23012            (
23013                &[b"TS.QUERYINDEX", b"room=kitchen"],
23014                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
23015            ),
23016            (
23017                &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
23018                "*1\r\n$1\r\na\r\n",
23019            ),
23020            (
23021                &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
23022                "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
23023            ),
23024            // An empty list still counts as something that says which series to
23025            // take, it just never takes any.
23026            (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
23027            // Absent and present, neither of which stands on its own.
23028            (
23029                &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
23030                "*1\r\n$1\r\nc\r\n",
23031            ),
23032            (
23033                &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
23034                "*1\r\n$1\r\na\r\n",
23035            ),
23036            (
23037                &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
23038                "-ERR TSDB: please provide at least one matcher\r\n",
23039            ),
23040            // A run of separators is one separator and everything past the
23041            // second field is dropped, so all three of these ask one question.
23042            (
23043                &[b"TS.QUERYINDEX", b"room==kitchen"],
23044                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
23045            ),
23046            (
23047                &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
23048                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
23049            ),
23050            (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
23051            // A bracket is only a list when it sits straight behind the
23052            // separator, and then the label in front of it has to be there.
23053            (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
23054            (
23055                &[b"TS.QUERYINDEX", b"=(1)"],
23056                "-ERR TSDB: failed parsing labels\r\n",
23057            ),
23058            (
23059                &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
23060                "-ERR TSDB: failed parsing labels\r\n",
23061            ),
23062            (
23063                &[b"TS.QUERYINDEX", b"room=(kitchen"],
23064                "-ERR TSDB: failed parsing labels\r\n",
23065            ),
23066            (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
23067            (
23068                &[b"TS.QUERYINDEX", b"nonsense"],
23069                "-ERR TSDB: failed parsing labels\r\n",
23070            ),
23071            // Nothing here says which series to take.
23072            (
23073                &[b"TS.QUERYINDEX", b"room!=kitchen"],
23074                "-ERR TSDB: please provide at least one matcher\r\n",
23075            ),
23076            // Names and values are both compared byte for byte.
23077            (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
23078            (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
23079            (
23080                &[b"TS.QUERYINDEX"],
23081                "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
23082            ),
23083        ];
23084        for (argv, want) in cases {
23085            let got = f.run(argv);
23086            assert_eq!(&got, want, "{:?}", argv.last());
23087        }
23088    }
23089
23090    /// `TS.QUERYLABELS`, whose filter is the one that is allowed to be missing.
23091    #[test]
23092    fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
23093        let mut f = labelled();
23094        let cases: &[(&[&[u8]], &str)] = &[
23095            (
23096                &[b"TS.QUERYLABELS", b"LABELS"],
23097                "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
23098            ),
23099            (
23100                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
23101                "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
23102            ),
23103            (
23104                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
23105                "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
23106            ),
23107            // The series wearing `r` twice contributes the smaller of the two
23108            // here, which is not the one it was written down as first.
23109            (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
23110            (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
23111            (
23112                &[b"TS.QUERYLABELS", b"VALUES"],
23113                "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
23114            ),
23115            (
23116                &[b"TS.QUERYLABELS", b"ZZZ"],
23117                "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
23118            ),
23119            (
23120                &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
23121                "-ERR TSDB: unknown argument, expected FILTER\r\n",
23122            ),
23123            (
23124                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
23125                "-ERR TSDB: FILTER given with no filter expressions\r\n",
23126            ),
23127            // With no filter at all every series is taken, which is why the
23128            // first case here answers about `r` as well. A filter that is there
23129            // still has to say which series to take.
23130            (
23131                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
23132                "-ERR TSDB: please provide at least one matcher\r\n",
23133            ),
23134            (
23135                &[
23136                    b"TS.QUERYLABELS",
23137                    b"LABELS",
23138                    b"FILTER",
23139                    b"room=kitchen",
23140                    b"x=",
23141                ],
23142                "*1\r\n$4\r\nroom\r\n",
23143            ),
23144        ];
23145        for (argv, want) in cases {
23146            let got = f.run(argv);
23147            assert_eq!(&got, want, "{:?}", argv.last());
23148        }
23149    }
23150
23151    /// `TS.MGET`, the newest sample of every series a filter takes, and the two
23152    /// ways of asking for the labels back alongside it.
23153    #[test]
23154    fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
23155        let mut f = labelled();
23156        let cases: &[(&[&[u8]], &str)] = &[
23157            // A series with no samples writes an empty array where the sample
23158            // goes rather than dropping out of the reply.
23159            (
23160                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
23161                "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
23162                 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
23163            ),
23164            (
23165                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
23166                "*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\
23167                 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
23168                 *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",
23169            ),
23170            // A selected label the series does not wear is a nil, not a gap.
23171            (
23172                &[
23173                    b"TS.MGET",
23174                    b"SELECTED_LABELS",
23175                    b"x",
23176                    b"FILTER",
23177                    b"room=kitchen",
23178                ],
23179                "*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\
23180                 *2\r\n:100\r\n+1.5\r\n\
23181                 *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",
23182            ),
23183            // The other half of the duplicated name rule. This one takes the
23184            // first written down where `TS.QUERYLABELS` takes the smallest.
23185            (
23186                &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
23187                "*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",
23188            ),
23189            (
23190                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
23191                "*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\
23192                 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
23193            ),
23194            // A word that is not an option is ignored, but a missing `FILTER`
23195            // is an arity error whatever else was written.
23196            (
23197                &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
23198                "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
23199            ),
23200            (
23201                &[b"TS.MGET", b"a", b"b", b"c"],
23202                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
23203            ),
23204            (
23205                &[b"TS.MGET", b"FILTER"],
23206                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
23207            ),
23208            // Both keyword checks happen before the filter is read, and the two
23209            // sentences spell the second keyword without its `ED`.
23210            (
23211                &[
23212                    b"TS.MGET",
23213                    b"WITHLABELS",
23214                    b"SELECTED_LABELS",
23215                    b"x",
23216                    b"FILTER",
23217                    b"bad",
23218                ],
23219                "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
23220            ),
23221            (
23222                &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
23223                "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
23224            ),
23225        ];
23226        for (argv, want) in cases {
23227            let got = f.run(argv);
23228            assert_eq!(&got, want, "{:?}", argv.last());
23229        }
23230    }
23231
23232    /// What RESP3 changes across the label surface, which is a set where there
23233    /// was an array and a map where there was a pair of them.
23234    #[test]
23235    fn resp3_writes_the_label_surface_as_sets_and_maps() {
23236        let mut f = labelled();
23237        f.out = Out::new(Proto::Resp3);
23238        let cases: &[(&[&[u8]], &str)] = &[
23239            (
23240                &[b"TS.QUERYINDEX", b"room=kitchen"],
23241                "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
23242            ),
23243            (
23244                &[b"TS.QUERYLABELS", b"LABELS"],
23245                "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
23246            ),
23247            (
23248                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
23249                "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
23250            ),
23251            // The key stops being the first of three and becomes the map key,
23252            // and the labels stop being pairs and become a map of their own.
23253            (
23254                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
23255                "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
23256                 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
23257            ),
23258            (
23259                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
23260                "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
23261                 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
23262                 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
23263            ),
23264            (
23265                &[
23266                    b"TS.MGET",
23267                    b"SELECTED_LABELS",
23268                    b"x",
23269                    b"FILTER",
23270                    b"room=kitchen",
23271                ],
23272                "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
23273                 *2\r\n:100\r\n,1.5\r\n\
23274                 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
23275            ),
23276            // A map with a name in it twice, which is what a series wearing one
23277            // label name twice turns into.
23278            (
23279                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
23280                "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
23281                 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
23282            ),
23283        ];
23284        for (argv, want) in cases {
23285            let got = f.run(argv);
23286            assert_eq!(&got, want, "{:?}", argv.last());
23287        }
23288    }
23289
23290    /// The same five series with enough samples in them for a group to have
23291    /// something to fold.
23292    fn spanned() -> Fixture {
23293        let mut f = labelled();
23294        f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
23295        f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
23296        f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
23297        f
23298    }
23299
23300    /// A span read out of every series a filter takes, with and without a group
23301    /// over the top of it.
23302    #[test]
23303    fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
23304        let mut f = spanned();
23305        let cases: &[(&[&[u8]], &str)] = &[
23306            (
23307                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
23308                "*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\
23309                 *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",
23310            ),
23311            // Newest first is applied to each series before anything else sees
23312            // the rows.
23313            (
23314                &[
23315                    b"TS.MREVRANGE",
23316                    b"-",
23317                    b"+",
23318                    b"WITHLABELS",
23319                    b"FILTER",
23320                    b"room=kitchen",
23321                ],
23322                "*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\
23323                 *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\
23324                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
23325                 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
23326            ),
23327            // A label a series does not wear comes back against a nil rather
23328            // than being left out.
23329            (
23330                &[
23331                    b"TS.MRANGE",
23332                    b"-",
23333                    b"+",
23334                    b"SELECTED_LABELS",
23335                    b"x",
23336                    b"FILTER",
23337                    b"room=kitchen",
23338                ],
23339                "*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\
23340                 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
23341                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
23342                 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
23343            ),
23344            // The fold: 100 is in both series and adds up, the other two are in
23345            // one each and are still rows.
23346            (
23347                &[
23348                    b"TS.MRANGE",
23349                    b"-",
23350                    b"+",
23351                    b"FILTER",
23352                    b"room=kitchen",
23353                    b"GROUPBY",
23354                    b"room",
23355                    b"REDUCE",
23356                    b"sum",
23357                ],
23358                "*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\
23359                 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
23360            ),
23361            // RESP2 has nowhere to put the reducer and the member keys, so a
23362            // group wearing labels writes them as two more labels.
23363            (
23364                &[
23365                    b"TS.MRANGE",
23366                    b"-",
23367                    b"+",
23368                    b"WITHLABELS",
23369                    b"FILTER",
23370                    b"room=kitchen",
23371                    b"GROUPBY",
23372                    b"room",
23373                    b"REDUCE",
23374                    b"max",
23375                ],
23376                "*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\
23377                 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
23378                 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
23379                 *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",
23380            ),
23381            // A count is applied to each member and then again to the fold.
23382            (
23383                &[
23384                    b"TS.MREVRANGE",
23385                    b"-",
23386                    b"+",
23387                    b"COUNT",
23388                    b"1",
23389                    b"FILTER",
23390                    b"room=kitchen",
23391                    b"GROUPBY",
23392                    b"room",
23393                    b"REDUCE",
23394                    b"count",
23395                ],
23396                "*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",
23397            ),
23398            // Nothing wears the label, so nothing is in any group.
23399            (
23400                &[
23401                    b"TS.MRANGE",
23402                    b"-",
23403                    b"+",
23404                    b"FILTER",
23405                    b"room=kitchen",
23406                    b"GROUPBY",
23407                    b"nope",
23408                    b"REDUCE",
23409                    b"sum",
23410                ],
23411                "*0\r\n",
23412            ),
23413            (
23414                &[
23415                    b"TS.MRANGE",
23416                    b"-",
23417                    b"+",
23418                    b"AGGREGATION",
23419                    b"sum,avg",
23420                    b"100",
23421                    b"FILTER",
23422                    b"room=bedroom",
23423                ],
23424                "*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",
23425            ),
23426            // The errors, in the order they are looked for.
23427            (
23428                &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
23429                "-ERR TSDB: missing FILTER argument\r\n",
23430            ),
23431            (
23432                &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
23433                "-ERR TSDB: missing labels for filter argument\r\n",
23434            ),
23435            (
23436                &[
23437                    b"TS.MRANGE",
23438                    b"-",
23439                    b"+",
23440                    b"GROUPBY",
23441                    b"room",
23442                    b"REDUCE",
23443                    b"sum",
23444                    b"FILTER",
23445                    b"room=kitchen",
23446                ],
23447                "-ERR TSDB: GROUPBY should always come after filter\r\n",
23448            ),
23449            // The group is four words from the end here, so the length is what
23450            // is wrong with it.
23451            (
23452                &[
23453                    b"TS.MRANGE",
23454                    b"-",
23455                    b"+",
23456                    b"FILTER",
23457                    b"room=kitchen",
23458                    b"GROUPBY",
23459                    b"room",
23460                    b"REDUCE",
23461                    b"sum",
23462                    b"x",
23463                ],
23464                "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
23465            ),
23466            // And here it is not, so its words are filters and answer first.
23467            (
23468                &[
23469                    b"TS.MRANGE",
23470                    b"-",
23471                    b"+",
23472                    b"FILTER",
23473                    b"nope",
23474                    b"GROUPBY",
23475                    b"room",
23476                    b"REDUCE",
23477                    b"sum",
23478                    b"x",
23479                ],
23480                "-ERR TSDB: failed parsing labels\r\n",
23481            ),
23482            (
23483                &[
23484                    b"TS.MRANGE",
23485                    b"-",
23486                    b"+",
23487                    b"FILTER",
23488                    b"room=kitchen",
23489                    b"GROUPBY",
23490                    b"room",
23491                    b"REDUCE",
23492                    b"twa",
23493                ],
23494                "-ERR TSDB: Invalid reducer type\r\n",
23495            ),
23496            (
23497                &[
23498                    b"TS.MRANGE",
23499                    b"-",
23500                    b"+",
23501                    b"AGGREGATION",
23502                    b"sum,avg",
23503                    b"100",
23504                    b"FILTER",
23505                    b"room=kitchen",
23506                    b"GROUPBY",
23507                    b"room",
23508                    b"REDUCE",
23509                    b"sum",
23510                ],
23511                "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
23512            ),
23513            // The label list ends at a keyword, so this is a `COUNT` with a
23514            // `FILTER` where its number should be.
23515            (
23516                &[
23517                    b"TS.MRANGE",
23518                    b"-",
23519                    b"+",
23520                    b"SELECTED_LABELS",
23521                    b"COUNT",
23522                    b"FILTER",
23523                    b"room=kitchen",
23524                ],
23525                "-ERR TSDB: Couldn't parse COUNT\r\n",
23526            ),
23527        ];
23528        for (argv, want) in cases {
23529            let got = f.run(argv);
23530            assert_eq!(&got, want, "{argv:?}");
23531        }
23532    }
23533
23534    /// The multi key reads on RESP3, where the key becomes a map key and the
23535    /// reducer and the member keys become fields of their own.
23536    #[test]
23537    fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
23538        let mut f = spanned();
23539        f.out = Out::new(Proto::Resp3);
23540        let cases: &[(&[&[u8]], &str)] = &[
23541            (
23542                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
23543                "%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\
23544                 *1\r\n*2\r\n:200\r\n,2\r\n",
23545            ),
23546            // The reductions a read asked for, which RESP2 has no room for at
23547            // all and which is empty on a read that asked for none.
23548            (
23549                &[
23550                    b"TS.MRANGE",
23551                    b"-",
23552                    b"+",
23553                    b"AGGREGATION",
23554                    b"sum,avg",
23555                    b"100",
23556                    b"FILTER",
23557                    b"room=bedroom",
23558                ],
23559                "%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\
23560                 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
23561            ),
23562            (
23563                &[
23564                    b"TS.MRANGE",
23565                    b"-",
23566                    b"+",
23567                    b"FILTER",
23568                    b"room=kitchen",
23569                    b"GROUPBY",
23570                    b"room",
23571                    b"REDUCE",
23572                    b"sum",
23573                ],
23574                "%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\
23575                 $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\
23576                 *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",
23577            ),
23578            // The labels hold only the pair the group was made on, because the
23579            // reducer and the sources have somewhere else to go.
23580            (
23581                &[
23582                    b"TS.MRANGE",
23583                    b"-",
23584                    b"+",
23585                    b"WITHLABELS",
23586                    b"FILTER",
23587                    b"room=kitchen",
23588                    b"GROUPBY",
23589                    b"room",
23590                    b"REDUCE",
23591                    b"max",
23592                ],
23593                "%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\
23594                 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
23595                 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
23596                 *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",
23597            ),
23598            (
23599                &[
23600                    b"TS.MRANGE",
23601                    b"-",
23602                    b"+",
23603                    b"FILTER",
23604                    b"room=kitchen",
23605                    b"GROUPBY",
23606                    b"nope",
23607                    b"REDUCE",
23608                    b"sum",
23609                ],
23610                "%0\r\n",
23611            ),
23612        ];
23613        for (argv, want) in cases {
23614            let got = f.run(argv);
23615            assert_eq!(&got, want, "{argv:?}");
23616        }
23617    }
23618
23619    /// `TS.CREATERULE`, whose refusals come in an order of their own.
23620    #[test]
23621    fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
23622        let mut f = Fixture::new();
23623        f.run(&[b"TS.CREATE", b"src"]);
23624        f.run(&[b"TS.CREATE", b"dst"]);
23625        f.run(&[b"SET", b"plain", b"v"]);
23626        let cases: &[(&[&[u8]], &str)] = &[
23627            // The width is read before the reduction, the reduction before the
23628            // width being above zero, and all three before either key is looked
23629            // at, so a command that is wrong twice complains about the first.
23630            (
23631                &[
23632                    b"TS.CREATERULE",
23633                    b"src",
23634                    b"dst",
23635                    b"AGGREGATION",
23636                    b"nope",
23637                    b"x",
23638                ],
23639                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
23640            ),
23641            (
23642                &[
23643                    b"TS.CREATERULE",
23644                    b"src",
23645                    b"dst",
23646                    b"AGGREGATION",
23647                    b"nope",
23648                    b"10",
23649                ],
23650                "-ERR TSDB: Unknown aggregation type\r\n",
23651            ),
23652            (
23653                &[
23654                    b"TS.CREATERULE",
23655                    b"src",
23656                    b"dst",
23657                    b"AGGREGATION",
23658                    b"avg",
23659                    b"0",
23660                ],
23661                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
23662            ),
23663            (
23664                &[
23665                    b"TS.CREATERULE",
23666                    b"src",
23667                    b"dst",
23668                    b"AGGREGATION",
23669                    b"avg",
23670                    b"10",
23671                    b"x",
23672                ],
23673                "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
23674            ),
23675            (
23676                &[
23677                    b"TS.CREATERULE",
23678                    b"src",
23679                    b"src",
23680                    b"AGGREGATION",
23681                    b"avg",
23682                    b"10",
23683                ],
23684                "-ERR TSDB: the source key and destination key should be different\r\n",
23685            ),
23686            // A key holding something else answers the same as a key that is not
23687            // there at all, because the source is looked up first and neither of
23688            // them is a series.
23689            (
23690                &[
23691                    b"TS.CREATERULE",
23692                    b"nope",
23693                    b"plain",
23694                    b"AGGREGATION",
23695                    b"avg",
23696                    b"10",
23697                ],
23698                "-ERR TSDB: the key does not exist\r\n",
23699            ),
23700            (
23701                &[
23702                    b"TS.CREATERULE",
23703                    b"src",
23704                    b"nope",
23705                    b"AGGREGATION",
23706                    b"avg",
23707                    b"10",
23708                ],
23709                "-ERR TSDB: the key does not exist\r\n",
23710            ),
23711            // A keyword other than AGGREGATION is an arity error rather than a
23712            // syntax one, because the arity is all that is checked.
23713            (
23714                &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
23715                "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
23716            ),
23717            (
23718                &[
23719                    b"TS.CREATERULE",
23720                    b"src",
23721                    b"dst",
23722                    b"AGGREGATION",
23723                    b"avg",
23724                    b"10",
23725                ],
23726                "+OK\r\n",
23727            ),
23728            // The link is now in place, so the same rule again is refused from
23729            // the destination's end.
23730            (
23731                &[
23732                    b"TS.CREATERULE",
23733                    b"src",
23734                    b"dst",
23735                    b"AGGREGATION",
23736                    b"avg",
23737                    b"10",
23738                ],
23739                "-ERR TSDB: the destination key already has a src rule\r\n",
23740            ),
23741            // A source that is already someone's destination, and a destination
23742            // that is already someone's source, are two different sentences.
23743            (
23744                &[
23745                    b"TS.CREATERULE",
23746                    b"dst",
23747                    b"src",
23748                    b"AGGREGATION",
23749                    b"avg",
23750                    b"10",
23751                ],
23752                "-ERR TSDB: the source key already has a source rule\r\n",
23753            ),
23754            (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
23755            (
23756                &[b"TS.DELETERULE", b"src", b"dst"],
23757                "-ERR TSDB: compaction rule does not exist\r\n",
23758            ),
23759            // The source is looked up and the destination is not, so a missing
23760            // destination is a missing rule and a missing source is a missing
23761            // key, which is the other way round from `TS.CREATERULE`.
23762            (
23763                &[b"TS.DELETERULE", b"src", b"nope"],
23764                "-ERR TSDB: compaction rule does not exist\r\n",
23765            ),
23766            (
23767                &[b"TS.DELETERULE", b"nope", b"dst"],
23768                "-ERR TSDB: the key does not exist\r\n",
23769            ),
23770        ];
23771        for (argv, want) in cases {
23772            let got = f.run(argv);
23773            assert_eq!(&got, want, "{argv:?}");
23774        }
23775    }
23776
23777    /// What a rule writes, which is every bucket but the one it is filling.
23778    #[test]
23779    fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
23780        let mut f = Fixture::new();
23781        f.run(&[b"TS.CREATE", b"src"]);
23782        f.run(&[b"TS.CREATE", b"dst"]);
23783        // The readings written before the rule was made are not folded, so the
23784        // destination is still empty after the first two.
23785        f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
23786        f.run(&[
23787            b"TS.CREATERULE",
23788            b"src",
23789            b"dst",
23790            b"AGGREGATION",
23791            b"sum",
23792            b"100",
23793        ]);
23794        f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
23795        assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
23796        // The bucket the rule is filling holds only what it was given, so it is
23797        // 2 rather than 3, and it is written when a reading lands past it.
23798        assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
23799        f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
23800        assert_eq!(
23801            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
23802            "*1\r\n*2\r\n:0\r\n+2\r\n"
23803        );
23804        // A reading into a bucket that has already been written works that
23805        // bucket out again over everything the source now holds.
23806        f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
23807        assert_eq!(
23808            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
23809            "*1\r\n*2\r\n:0\r\n+11\r\n"
23810        );
23811        // Deleting from the source works the buckets it touched out again and
23812        // reopens the newest one, so `LATEST` starts from the whole bucket.
23813        assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
23814        assert_eq!(
23815            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
23816            "*1\r\n*2\r\n:0\r\n+8\r\n"
23817        );
23818        assert_eq!(
23819            f.run(&[b"TS.GET", b"dst", b"LATEST"]),
23820            "*2\r\n:100\r\n+4\r\n"
23821        );
23822        // The link shows on both ends, and dropping either key takes it down.
23823        assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
23824        f.run(&[b"DEL", b"dst"]);
23825        assert_eq!(
23826            f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
23827            "-ERR TSDB: compaction rule does not exist\r\n"
23828        );
23829    }
23830
23831    /// The three shapes an `XADD` id can take, and the one rule behind all of
23832    /// them.
23833    #[test]
23834    fn xadd_ids_only_ever_go_up() {
23835        let mut f = Fixture::new();
23836        // A bare millisecond is that millisecond and sequence zero.
23837        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
23838        // And `5-*` is the next free sequence inside it.
23839        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
23840        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
23841        assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
23842        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
23843
23844        assert!(
23845            f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
23846                .contains("equal or smaller")
23847        );
23848        assert!(
23849            f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
23850                .contains("must be greater than 0-0")
23851        );
23852        assert!(
23853            f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
23854                .contains("Invalid stream ID")
23855        );
23856        // The pairs have to be pairs, and Redis calls an odd one an arity error
23857        // rather than a syntax error even though the table has already passed.
23858        assert!(
23859            f.run(&[b"XADD", b"s", b"*", b"a"])
23860                .contains("wrong number of arguments")
23861        );
23862
23863        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
23864        // producer can tell nobody is consuming this yet from the write landed.
23865        assert_eq!(
23866            f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
23867            "$-1\r\n"
23868        );
23869        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
23870        assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
23871        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
23872    }
23873
23874    /// The trim options, which are three keywords that disagree about how many
23875    /// arguments they take.
23876    #[test]
23877    fn trimming_reads_its_options_the_way_redis_does() {
23878        let mut f = Fixture::new();
23879        for i in 1..=10u32 {
23880            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
23881        }
23882        assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
23883        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
23884        assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
23885        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
23886
23887        // One argument after the keyword and the `~` is read as the threshold,
23888        // which is what a real server does and is the reason this is a number
23889        // complaint and not a syntax one.
23890        assert!(
23891            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
23892                .contains("not an integer")
23893        );
23894        assert!(
23895            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
23896                .contains("MAXLEN argument must be >= 0")
23897        );
23898        // The strategy check runs before the approximation check, so a LIMIT
23899        // with neither is told about the missing strategy.
23900        assert!(
23901            f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
23902                .contains("without specifying a trimming strategy")
23903        );
23904        assert!(
23905            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
23906                .contains("without the special ~ option")
23907        );
23908        assert!(
23909            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
23910                .contains("at the same time are not compatible")
23911        );
23912        // NOMKSTREAM is XADD's and XTRIM does not take it.
23913        assert!(
23914            f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
23915                .contains("syntax error")
23916        );
23917        assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
23918    }
23919
23920    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
23921    #[test]
23922    fn xrange_looks_the_key_up_before_it_reads_the_count() {
23923        let mut f = Fixture::new();
23924        f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
23925        f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
23926
23927        assert_eq!(
23928            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
23929            "*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\
23930             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
23931        );
23932        assert_eq!(
23933            f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
23934            "*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"
23935        );
23936        // The exclusive bound is stepped after the missing sequence is filled
23937        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
23938        // `6-1` is still in the range.
23939        assert_eq!(
23940            f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
23941            "*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\
23942             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
23943        );
23944        assert_eq!(
23945            f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
23946            "*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"
23947        );
23948        assert!(
23949            f.run(&[b"XRANGE", b"s", b"(-", b"+"])
23950                .contains("Invalid stream ID")
23951        );
23952
23953        // The two kinds of nothing. A key that is not there is an empty array
23954        // and a key that is there with a count of zero is a null array, because
23955        // the lookup happens first.
23956        assert_eq!(
23957            f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
23958            "*0\r\n"
23959        );
23960        assert_eq!(
23961            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
23962            "*-1\r\n"
23963        );
23964        f.run(&[b"SET", b"str", b"v"]);
23965        assert!(
23966            f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
23967                .starts_with("-WRONGTYPE")
23968        );
23969        // The count is read in a loop, so the last one wins.
23970        assert_eq!(
23971            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
23972            "*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"
23973        );
23974    }
23975
23976    /// `XDEL` and `XACK` check every id before they touch any of them.
23977    #[test]
23978    fn a_bad_id_late_in_the_list_stops_the_whole_command() {
23979        let mut f = Fixture::new();
23980        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
23981        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
23982        assert!(
23983            f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
23984                .contains("Invalid stream ID")
23985        );
23986        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
23987        assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
23988        assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
23989        assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
23990        assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
23991    }
23992
23993    /// `XGROUP`, and the two different complaints it makes about arguments.
23994    #[test]
23995    fn xgroup_has_an_arity_per_subcommand() {
23996        let mut f = Fixture::new();
23997        assert!(
23998            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
23999                .contains("requires the key")
24000        );
24001        assert_eq!(
24002            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
24003            "+OK\r\n"
24004        );
24005        // A second CREATE is BUSYGROUP and not an ordinary error, because a
24006        // client racing another one to make a group branches on the prefix.
24007        assert!(
24008            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
24009                .starts_with("-BUSYGROUP")
24010        );
24011        assert_eq!(
24012            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
24013            ":1\r\n"
24014        );
24015        assert_eq!(
24016            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
24017            ":0\r\n"
24018        );
24019        assert_eq!(
24020            f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
24021            ":0\r\n"
24022        );
24023
24024        // Below the subcommand's own arity is an arity error naming the pair.
24025        let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
24026        assert!(
24027            short.contains("wrong number of arguments for 'xgroup|destroy' command"),
24028            "{short}"
24029        );
24030        // At or above it in a shape the handler will not take is the other one.
24031        let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
24032        assert!(
24033            odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
24034            "{odd}"
24035        );
24036        assert!(
24037            f.run(&[b"XGROUP", b"NOSUCH", b"s"])
24038                .contains("Try XGROUP HELP")
24039        );
24040
24041        assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
24042        assert!(
24043            f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
24044                .starts_with("-NOGROUP")
24045        );
24046        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
24047        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
24048        assert!(
24049            f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
24050                .contains("requires the key")
24051        );
24052    }
24053
24054    /// A group read, an acknowledgement, and what is left in between.
24055    #[test]
24056    fn xreadgroup_hands_out_and_xack_takes_back() {
24057        let mut f = Fixture::new();
24058        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24059        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
24060        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24061
24062        let first = f.run(&[
24063            b"XREADGROUP",
24064            b"GROUP",
24065            b"g",
24066            b"c1",
24067            b"COUNT",
24068            b"1",
24069            b"STREAMS",
24070            b"s",
24071            b">",
24072        ]);
24073        assert_eq!(
24074            first,
24075            "*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"
24076        );
24077        // A history read names its stream even with nothing to show, which is
24078        // the difference between it and a `>` read that found nothing.
24079        assert_eq!(
24080            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
24081            "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
24082        );
24083        assert_eq!(
24084            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
24085            "*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"
24086        );
24087
24088        assert_eq!(
24089            f.run(&[b"XPENDING", b"s", b"g"]),
24090            "*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"
24091        );
24092        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
24093        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
24094        // Empty is four nulls and not a zero with three empty things.
24095        assert_eq!(
24096            f.run(&[b"XPENDING", b"s", b"g"]),
24097            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
24098        );
24099
24100        // A history read of an entry that has since been deleted is the id with
24101        // a null beside it, so the consumer can still acknowledge it.
24102        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
24103        f.run(&[b"XDEL", b"s", b"2-1"]);
24104        assert_eq!(
24105            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
24106            "*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"
24107        );
24108
24109        // The group lookup runs before the id parse, so a `+` at a stream with
24110        // no such group is told about the group and not about the id.
24111        assert!(
24112            f.run(&[
24113                b"XREADGROUP",
24114                b"GROUP",
24115                b"nope",
24116                b"c",
24117                b"STREAMS",
24118                b"s",
24119                b"+"
24120            ])
24121            .starts_with("-NOGROUP")
24122        );
24123        assert!(
24124            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
24125                .contains("meaningless in the context of XREADGROUP")
24126        );
24127        assert!(
24128            f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
24129                .contains("only supported by XREADGROUP")
24130        );
24131        assert!(
24132            f.run(&[
24133                b"XREADGROUP",
24134                b"GROUP",
24135                b"g",
24136                b"c",
24137                b"STREAMS",
24138                b"s",
24139                b"a",
24140                b"b"
24141            ])
24142            .contains("Unbalanced 'xreadgroup' list of streams")
24143        );
24144    }
24145
24146    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
24147    /// answer.
24148    #[test]
24149    fn xread_with_no_block_writes_the_null_itself() {
24150        let mut f = Fixture::new();
24151        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24152        assert_eq!(
24153            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
24154            "*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"
24155        );
24156        // Nothing new is a null array and not an empty one, and a stream with
24157        // nothing new is left out rather than sent with an empty list.
24158        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
24159        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
24160        f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
24161        assert_eq!(
24162            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
24163            "*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"
24164        );
24165        // `$` is the last id, so nothing that is already there comes back.
24166        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
24167        // And `+` is the last entry, whatever COUNT says.
24168        assert_eq!(
24169            f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
24170            "*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"
24171        );
24172        // A count of zero means unlimited here, which is the opposite of what it
24173        // means to XRANGE.
24174        assert_eq!(
24175            f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
24176            "*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"
24177        );
24178        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
24179        assert!(
24180            f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
24181                .contains("not an integer")
24182        );
24183        assert!(
24184            f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
24185                .contains("timeout is negative")
24186        );
24187        assert!(
24188            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
24189                .contains("Unbalanced 'xread' list of streams")
24190        );
24191    }
24192
24193    /// A blocked reader, and the two ways it stops being blocked.
24194    #[test]
24195    fn a_blocked_xread_wakes_on_the_next_entry() {
24196        let mut f = Fixture::new();
24197        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24198        let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
24199        assert_eq!(flow, Flow::Block);
24200        assert!(reply.is_empty());
24201
24202        // Everybody parked on the stream gets the entry, because a read takes
24203        // nothing away. That is the difference between this and BLPOP. Two
24204        // clients rather than one twice, since a client that is waiting is not
24205        // reading and cannot block again.
24206        f.session = Session::new(8);
24207        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
24208        assert_eq!(flow, Flow::Block);
24209        assert_eq!(f.server.parked(), 2);
24210
24211        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
24212        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";
24213        for client in [7, 8] {
24214            let mut out = Out::new(Proto::Resp2);
24215            assert!(f.server.serve_waiter(client, 0, &mut out));
24216            assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
24217        }
24218
24219        // And a deadline that runs out is a null array, the same as a plain
24220        // XREAD that found nothing.
24221        f.server.forget_waiters(7);
24222        f.server.forget_waiters(8);
24223        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
24224        assert_eq!(flow, Flow::Block);
24225        let mut out = Out::new(Proto::Resp2);
24226        assert!(!f.server.serve_waiter(8, 0, &mut out));
24227        assert!(out.as_slice().is_empty());
24228        assert!(f.server.serve_waiter(8, u64::MAX, &mut out));
24229        assert_eq!(
24230            core::str::from_utf8(out.as_slice()).expect("ascii"),
24231            "*-1\r\n"
24232        );
24233    }
24234
24235    /// A blocked group reader whose group is destroyed under it.
24236    #[test]
24237    fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
24238        let mut f = Fixture::new();
24239        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24240        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
24241        let (flow, _) = f.flow(&[
24242            b"XREADGROUP",
24243            b"GROUP",
24244            b"g",
24245            b"c",
24246            b"BLOCK",
24247            b"0",
24248            b"STREAMS",
24249            b"s",
24250            b">",
24251        ]);
24252        assert_eq!(flow, Flow::Block);
24253
24254        f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
24255        let mut out = Out::new(Proto::Resp2);
24256        assert!(f.server.serve_waiter(7, 0, &mut out));
24257        // The ordinary sentence and not a special one about having been parked,
24258        // which is what a running 8.10 sends.
24259        assert_eq!(
24260            core::str::from_utf8(out.as_slice()).expect("ascii"),
24261            "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
24262        );
24263    }
24264
24265    /// `XCLAIM`, whose argument shape is the odd one in the group.
24266    #[test]
24267    fn xclaim_reads_ids_until_one_will_not_parse() {
24268        let mut f = Fixture::new();
24269        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24270        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
24271        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24272        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
24273
24274        // Everything after the first argument that is not an id is an option, so
24275        // a `-` is an unrecognised option and not a bad id.
24276        assert!(
24277            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
24278                .contains("Unrecognized XCLAIM option '-'")
24279        );
24280        assert_eq!(
24281            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
24282            "*1\r\n$3\r\n1-1\r\n"
24283        );
24284        // An id that is pending but whose entry has gone is an empty answer, and
24285        // it leaves the pending list on the way past.
24286        f.run(&[b"XDEL", b"s", b"2-1"]);
24287        assert_eq!(
24288            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
24289            "*0\r\n"
24290        );
24291        assert!(
24292            f.run(&[b"XPENDING", b"s", b"g"])
24293                .starts_with("*4\r\n:1\r\n")
24294        );
24295        assert!(
24296            f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
24297                .starts_with("-NOGROUP")
24298        );
24299        assert!(
24300            f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
24301                .contains("Invalid min-idle-time argument for XCLAIM")
24302        );
24303    }
24304
24305    /// `XAUTOCLAIM`, and the third value nobody expects.
24306    #[test]
24307    fn xautoclaim_reports_what_it_dropped() {
24308        let mut f = Fixture::new();
24309        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24310        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
24311        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24312        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
24313        f.run(&[b"XDEL", b"s", b"1-1"]);
24314
24315        // The cursor, what was claimed, and what was dropped for no longer being
24316        // in the stream. The third one is what makes a sweep converge.
24317        assert_eq!(
24318            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
24319            "*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"
24320        );
24321        assert!(
24322            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
24323                .contains("COUNT must be > 0")
24324        );
24325        assert!(
24326            f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
24327                .starts_with("-NOGROUP")
24328        );
24329    }
24330
24331    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
24332    #[test]
24333    fn xdelex_answers_one_integer_an_id() {
24334        let mut f = Fixture::new();
24335        for i in 1..=4 {
24336            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
24337        }
24338        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24339        f.run(&[
24340            b"XREADGROUP",
24341            b"GROUP",
24342            b"g",
24343            b"c",
24344            b"COUNT",
24345            b"2",
24346            b"STREAMS",
24347            b"s",
24348            b">",
24349        ]);
24350
24351        // One means gone and minus one means it was not there to start with.
24352        assert_eq!(
24353            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
24354            "*2\r\n:1\r\n:-1\r\n"
24355        );
24356        // `KEEPREF` leaves the pending entry behind, so the group still counts
24357        // the one it was handed even though the entry has gone.
24358        assert!(
24359            f.run(&[b"XPENDING", b"s", b"g"])
24360                .starts_with("*4\r\n:2\r\n")
24361        );
24362        // `DELREF` takes it out of every pending list on the way past.
24363        assert_eq!(
24364            f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
24365            "*1\r\n:1\r\n"
24366        );
24367        // `1-1` is still in the list, because the delete before it said KEEPREF.
24368        assert_eq!(
24369            f.run(&[b"XPENDING", b"s", b"g"]),
24370            "*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"
24371        );
24372
24373        // Two means somebody still wants it, and the question is wider than the
24374        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
24375        // refused even though no consumer has ever been handed it.
24376        assert_eq!(
24377            f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
24378            "*2\r\n:2\r\n:2\r\n"
24379        );
24380
24381        // A key that is not there answers minus ones without reading the IDs.
24382        assert_eq!(
24383            f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
24384            "*2\r\n:-1\r\n:-1\r\n"
24385        );
24386        // A key that is there validates every ID before deleting any of them.
24387        assert!(
24388            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
24389                .starts_with("-ERR Invalid stream ID")
24390        );
24391        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
24392
24393        assert!(
24394            f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
24395                .contains("Number of IDs must be a positive integer")
24396        );
24397        assert!(
24398            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
24399                .contains("The `numids` parameter must match the number of arguments")
24400        );
24401        // The condition is one word, so a second one is a syntax error, and so
24402        // is one ID more than the count promised.
24403        assert!(
24404            f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
24405                .starts_with("-ERR syntax error")
24406        );
24407        assert!(
24408            f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
24409                .starts_with("-ERR syntax error")
24410        );
24411        // The key is looked up first, so the wrong type beats the syntax.
24412        f.run(&[b"SET", b"str", b"v"]);
24413        assert!(
24414            f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
24415                .starts_with("-WRONGTYPE")
24416        );
24417    }
24418
24419    /// `XACKDEL`, whose reply is about the pending list and not about the log.
24420    #[test]
24421    fn xackdel_reports_what_the_group_was_holding() {
24422        let mut f = Fixture::new();
24423        for i in 1..=3 {
24424            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
24425        }
24426        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24427        f.run(&[
24428            b"XREADGROUP",
24429            b"GROUP",
24430            b"g",
24431            b"c",
24432            b"COUNT",
24433            b"1",
24434            b"STREAMS",
24435            b"s",
24436            b">",
24437        ]);
24438
24439        // Minus one is not about the stream: `2-1` is sitting there unread and
24440        // still answers minus one, because the group was not holding it. It also
24441        // stays, since only an ID that was acknowledged is deleted.
24442        assert_eq!(
24443            f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
24444            "*2\r\n:1\r\n:-1\r\n"
24445        );
24446        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
24447
24448        // A missing group is minus one an ID and not a NOGROUP.
24449        assert_eq!(
24450            f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
24451            "*1\r\n:-1\r\n"
24452        );
24453        assert_eq!(
24454            f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
24455            "*1\r\n:-1\r\n"
24456        );
24457
24458        // The acknowledgement happens whatever the condition says, so an ACKED
24459        // that answers two has still emptied the pending list.
24460        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
24461        f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
24462        assert_eq!(
24463            f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
24464            "*1\r\n:2\r\n"
24465        );
24466        assert_eq!(
24467            f.run(&[b"XPENDING", b"s", b"g"]),
24468            "*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"
24469        );
24470        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
24471    }
24472
24473    /// `XNACK`, which hands an entry back to nobody.
24474    #[test]
24475    fn xnack_releases_an_entry_for_the_next_claim() {
24476        let mut f = Fixture::new();
24477        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24478        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
24479        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24480        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
24481        // Twice, so the delivery count is two and the words have something to
24482        // do with it.
24483        f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
24484
24485        assert_eq!(
24486            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
24487            ":1\r\n"
24488        );
24489        // No owner, no idle time, and the count left where it was. A released
24490        // entry reads as idle for longer than any min-idle-time, which is what
24491        // puts it at the front of the next claim.
24492        assert_eq!(
24493            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
24494            "*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"
24495        );
24496        // The consumer no longer holds it, so a filtered XPENDING skips it.
24497        assert_eq!(
24498            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
24499            "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
24500        );
24501        // The bookmark did not move, so a `>` read will not hand it out again.
24502        assert_eq!(
24503            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
24504            "*-1\r\n"
24505        );
24506        // A claim at any min-idle-time takes it.
24507        assert_eq!(
24508            f.run(&[
24509                b"XAUTOCLAIM",
24510                b"s",
24511                b"g",
24512                b"c2",
24513                b"99999999",
24514                b"-",
24515                b"JUSTID"
24516            ]),
24517            "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
24518        );
24519
24520        // `SILENT` takes one off the count rather than putting it back to zero,
24521        // which only shows on an entry that has been handed out more than once.
24522        // It was delivered and then claimed, so it is on two and goes to one.
24523        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
24524        assert!(
24525            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24526                .contains(":-1\r\n:1\r\n")
24527        );
24528        // And it stops at zero rather than wrapping.
24529        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
24530        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
24531        assert!(
24532            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24533                .contains(":-1\r\n:0\r\n")
24534        );
24535        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
24536        f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
24537        assert!(
24538            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24539                .contains(":9223372036854775807\r\n")
24540        );
24541        f.run(&[
24542            b"XNACK",
24543            b"s",
24544            b"g",
24545            b"FATAL",
24546            b"IDS",
24547            b"1",
24548            b"1-1",
24549            b"RETRYCOUNT",
24550            b"3",
24551        ]);
24552        assert!(
24553            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24554                .contains(":-1\r\n:3\r\n")
24555        );
24556
24557        // Releasing something the group is not holding is zero, and `FORCE`
24558        // makes the pending entry rather than answering zero. A forced entry
24559        // starts at zero, since there was no earlier count to keep.
24560        f.run(&[b"XACK", b"s", b"g", b"2-1"]);
24561        assert_eq!(
24562            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
24563            ":0\r\n"
24564        );
24565        assert_eq!(
24566            f.run(&[
24567                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
24568            ]),
24569            ":1\r\n"
24570        );
24571        assert!(
24572            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24573                .contains(":-1\r\n:0\r\n")
24574        );
24575        // `FORCE` on an ID the stream does not have is still zero.
24576        assert_eq!(
24577            f.run(&[
24578                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
24579            ]),
24580            ":0\r\n"
24581        );
24582
24583        // The group is looked up before the mode word, and it raises rather
24584        // than answering per ID the way the two delete commands do.
24585        assert_eq!(
24586            f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
24587            "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
24588        );
24589        assert!(
24590            f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
24591                .starts_with("-ERR")
24592        );
24593        // Its own sentences, which are not the ones XDELEX uses.
24594        assert!(
24595            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
24596                .contains("numids must be a positive integer")
24597        );
24598        assert!(
24599            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
24600                .contains("number of IDs doesn't match numids")
24601        );
24602        // Everything past the counted IDs is an option, so one too many is an
24603        // option nobody recognises and not a count that does not add up.
24604        assert!(
24605            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
24606                .contains("Unrecognized XNACK option '2-1'")
24607        );
24608    }
24609
24610    /// `XINFO`, which is where the shape of the storage shows through.
24611    #[test]
24612    fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
24613        let mut f = Fixture::new();
24614        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24615        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
24616        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24617        f.run(&[
24618            b"XREADGROUP",
24619            b"GROUP",
24620            b"g",
24621            b"c1",
24622            b"COUNT",
24623            b"1",
24624            b"STREAMS",
24625            b"s",
24626            b">",
24627        ]);
24628
24629        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
24630        // Ten pairs, since the six idempotency fields have nothing behind them
24631        // here and a zero would claim they had. That is D-27.
24632        assert!(info.starts_with("*20\r\n"), "{info}");
24633        assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
24634        assert!(
24635            info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
24636            "{info}"
24637        );
24638        assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
24639        assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
24640
24641        let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
24642        assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
24643        assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
24644        assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
24645        assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
24646
24647        // A consumer that has never been given anything reports minus one for
24648        // inactive rather than the moment it turned up, which is what tells a
24649        // worker that is stuck from one that has nothing to do.
24650        f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
24651        let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
24652        assert!(consumers.starts_with("*2\r\n"), "{consumers}");
24653        assert!(
24654            consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
24655            "{consumers}"
24656        );
24657        // And in name order, which the storage does not hold them in.
24658        let c1 = consumers.find("c1").unwrap();
24659        let c2 = consumers.find("c2").unwrap();
24660        assert!(c1 < c2, "{consumers}");
24661
24662        let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
24663        assert!(full.starts_with("*18\r\n"), "{full}");
24664        assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
24665        assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
24666
24667        assert!(
24668            f.run(&[b"XINFO", b"STREAM", b"missing"])
24669                .contains("no such key")
24670        );
24671        assert!(
24672            f.run(&[b"XINFO", b"GROUPS", b"missing"])
24673                .contains("no such key")
24674        );
24675        assert!(
24676            f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
24677                .starts_with("-NOGROUP")
24678        );
24679        assert!(
24680            f.run(&[b"XINFO", b"NOSUCH", b"s"])
24681                .contains("Try XINFO HELP")
24682        );
24683        assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
24684        assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
24685    }
24686
24687    /// `XPENDING`'s long form, which reads its arguments by counting them.
24688    #[test]
24689    fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
24690        let mut f = Fixture::new();
24691        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24692        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24693        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
24694
24695        let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
24696        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");
24697        assert_eq!(
24698            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
24699            "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
24700        );
24701        // A consumer nobody has heard of holds nothing rather than erroring.
24702        assert_eq!(
24703            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
24704            "*0\r\n"
24705        );
24706        assert_eq!(
24707            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
24708            list
24709        );
24710        // IDLE is only read at position three.
24711        assert!(
24712            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
24713                .contains("syntax error")
24714        );
24715        assert!(
24716            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
24717                .contains("syntax error")
24718        );
24719        assert_eq!(
24720            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
24721            "*0\r\n"
24722        );
24723        assert!(
24724            f.run(&[b"XPENDING", b"missing", b"g"])
24725                .starts_with("-NOGROUP")
24726        );
24727    }
24728
24729    /// `XSETID`, which is three counters and two refusals.
24730    #[test]
24731    fn xsetid_will_not_go_below_what_is_there() {
24732        let mut f = Fixture::new();
24733        f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
24734        assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
24735        assert_eq!(
24736            f.run(&[
24737                b"XSETID",
24738                b"s",
24739                b"10-1",
24740                b"ENTRIESADDED",
24741                b"7",
24742                b"MAXDELETEDID",
24743                b"9-1"
24744            ]),
24745            "+OK\r\n"
24746        );
24747        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
24748        assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
24749        assert!(
24750            info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
24751            "{info}"
24752        );
24753
24754        assert!(
24755            f.run(&[b"XSETID", b"s", b"1-1"])
24756                .contains("smaller than the target stream top item")
24757        );
24758        assert!(
24759            f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
24760                .contains("entries_added must be positive")
24761        );
24762        assert!(
24763            f.run(&[b"XSETID", b"missing", b"1-1"])
24764                .contains("no such key")
24765        );
24766    }
24767
24768    /// RESP3, where the two reads answer a map and the entries stay an array.
24769    #[test]
24770    fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
24771        let mut f = Fixture::new();
24772        f.run(&[b"HELLO", b"3"]);
24773        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24774        // A map header and then the key and the entries side by side, with no
24775        // two element array wrapping the pair.
24776        assert_eq!(
24777            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
24778            "%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"
24779        );
24780        // The fields are still one flat array and not a map, which is Redis's
24781        // shape and is what every consumer written before RESP3 expects.
24782        assert_eq!(
24783            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
24784            "*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"
24785        );
24786        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
24787    }
24788
24789    /// A store to migrate values into, so a test can watch the inversion.
24790    ///
24791    /// A vector rather than a file for the same reason the tier's own tests use
24792    /// one: the file work has not attached a real store yet, and what this is
24793    /// checking is the policy above the store rather than the store.
24794    struct Mem {
24795        blobs: Vec<Vec<u8>>,
24796    }
24797
24798    impl yo_kv::cold::Blocks for Mem {
24799        fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
24800            self.blobs.push(bytes.to_vec());
24801            Ok(yo_common::Addr::new(
24802                yo_common::Space::Log,
24803                (self.blobs.len() - 1) as u64,
24804            ))
24805        }
24806
24807        fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
24808            self.blobs
24809                .get(at.offset() as usize)
24810                .map(Vec::as_slice)
24811                .ok_or_else(|| {
24812                    yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
24813                })
24814        }
24815
24816        fn bytes(&self) -> u64 {
24817            self.blobs.iter().map(|b| b.len() as u64).sum()
24818        }
24819    }
24820
24821    /// A server holding several segments of strings, with somewhere to put them.
24822    ///
24823    /// Answers the fixture and what it was holding when it stopped filling.
24824    /// The three tests that call this are the ones Miri is not run over.
24825    ///
24826    /// What they are about is the regime a database is in once the arena has
24827    /// several segments, and a segment is two megabytes, so there is no smaller
24828    /// version of the question: twenty four thousand keys is already the least
24829    /// that gets there. Interpreted, each of them sat for over forty minutes
24830    /// and was still going. The arena's own segment handling is interpreted in
24831    /// full in its own crate, and the policy these three check is ordinary
24832    /// bookkeeping with no unsafe block anywhere in it.
24833    fn filled(attach: bool) -> (Fixture, usize) {
24834        let mut f = Fixture::new();
24835        if attach {
24836            f.server
24837                .striped(0)
24838                .hold_stripe(0)
24839                .attach(Box::new(Mem { blobs: Vec::new() }));
24840        }
24841        let val = vec![b'v'; 256];
24842        for i in 0..24000u32 {
24843            let k = format!("key:{i:08}");
24844            f.run(&[b"SET", k.as_bytes(), &val]);
24845        }
24846        let full = f.server.memory_bytes();
24847        assert!(full > 3 * 1024 * 1024, "the arena is several segments");
24848        (f, full)
24849    }
24850
24851    /// Write until the server is under `limit` or the writes run out.
24852    ///
24853    /// The same shape the eviction test uses. A memory limit is enforced in
24854    /// front of a command, so nothing happens until something is written, and
24855    /// the budget means one command does not do the whole job.
24856    fn press(f: &mut Fixture, limit: usize) {
24857        let val = vec![b'v'; 256];
24858        for i in 0..3000u32 {
24859            let k = format!("new:{i:08}");
24860            assert_eq!(
24861                f.run(&[b"SET", k.as_bytes(), &val]),
24862                "+OK\r\n",
24863                "write {i} was refused"
24864            );
24865            f.server.refresh_memory();
24866            if f.server.memory_bytes() <= limit {
24867                return;
24868            }
24869        }
24870        panic!(
24871            "it never got under: {} against {limit}",
24872            f.server.memory_bytes()
24873        );
24874    }
24875
24876    #[test]
24877    fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
24878        let mut f = Fixture::new();
24879        assert_eq!(
24880            f.run(&[b"CONFIG", b"GET", b"maxstore"]),
24881            "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
24882            "no limit is the default"
24883        );
24884        // The same memory value parser `maxmemory` uses, and the same trap in
24885        // it, plus the one spelling that means no limit at all.
24886        for (typed, bytes) in [
24887            (&b"0"[..], "0"),
24888            (b"1024", "1024"),
24889            (b"1k", "1000"),
24890            (b"1gb", "1073741824"),
24891            (b"-1", "-1"),
24892        ] {
24893            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
24894            assert_eq!(
24895                f.run(&[b"CONFIG", b"GET", b"maxstore"]),
24896                format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
24897                "set {}",
24898                String::from_utf8_lossy(typed)
24899            );
24900        }
24901        for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
24902            assert_eq!(
24903                f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
24904                "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
24905                "refused {}",
24906                String::from_utf8_lossy(bad)
24907            );
24908        }
24909        // Nothing is attached, so the answer to a memory limit is still Redis's.
24910        let info = f.run(&[b"INFO", b"memory"]);
24911        assert!(info.contains("maxstore:-1"), "{info}");
24912        assert!(info.contains("yo_memory_regime:evict"), "{info}");
24913        assert!(info.contains("yo_store_bytes:0"), "{info}");
24914    }
24915
24916    #[cfg_attr(miri, ignore = "several megabytes of arena, see `filled`")]
24917    #[test]
24918    fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
24919        // The inversion. The same pressure that makes a Redis server throw keys
24920        // away makes this one move values to the file, and afterwards every key
24921        // is still there and still answers with what was stored in it.
24922        let (mut f, full) = filled(true);
24923        let keys = f.run(&[b"DBSIZE"]);
24924        assert!(
24925            f.run(&[b"INFO", b"memory"])
24926                .contains("yo_memory_regime:migrate"),
24927            "a database with somewhere to put values migrates"
24928        );
24929
24930        let limit = full - 2 * 1024 * 1024;
24931        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
24932        f.run(&[
24933            b"CONFIG",
24934            b"SET",
24935            b"maxmemory",
24936            limit.to_string().as_bytes(),
24937        ]);
24938        press(&mut f, limit);
24939
24940        assert!(
24941            f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
24942            "nothing was thrown away"
24943        );
24944        let after: usize = f.run(&[b"DBSIZE"])[1..]
24945            .trim_end()
24946            .parse()
24947            .expect("a count");
24948        let before: usize = keys[1..].trim_end().parse().expect("a count");
24949        assert!(after > before, "the keys that came in are all still here");
24950        assert!(
24951            f.server.store_bytes() > 0,
24952            "and what came out of memory went to the file"
24953        );
24954        // And the values read back, which is the part that makes it a migration
24955        // rather than a loss.
24956        let val = format!("$256\r\n{}\r\n", "v".repeat(256));
24957        assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
24958        assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
24959    }
24960
24961    #[cfg_attr(miri, ignore = "several megabytes of arena, see `filled`")]
24962    #[test]
24963    fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
24964        // The documented setting for a drop in cache. A file that may hold
24965        // nothing cannot be migrated to, so eviction is all that is left, and
24966        // the server behaves exactly as it did before any of this existed.
24967        let (mut f, full) = filled(true);
24968        f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
24969        assert!(
24970            f.run(&[b"INFO", b"memory"])
24971                .contains("yo_memory_regime:evict"),
24972            "nothing may go to the file"
24973        );
24974
24975        let limit = full - 2 * 1024 * 1024;
24976        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
24977        f.run(&[
24978            b"CONFIG",
24979            b"SET",
24980            b"maxmemory",
24981            limit.to_string().as_bytes(),
24982        ]);
24983        press(&mut f, limit);
24984
24985        assert!(
24986            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
24987            "keys were thrown away, which is what was asked for"
24988        );
24989        assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
24990    }
24991
24992    #[cfg_attr(miri, ignore = "several megabytes of arena, see `filled`")]
24993    #[test]
24994    fn a_full_file_goes_back_to_evicting() {
24995        // A storage limit reached is a storage limit, and eviction is the right
24996        // answer to one. The budget here is a few kilobytes, so the first round
24997        // of migration fills it and everything after that is evicted.
24998        let (mut f, full) = filled(true);
24999        f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
25000        let limit = full - 2 * 1024 * 1024;
25001        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
25002        f.run(&[
25003            b"CONFIG",
25004            b"SET",
25005            b"maxmemory",
25006            limit.to_string().as_bytes(),
25007        ]);
25008        press(&mut f, limit);
25009
25010        assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
25011        assert!(
25012            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
25013            "and then it started evicting"
25014        );
25015        assert!(
25016            f.run(&[b"INFO", b"memory"])
25017                .contains("yo_memory_regime:evict"),
25018            "and it says so"
25019        );
25020    }
25021    // ------------------------------------------------------------- stripes
25022
25023    /// Every string command, run twice: once on a database that is one keyspace
25024    /// and once on a database that is eight, with the same commands in the same
25025    /// order and the replies compared byte for byte.
25026    ///
25027    /// This is the whole claim the striping rests on. A key belongs to one
25028    /// stripe and to no other, so the answer to a command cannot depend on how
25029    /// many stripes there are, and the way to check that is to ask the same
25030    /// question of two servers that differ in nothing else.
25031    ///
25032    /// The keys are chosen to land on different stripes rather than to look
25033    /// tidy. `MSET a 1 b 2 c 3` over eight stripes is only a test of anything if
25034    /// those three keys are not all on the same one, and at eight stripes three
25035    /// keys land together about one time in fifty.
25036    #[test]
25037    fn the_string_group_answers_the_same_however_many_stripes_there_are() {
25038        let script: &[&[&[u8]]] = &[
25039            // The single key commands, which are the ones that get handed one
25040            // stripe at the dispatch site.
25041            &[b"SET", b"k1", b"v1"],
25042            &[b"SET", b"k2", b"v2"],
25043            &[b"GET", b"k1"],
25044            &[b"GET", b"nothing"],
25045            &[b"GETSET", b"k1", b"v1b"],
25046            &[b"SETNX", b"k1", b"no"],
25047            &[b"SETNX", b"k3", b"yes"],
25048            &[b"APPEND", b"k3", b"!"],
25049            &[b"STRLEN", b"k3"],
25050            &[b"SETRANGE", b"k3", b"1", b"XY"],
25051            &[b"GETRANGE", b"k3", b"0", b"-1"],
25052            &[b"INCR", b"n1"],
25053            &[b"INCRBY", b"n1", b"41"],
25054            &[b"DECRBY", b"n1", b"2"],
25055            &[b"INCRBYFLOAT", b"f1", b"1.5"],
25056            &[b"SETEX", b"e1", b"100", b"v"],
25057            &[b"PSETEX", b"e2", b"100000", b"v"],
25058            &[b"GETEX", b"e1", b"PERSIST"],
25059            &[b"GETDEL", b"k2"],
25060            &[b"GET", b"k2"],
25061            &[b"DIGEST", b"k1"],
25062            &[b"DELEX", b"k3"],
25063            // The five that name more than one key, which are the ones that
25064            // cannot be handed one stripe at all.
25065            &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
25066            &[b"MGET", b"a", b"b", b"c", b"missing"],
25067            &[b"MSETNX", b"d", b"4", b"e", b"5"],
25068            &[b"MSETNX", b"e", b"6", b"f", b"7"],
25069            &[b"MGET", b"d", b"e", b"f"],
25070            &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
25071            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
25072            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
25073            &[b"MGET", b"g", b"h"],
25074            &[b"SET", b"s1", b"ohmytext"],
25075            &[b"SET", b"s2", b"mynewtext"],
25076            &[b"LCS", b"s1", b"s2"],
25077            &[b"LCS", b"s1", b"s2", b"LEN"],
25078            &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
25079            &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
25080            &[b"LCS", b"s1", b"gone"],
25081            // And the errors, which have to be the same errors.
25082            &[b"MSET", b"odd"],
25083            &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
25084            &[b"MGET"],
25085        ];
25086
25087        let mut one = Fixture::new();
25088        let mut many = Fixture::striped(8);
25089        for parts in script {
25090            let a = one.run(parts);
25091            let b = many.run(parts);
25092            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25093        }
25094    }
25095
25096    /// The keys of an `MSET` really do end up on different stripes.
25097    ///
25098    /// Without this the test above could pass on a server whose stripe number
25099    /// happened to be a constant, which is a striped database in name only.
25100    #[test]
25101    fn a_striped_database_spreads_the_keys_it_is_given() {
25102        let mut f = Fixture::striped(8);
25103        for i in 0..256 {
25104            let key = format!("key:{i}");
25105            f.run(&[b"SET", key.as_bytes(), b"v"]);
25106        }
25107        assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
25108    }
25109
25110    /// A wrong type stops an `MGET` no more than it does on one stripe: the key
25111    /// that is not a string comes back nil and the rest of the reply is intact.
25112    #[test]
25113    fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
25114        let mut one = Fixture::new();
25115        let mut many = Fixture::striped(8);
25116        for f in [&mut one, &mut many] {
25117            f.run(&[b"SET", b"str", b"v"]);
25118            // Planted rather than pushed. `RPUSH` belongs to the list group,
25119            // which has not been taught about stripes yet and would refuse the
25120            // wide server. What is under test is what `MGET` does when it walks
25121            // onto a key that is not a string, and that does not care how the
25122            // key got there.
25123            f.server
25124                .striped(0)
25125                .hold(b"list")
25126                .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
25127                .expect("a new list");
25128        }
25129        assert_eq!(
25130            one.run(&[b"MGET", b"str", b"list", b"gone"]),
25131            many.run(&[b"MGET", b"str", b"list", b"gone"])
25132        );
25133    }
25134
25135    /// The same claim for the keyspace group, and the same way of checking it.
25136    ///
25137    /// `SORT` is not in the script because it is the one command in that file
25138    /// that has not been taught about stripes, and `SCAN`, `KEYS` and
25139    /// `RANDOMKEY` are not in it either, because those three do not promise an
25140    /// order and comparing two replies byte for byte would be asserting one.
25141    /// They get tests of their own below.
25142    #[test]
25143    fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
25144        let script: &[&[&[u8]]] = &[
25145            &[b"SET", b"k1", b"v1"],
25146            &[b"SET", b"k2", b"v2"],
25147            &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
25148            &[b"TYPE", b"k1"],
25149            &[b"TYPE", b"gone"],
25150            &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
25151            &[b"EXPIRE", b"k1", b"100"],
25152            &[b"TTL", b"k1"],
25153            &[b"EXPIRE", b"k1", b"200", b"NX"],
25154            &[b"PERSIST", b"k1"],
25155            &[b"TTL", b"k1"],
25156            &[b"PEXPIREAT", b"k2", b"1900000000000"],
25157            &[b"EXPIRETIME", b"k2"],
25158            &[b"PEXPIRETIME", b"k2"],
25159            &[b"PERSIST", b"k2"],
25160            &[b"OBJECT", b"ENCODING", b"k1"],
25161            &[b"OBJECT", b"REFCOUNT", b"k1"],
25162            &[b"OBJECT", b"IDLETIME", b"k1"],
25163            &[b"OBJECT", b"FREQ", b"k1"],
25164            &[b"OBJECT", b"ENCODING", b"gone"],
25165            &[b"OBJECT", b"HELP"],
25166            &[b"RENAME", b"k1", b"k9"],
25167            &[b"GET", b"k9"],
25168            &[b"RENAME", b"gone", b"x"],
25169            &[b"RENAMENX", b"k9", b"k2"],
25170            &[b"RENAMENX", b"k9", b"k8"],
25171            &[b"GET", b"k8"],
25172            &[b"COPY", b"k8", b"c1"],
25173            &[b"COPY", b"k8", b"c1"],
25174            &[b"COPY", b"k8", b"c1", b"REPLACE"],
25175            &[b"COPY", b"k8", b"k8"],
25176            &[b"COPY", b"gone", b"c2"],
25177            &[b"COPY", b"k8", b"k8", b"DB", b"1"],
25178            &[b"COPY", b"k8", b"c9", b"DB", b"9"],
25179            &[b"MOVE", b"c1", b"1"],
25180            &[b"MOVE", b"c1", b"1"],
25181            &[b"MOVE", b"k8", b"0"],
25182            &[b"DEL", b"k2", b"gone"],
25183            &[b"UNLINK", b"k8", b"k8"],
25184            &[b"DBSIZE"],
25185        ];
25186
25187        let mut one = Fixture::new();
25188        let mut many = Fixture::striped(8);
25189        for parts in script {
25190            let a = one.run(parts);
25191            let b = many.run(parts);
25192            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25193        }
25194
25195        // `RESTORE` needs bytes a client would have got from a `DUMP`, so the
25196        // payload is taken from the store rather than parsed back out of a
25197        // reply that is not text. Both servers dump the same key and the bytes
25198        // are the same bytes, which is the first half of what is being checked
25199        // here.
25200        for f in [&mut one, &mut many] {
25201            f.run(&[b"SET", b"d1", b"payload"]);
25202            let payload = f
25203                .server
25204                .striped(0)
25205                .hold(b"d1")
25206                .dump(b"d1")
25207                .expect("a key that is there");
25208            assert!(
25209                f.run(&[b"DUMP", b"d1"])
25210                    .starts_with(&format!("${}", payload.len())),
25211                "a payload of the length the store gave"
25212            );
25213            assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
25214            assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
25215            assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
25216            assert_eq!(
25217                f.run(&[b"RESTORE", b"d2", b"0", &payload]),
25218                "-BUSYKEY Target key name already exists.\r\n"
25219            );
25220            assert_eq!(
25221                f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
25222                "-ERR DUMP payload version or checksum are wrong\r\n"
25223            );
25224        }
25225    }
25226
25227    /// A `SCAN` of a database of eight stripes comes back with all of it.
25228    ///
25229    /// The cursor is the thing under test. It has to carry the stripe as well
25230    /// as the place in it, so a client that stops at one stripe and comes back
25231    /// carries on in that stripe and not at the top of the database, and the
25232    /// walk has to end once rather than eight times.
25233    #[test]
25234    fn a_scan_of_a_striped_database_walks_all_of_it() {
25235        // Eight stripes and a COUNT of ten, so eighty keys is already more than
25236        // one page on every stripe and the cursor has to carry which stripe it
25237        // was on, which is the thing being checked.
25238        let n = if cfg!(miri) { 80 } else { 500 };
25239        let mut f = Fixture::striped(8);
25240        for i in 0..n {
25241            let key = format!("key:{i}");
25242            f.run(&[b"SET", key.as_bytes(), b"v"]);
25243        }
25244
25245        let mut seen = Vec::new();
25246        let mut cursor = "0".to_owned();
25247        let mut calls = 0;
25248        loop {
25249            let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
25250            let (next, keys) = scan_reply(&reply);
25251            seen.extend(keys);
25252            cursor = next;
25253            calls += 1;
25254            assert!(calls < 5_000, "a scan that will not finish");
25255            if cursor == "0" {
25256                break;
25257            }
25258        }
25259        seen.sort();
25260        assert_eq!(seen.len(), n, "a quiet scan answered a key twice");
25261        assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
25262
25263        // And the options still work when the walk is over several stripes,
25264        // since a `MATCH` is applied to keys a stripe handed up and a `TYPE` is
25265        // applied by each stripe on the way.
25266        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
25267        let (_, keys) = scan_reply(&reply);
25268        assert_eq!(keys.len(), 10, "key:40 through key:49");
25269        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
25270        let (_, keys) = scan_reply(&reply);
25271        assert!(keys.is_empty(), "nothing here is a list");
25272    }
25273
25274    /// `RANDOMKEY` on a striped database answers a key from any of the stripes.
25275    ///
25276    /// The draw picks the stripe first, so the thing that can go wrong is that
25277    /// it always picks the same one, and two hundred draws over eight stripes
25278    /// would make that obvious.
25279    #[test]
25280    fn a_random_key_can_come_from_any_stripe() {
25281        let mut f = Fixture::striped(8);
25282        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
25283        for i in 0..200 {
25284            let key = format!("key:{i}");
25285            f.run(&[b"SET", key.as_bytes(), b"v"]);
25286        }
25287        let mut homes = std::collections::HashSet::new();
25288        for _ in 0..200 {
25289            let got = f.run(&[b"RANDOMKEY"]);
25290            let key = got.split("\r\n").nth(1).expect("a key").to_owned();
25291            assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
25292            homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
25293        }
25294        assert_eq!(homes.len(), 8, "some stripe was never drawn from");
25295    }
25296
25297    /// Two keys that are not on the same stripe, which is what `RENAME` and
25298    /// `COPY` have to cope with and what a test has to arrange rather than
25299    /// hope for.
25300    fn apart(f: &mut Fixture, src: &str) -> String {
25301        let home = f.server.striped(0).stripe_of(src.as_bytes());
25302        for i in 0..1_000 {
25303            let dst = format!("dst:{i}");
25304            if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
25305                return dst;
25306            }
25307        }
25308        panic!("eight stripes and a thousand keys all landed in one place");
25309    }
25310
25311    /// A rename whose two keys are on two stripes moves the value, the deadline
25312    /// and, for a collection, the body itself.
25313    #[test]
25314    fn a_rename_across_stripes_takes_everything_with_it() {
25315        let mut f = Fixture::striped(8);
25316        let dst = apart(&mut f, "src");
25317        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
25318
25319        f.run(&[b"SET", src, b"v"]);
25320        f.run(&[b"EXPIRE", src, b"100"]);
25321        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
25322        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
25323        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
25324        assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
25325
25326        // A list, because a string lives in its record and a collection lives
25327        // in a slab, and the second of those is the one that can be left
25328        // behind. Planted through the store, since the list group has not been
25329        // taught about stripes yet.
25330        f.server
25331            .striped(0)
25332            .hold(src)
25333            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
25334            .expect("a new list");
25335        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
25336        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
25337        assert_eq!(
25338            f.server.striped(0).hold(dst).llen(dst).expect("a list"),
25339            2,
25340            "the members are on the stripe the key moved to"
25341        );
25342
25343        // And `RENAMENX` still refuses a destination that is taken, which is
25344        // the one answer the cross stripe path has to work out for itself.
25345        f.run(&[b"SET", src, b"v"]);
25346        assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
25347        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
25348        assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
25349    }
25350
25351    /// And a copy across two stripes leaves both keys behind it.
25352    #[test]
25353    fn a_copy_across_stripes_leaves_the_source_where_it_was() {
25354        let mut f = Fixture::striped(8);
25355        let dst = apart(&mut f, "src");
25356        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
25357
25358        f.run(&[b"SET", src, b"v"]);
25359        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
25360        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
25361        assert_eq!(
25362            f.run(&[b"COPY", src, dst]),
25363            ":0\r\n",
25364            "the destination is taken"
25365        );
25366        f.run(&[b"SET", src, b"w"]);
25367        assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
25368        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
25369
25370        // A collection is cloned rather than moved, so both keys have a body of
25371        // their own afterwards and writing to one does not show up in the
25372        // other.
25373        f.run(&[b"DEL", src, dst]);
25374        f.server
25375            .striped(0)
25376            .hold(src)
25377            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
25378            .expect("a new list");
25379        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
25380        f.server
25381            .striped(0)
25382            .hold(src)
25383            .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
25384            .expect("a list that is there");
25385        assert_eq!(f.server.striped(0).hold(src).llen(src).expect("a list"), 3);
25386        assert_eq!(f.server.striped(0).hold(dst).llen(dst).expect("a list"), 2);
25387    }
25388
25389    /// Every bitmap command, on one stripe and on eight, replies compared byte
25390    /// for byte.
25391    ///
25392    /// `BITOP` is the one that names more than one key and it is where the work
25393    /// went. The rest are single key commands that now find their own stripe,
25394    /// and they are here because the cheapest way to be sure the routing is
25395    /// right is to ask.
25396    #[test]
25397    fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
25398        let script: &[&[&[u8]]] = &[
25399            &[b"SET", b"k1", b"foobar"],
25400            &[b"SETBIT", b"b1", b"7", b"1"],
25401            &[b"SETBIT", b"b1", b"7", b"0"],
25402            &[b"GETBIT", b"k1", b"6"],
25403            &[b"GETBIT", b"k1", b"100"],
25404            &[b"BITCOUNT", b"k1"],
25405            &[b"BITCOUNT", b"k1", b"0", b"0"],
25406            &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
25407            &[b"BITPOS", b"k1", b"1"],
25408            &[b"BITPOS", b"k1", b"0", b"2"],
25409            &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
25410            &[
25411                b"BITFIELD",
25412                b"bf",
25413                b"SET",
25414                b"u8",
25415                b"0",
25416                b"255",
25417                b"GET",
25418                b"u8",
25419                b"0",
25420            ],
25421            &[
25422                b"BITFIELD",
25423                b"bf",
25424                b"OVERFLOW",
25425                b"SAT",
25426                b"INCRBY",
25427                b"u8",
25428                b"0",
25429                b"10",
25430            ],
25431            &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
25432            // The multi key one, over sources that are not on one stripe unless
25433            // eight stripes have folded into one.
25434            &[b"SET", b"s1", b"abc"],
25435            &[b"SET", b"s2", b"abd"],
25436            &[b"SET", b"s3", b"a"],
25437            &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
25438            &[b"GET", b"d1"],
25439            &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
25440            &[b"GET", b"d2"],
25441            &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
25442            &[b"STRLEN", b"d3"],
25443            &[b"BITOP", b"NOT", b"d4", b"s1"],
25444            &[b"STRLEN", b"d4"],
25445            &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
25446            &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
25447            &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
25448            &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
25449            // A source that is not there reads as empty, and a result with
25450            // nothing in it deletes the destination rather than writing one.
25451            &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
25452            &[b"EXISTS", b"d1"],
25453            &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
25454            &[b"GET", b"d9"],
25455            // And the errors, which have to be the same errors. The key that
25456            // is not a string is planted below rather than pushed here, since
25457            // the list group has not been taught about stripes yet.
25458            &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
25459            &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
25460            &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
25461            &[b"BITOP", b"DIFF", b"d1", b"s1"],
25462            &[b"BITOP", b"NOPE", b"d1", b"s1"],
25463            &[b"BITCOUNT", b"list"],
25464            &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
25465        ];
25466
25467        let mut one = Fixture::new();
25468        let mut many = Fixture::striped(8);
25469        for f in [&mut one, &mut many] {
25470            plant_list(f, b"list");
25471        }
25472        for parts in script {
25473            let a = one.run(parts);
25474            let b = many.run(parts);
25475            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25476        }
25477    }
25478
25479    /// A list under `key`, put there through the store.
25480    ///
25481    /// What a test does when it wants a key of the wrong type on a striped
25482    /// server, because the command that would make one is in a group that has
25483    /// not been taught about stripes yet.
25484    fn plant_list(f: &mut Fixture, key: &[u8]) {
25485        f.server
25486            .striped(0)
25487            .hold(key)
25488            .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
25489            .expect("a new list");
25490    }
25491
25492    /// A `BITOP` whose keys are on two stripes reads both of them.
25493    ///
25494    /// The test above spreads its keys by hashing and would still pass if one
25495    /// stripe were doing all the work, since the answers would be the same. This
25496    /// one puts the destination and the two sources where they are known not to
25497    /// share a stripe.
25498    #[test]
25499    fn a_bitop_across_stripes_reads_every_source() {
25500        let mut f = Fixture::striped(8);
25501        let other = apart(&mut f, "src");
25502        let (src, far) = (b"src".as_slice(), other.as_bytes());
25503        assert_ne!(
25504            f.server.striped(0).stripe_of(src),
25505            f.server.striped(0).stripe_of(far),
25506            "the two keys are the point of the test"
25507        );
25508
25509        f.run(&[b"SET", src, b"abc"]);
25510        f.run(&[b"SET", far, b"abd"]);
25511        assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
25512        assert_eq!(
25513            f.run(&[b"GET", far]),
25514            "$3\r\nab`\r\n",
25515            "a destination that is also a source"
25516        );
25517        f.run(&[b"SET", far, b"abd"]);
25518        assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
25519        assert_eq!(
25520            f.run(&[b"GET", src]),
25521            "$3\r\n\0\0\x07\r\n",
25522            "and the other way round"
25523        );
25524
25525        // A result of nothing deletes a destination on whatever stripe it is
25526        // on, and a source of the wrong type is refused before anything is
25527        // written.
25528        f.run(&[b"SET", src, b"abc"]);
25529        f.run(&[b"DEL", far]);
25530        assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
25531        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
25532        f.run(&[b"SET", src, b"abc"]);
25533        f.run(&[b"DEL", far]);
25534        plant_list(&mut f, far);
25535        assert_eq!(
25536            f.run(&[b"BITOP", b"OR", b"out", src, far]),
25537            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25538        );
25539        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
25540    }
25541
25542    /// Every HyperLogLog command, on one stripe and on eight.
25543    ///
25544    /// Not under Miri, for the reason on
25545    /// `the_debug_forms_answer_four_different_shapes`, and twice over here
25546    /// because the script is run against both shapes of server.
25547    #[cfg_attr(miri, ignore = "sixteen thousand registers a command")]
25548    #[test]
25549    fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
25550        let script: &[&[&[u8]]] = &[
25551            &[b"PFADD", b"h1", b"a", b"b", b"c"],
25552            &[b"PFADD", b"h1", b"a"],
25553            &[b"PFADD", b"h2"],
25554            &[b"PFADD", b"h2", b"c", b"d", b"e"],
25555            &[b"PFCOUNT", b"h1"],
25556            &[b"PFCOUNT", b"h2"],
25557            &[b"PFCOUNT", b"missing"],
25558            // The two that name more than one key.
25559            &[b"PFCOUNT", b"h1", b"h2"],
25560            &[b"PFCOUNT", b"h1", b"missing"],
25561            &[b"PFMERGE", b"m", b"h1", b"h2"],
25562            &[b"PFCOUNT", b"m"],
25563            &[b"STRLEN", b"m"],
25564            &[b"PFMERGE", b"m"],
25565            &[b"PFCOUNT", b"m"],
25566            &[b"PFMERGE", b"m2", b"missing"],
25567            &[b"PFCOUNT", b"m2"],
25568            // The debugging ones, which are single key and change what they
25569            // look at.
25570            &[b"PFDEBUG", b"ENCODING", b"h1"],
25571            &[b"PFDEBUG", b"DECODE", b"h1"],
25572            &[b"PFDEBUG", b"TODENSE", b"h1"],
25573            &[b"PFDEBUG", b"ENCODING", b"h1"],
25574            &[b"PFDEBUG", b"TODENSE", b"h1"],
25575            &[b"PFCOUNT", b"h1", b"h2"],
25576            &[b"PFSELFTEST"],
25577            // And the errors.
25578            &[b"SET", b"plain", b"not a sketch at all"],
25579            &[b"PFADD", b"plain", b"a"],
25580            &[b"PFCOUNT", b"plain"],
25581            &[b"PFCOUNT", b"h1", b"plain"],
25582            &[b"PFMERGE", b"plain", b"h1"],
25583            &[b"PFMERGE", b"m", b"plain"],
25584            &[b"PFDEBUG", b"ENCODING", b"gone"],
25585            &[b"PFDEBUG", b"NOPE", b"h1"],
25586        ];
25587
25588        let mut one = Fixture::new();
25589        let mut many = Fixture::striped(8);
25590        for parts in script {
25591            let a = one.run(parts);
25592            let b = many.run(parts);
25593            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25594        }
25595    }
25596
25597    /// Every set command, on one stripe and on eight.
25598    ///
25599    /// The commands that answer members answer them in whatever order the set
25600    /// or the table they were built in holds them, so those replies are
25601    /// compared as sets. Everything else is compared byte for byte. Two servers
25602    /// agreeing on the order would be a fact about the tables and not about the
25603    /// answer, and asserting it would make this test fail for a reason nobody
25604    /// cares about.
25605    #[test]
25606    fn the_set_group_answers_the_same_however_many_stripes_there_are() {
25607        const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
25608        let script: &[&[&[u8]]] = &[
25609            &[b"SADD", b"s1", b"a", b"b", b"c"],
25610            &[b"SADD", b"s1", b"a"],
25611            &[b"SADD", b"s2", b"b", b"c", b"d"],
25612            &[b"SADD", b"ints", b"1", b"2", b"3"],
25613            &[b"SCARD", b"s1"],
25614            &[b"SISMEMBER", b"s1", b"a"],
25615            &[b"SISMEMBER", b"s1", b"z"],
25616            &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
25617            &[b"SMEMBERS", b"s1"],
25618            &[b"SREM", b"s1", b"c"],
25619            &[b"SADD", b"s1", b"c"],
25620            &[b"SSCAN", b"s1", b"0"],
25621            &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
25622            // The two draws, on a set of one member, which is the only shape
25623            // whose answer two servers have to agree on.
25624            &[b"SADD", b"one", b"m"],
25625            &[b"SRANDMEMBER", b"one"],
25626            &[b"SRANDMEMBER", b"one", b"-3"],
25627            &[b"SRANDMEMBER", b"gone"],
25628            &[b"SPOP", b"one"],
25629            &[b"SPOP", b"one"],
25630            &[b"SPOP", b"gone", b"2"],
25631            // The one that names two keys.
25632            &[b"SMOVE", b"s1", b"s2", b"a"],
25633            &[b"SMOVE", b"s1", b"s2", b"zzz"],
25634            &[b"SMOVE", b"gone", b"s2", b"a"],
25635            &[b"SMEMBERS", b"s1"],
25636            &[b"SMEMBERS", b"s2"],
25637            // The algebra.
25638            &[b"SINTER", b"s1", b"s2"],
25639            &[b"SUNION", b"s1", b"s2"],
25640            &[b"SDIFF", b"s2", b"s1"],
25641            &[b"SINTER", b"s1", b"gone"],
25642            &[b"SUNION", b"s1", b"gone"],
25643            &[b"SDIFF", b"gone", b"s1"],
25644            &[b"SINTER", b"ints", b"s1"],
25645            &[b"SINTERCARD", b"2", b"s1", b"s2"],
25646            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
25647            &[b"SUNIONCARD", b"2", b"s1", b"s2"],
25648            &[b"SDIFFCARD", b"2", b"s2", b"s1"],
25649            &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
25650            &[b"SMEMBERS", b"d1"],
25651            &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
25652            &[b"SCARD", b"d2"],
25653            &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
25654            &[b"SCARD", b"d3"],
25655            // An empty result deletes the destination rather than storing a
25656            // set with nothing in it.
25657            &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
25658            &[b"EXISTS", b"d4"],
25659            // And a destination that is also a source.
25660            &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
25661            &[b"SCARD", b"s2"],
25662            // The errors, which have to be the same errors.
25663            &[b"SET", b"str", b"v"],
25664            &[b"SADD", b"str", b"a"],
25665            &[b"SINTER", b"s1", b"str"],
25666            &[b"SINTERSTORE", b"d5", b"s1", b"str"],
25667            &[b"EXISTS", b"d5"],
25668            &[b"SMOVE", b"str", b"s2", b"a"],
25669            &[b"SMOVE", b"s1", b"str", b"b"],
25670            &[b"SMOVE", b"gone", b"str", b"b"],
25671            &[b"SINTERCARD", b"0", b"s1"],
25672            &[b"SINTERCARD", b"3", b"s1", b"s2"],
25673            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
25674            &[b"SPOP", b"s1", b"-1"],
25675        ];
25676
25677        let mut one = Fixture::new();
25678        let mut many = Fixture::striped(8);
25679        for parts in script {
25680            let a = one.run(parts);
25681            let b = many.run(parts);
25682            let name = String::from_utf8_lossy(parts[0]).to_uppercase();
25683            if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
25684                assert_eq!(sorted(&a), sorted(&b), "{name}");
25685            } else {
25686                assert_eq!(a, b, "{name}");
25687            }
25688        }
25689    }
25690
25691    /// The algebra over sets that are known to be on different stripes.
25692    #[test]
25693    fn a_set_operation_across_stripes_reads_every_set() {
25694        let mut f = Fixture::striped(8);
25695        let second = apart(&mut f, "s1");
25696        let third = apart(&mut f, &second);
25697        let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
25698
25699        f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
25700        f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
25701        assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
25702        assert_eq!(
25703            sorted(&f.run(&[b"SUNION", s1, s2])),
25704            ["a", "b", "c", "d"],
25705            "a union of two stripes is both of them"
25706        );
25707        assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
25708        assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
25709        assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
25710        assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
25711
25712        // A destination on a third stripe, and then one that is also a source.
25713        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
25714        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
25715        assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
25716        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
25717        assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
25718        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
25719
25720        // An empty result deletes a destination wherever it is, and a key of
25721        // the wrong type stops the command before the destination is touched.
25722        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
25723        assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
25724        f.run(&[b"SET", s3, b"v"]);
25725        assert_eq!(
25726            f.run(&[b"SINTER", s1, s3]),
25727            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25728        );
25729        assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
25730    }
25731
25732    /// An `SMOVE` whose two keys are on two stripes.
25733    #[test]
25734    fn a_move_across_stripes_takes_the_member_with_it() {
25735        let mut f = Fixture::striped(8);
25736        let other = apart(&mut f, "src");
25737        let (src, dst) = (b"src".as_slice(), other.as_bytes());
25738
25739        f.run(&[b"SADD", src, b"a", b"b"]);
25740        f.run(&[b"SADD", dst, b"c"]);
25741        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
25742        assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
25743        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
25744        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
25745
25746        // A destination that is not there is created on its own stripe, and a
25747        // source that loses its last member is deleted from its own.
25748        f.run(&[b"DEL", dst]);
25749        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
25750        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
25751        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
25752
25753        // And a source that is not there answers zero without ever asking what
25754        // the destination holds, which is Redis's order and not the obvious
25755        // one.
25756        f.run(&[b"SET", dst, b"v"]);
25757        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
25758        f.run(&[b"SADD", src, b"b"]);
25759        assert_eq!(
25760            f.run(&[b"SMOVE", src, dst, b"b"]),
25761            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25762        );
25763    }
25764
25765    /// A count and a merge over sketches that are known to be on two stripes.
25766    #[test]
25767    fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
25768        let mut f = Fixture::striped(8);
25769        let other = apart(&mut f, "src");
25770        let (src, far) = (b"src".as_slice(), other.as_bytes());
25771
25772        for i in 0..150 {
25773            let ele = format!("e:{i}");
25774            f.run(&[b"PFADD", src, ele.as_bytes()]);
25775        }
25776        for i in 150..200 {
25777            let ele = format!("e:{i}");
25778            f.run(&[b"PFADD", far, ele.as_bytes()]);
25779        }
25780        // The three numbers a real server gives for these elements, which are
25781        // the numbers the single stripe tests in the keyspace crate check too.
25782        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
25783        assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
25784        assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
25785
25786        // A merge whose destination is on a third stripe, and then one that
25787        // writes into a source.
25788        let dest = apart(&mut f, &other);
25789        assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
25790        assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
25791        assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
25792        assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
25793        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
25794    }
25795
25796    /// Every sorted set command, on one stripe and on eight.
25797    ///
25798    /// Every reply here is compared byte for byte, unlike the set group, because
25799    /// a sorted set answers in rank order and members sharing a score come out
25800    /// in the order of their bytes. There is nothing left for the table the
25801    /// answer was built in to decide.
25802    #[test]
25803    fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
25804        let script: &[&[&[u8]]] = &[
25805            &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
25806            &[b"ZADD", b"z1", b"NX", b"9", b"a"],
25807            &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
25808            &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
25809            &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
25810            &[b"ZINCRBY", b"z1", b"1.5", b"b"],
25811            &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
25812            &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
25813            &[b"ZADD", b"one", b"1", b"m"],
25814            &[b"ZCARD", b"z1"],
25815            &[b"ZCARD", b"gone"],
25816            &[b"ZSCORE", b"z1", b"a"],
25817            &[b"ZSCORE", b"z1", b"zz"],
25818            &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
25819            &[b"ZRANK", b"z1", b"c"],
25820            &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
25821            &[b"ZREVRANK", b"z1", b"c"],
25822            &[b"ZRANK", b"z1", b"gone"],
25823            &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
25824            &[b"ZCOUNT", b"z1", b"(1", b"3"],
25825            &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
25826            // The range commands, which are one parse and one walk.
25827            &[b"ZRANGE", b"z1", b"0", b"-1"],
25828            &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
25829            &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
25830            &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
25831            &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
25832            &[b"ZREVRANGE", b"z1", b"0", b"-1"],
25833            &[
25834                b"ZRANGEBYSCORE",
25835                b"z1",
25836                b"-inf",
25837                b"+inf",
25838                b"LIMIT",
25839                b"1",
25840                b"1",
25841            ],
25842            &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
25843            &[b"ZSCAN", b"z1", b"0"],
25844            &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
25845            // The draw, on a sorted set of one member, which is the only shape
25846            // whose answer two servers have to agree on.
25847            &[b"ZRANDMEMBER", b"one"],
25848            &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
25849            &[b"ZRANDMEMBER", b"gone"],
25850            // The one that copies a window into another key.
25851            &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
25852            &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
25853            &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
25854            &[b"EXISTS", b"d0"],
25855            // The algebra, in both its shapes.
25856            &[b"ZUNION", b"2", b"z1", b"z2"],
25857            &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
25858            &[
25859                b"ZUNION",
25860                b"2",
25861                b"z1",
25862                b"z2",
25863                b"WEIGHTS",
25864                b"2",
25865                b"3",
25866                b"AGGREGATE",
25867                b"MAX",
25868                b"WITHSCORES",
25869            ],
25870            &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
25871            &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
25872            &[b"ZDIFF", b"2", b"gone", b"z1"],
25873            &[b"ZINTERCARD", b"2", b"z1", b"z2"],
25874            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
25875            &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
25876            &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
25877            &[
25878                b"ZINTERSTORE",
25879                b"d2",
25880                b"2",
25881                b"z1",
25882                b"z2",
25883                b"AGGREGATE",
25884                b"MIN",
25885            ],
25886            &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
25887            &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
25888            &[b"ZCARD", b"d3"],
25889            // An empty result deletes the destination rather than storing a
25890            // sorted set with nothing in it.
25891            &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
25892            &[b"EXISTS", b"d4"],
25893            // A plain set is a sorted set where every score is one, so it is a
25894            // legal input to all of these.
25895            &[b"SADD", b"plain", b"a", b"x"],
25896            &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
25897            &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
25898            // And a destination that is also a source.
25899            &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
25900            &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
25901            // The three removals and the two pops.
25902            &[b"ZREM", b"d5", b"x", b"nothere"],
25903            &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
25904            &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
25905            &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
25906            &[b"ZPOPMIN", b"z1"],
25907            &[b"ZPOPMAX", b"z1", b"2"],
25908            &[b"ZPOPMIN", b"gone"],
25909            &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
25910            &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
25911            // The errors, which have to be the same errors.
25912            &[b"SET", b"str", b"v"],
25913            &[b"ZADD", b"str", b"1", b"a"],
25914            &[b"ZSCORE", b"str", b"a"],
25915            &[b"ZADD", b"z1", b"nan", b"a"],
25916            &[b"ZUNION", b"2", b"z1", b"str"],
25917            &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
25918            &[b"EXISTS", b"d6"],
25919            &[b"ZINTERCARD", b"0", b"z1"],
25920            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
25921            &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
25922            &[b"ZMPOP", b"1", b"str", b"MIN"],
25923            &[b"ZPOPMIN", b"z1", b"-1"],
25924        ];
25925
25926        let mut one = Fixture::new();
25927        let mut many = Fixture::striped(8);
25928        for parts in script {
25929            let a = one.run(parts);
25930            let b = many.run(parts);
25931            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25932        }
25933    }
25934
25935    /// The algebra over sorted sets that are known to be on different stripes.
25936    #[test]
25937    fn a_sorted_set_operation_across_stripes_reads_every_input() {
25938        let mut f = Fixture::striped(8);
25939        let second = apart(&mut f, "z1");
25940        let third = apart(&mut f, &second);
25941        let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
25942
25943        f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
25944        f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
25945        // a is 1, c is 4, b is 2 and 3 added together, which is the order they
25946        // come out in and the answer that says both stripes were read.
25947        assert_eq!(
25948            f.run(&[b"ZUNION", b"2", z1, z2]),
25949            "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
25950        );
25951        assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
25952        assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
25953        assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
25954        assert_eq!(
25955            f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
25956            ":1\r\n"
25957        );
25958
25959        // A destination on a third stripe, and the weights and the aggregate
25960        // reaching every input.
25961        assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
25962        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
25963        assert_eq!(
25964            f.run(&[
25965                b"ZUNIONSTORE",
25966                z3,
25967                b"2",
25968                z1,
25969                z2,
25970                b"WEIGHTS",
25971                b"2",
25972                b"3",
25973                b"AGGREGATE",
25974                b"MAX"
25975            ]),
25976            ":3\r\n"
25977        );
25978        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
25979        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
25980        assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
25981        assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
25982        assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
25983
25984        // A pop over keys on several stripes takes from the first one that has
25985        // anything, which is what makes the order of the keys matter.
25986        let popped = format!(
25987            "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
25988            second.len()
25989        );
25990        assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
25991        f.run(&[b"ZADD", z2, b"3", b"b"]);
25992
25993        // An empty result deletes a destination wherever it is, and an input of
25994        // the wrong type stops the command before the destination is touched.
25995        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
25996        assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
25997        f.run(&[b"SET", z3, b"v"]);
25998        assert_eq!(
25999            f.run(&[b"ZUNION", b"2", z1, z3]),
26000            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
26001        );
26002        assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
26003
26004        // And a destination that is also a source works across stripes for the
26005        // reason it works on one: the whole result is built before anything is
26006        // written.
26007        assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
26008        assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
26009        assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
26010    }
26011
26012    /// A `ZRANGESTORE` whose two keys are on two stripes.
26013    #[test]
26014    fn a_range_store_across_stripes_copies_the_window() {
26015        let mut f = Fixture::striped(8);
26016        let other = apart(&mut f, "src");
26017        let third = apart(&mut f, &other);
26018        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
26019
26020        f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
26021        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
26022        assert_eq!(
26023            f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
26024            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
26025        );
26026        assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
26027
26028        // A window walked backwards takes the other end of the sorted set and
26029        // still stores what it took in score order.
26030        assert_eq!(
26031            f.run(&[
26032                b"ZRANGESTORE",
26033                dst,
26034                src,
26035                b"+inf",
26036                b"-inf",
26037                b"BYSCORE",
26038                b"REV",
26039                b"LIMIT",
26040                b"0",
26041                b"2"
26042            ]),
26043            ":2\r\n"
26044        );
26045        assert_eq!(
26046            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
26047            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
26048        );
26049
26050        // An empty window deletes the destination on its own stripe, and a
26051        // source of the wrong type is refused before the destination is touched.
26052        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
26053        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
26054        f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
26055        f.run(&[b"SET", plain, b"v"]);
26056        assert_eq!(
26057            f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
26058            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
26059        );
26060        assert_eq!(
26061            f.run(&[b"ZCARD", dst]),
26062            ":3\r\n",
26063            "and left the destination"
26064        );
26065    }
26066
26067    /// Every list command, on one stripe and on eight.
26068    ///
26069    /// The blocking six are in here too, both when they can be answered on the
26070    /// spot and when they cannot, since a command that parks its client writes
26071    /// nothing at all and two servers have to agree about that as much as they
26072    /// agree about a reply.
26073    #[test]
26074    fn the_list_group_answers_the_same_however_many_stripes_there_are() {
26075        let script: &[&[&[u8]]] = &[
26076            &[b"RPUSH", b"l1", b"a", b"b", b"c"],
26077            &[b"LPUSH", b"l1", b"z"],
26078            &[b"RPUSHX", b"l1", b"d"],
26079            &[b"LPUSHX", b"gone", b"x"],
26080            &[b"RPUSHX", b"gone", b"x"],
26081            &[b"LLEN", b"l1"],
26082            &[b"LLEN", b"gone"],
26083            &[b"LRANGE", b"l1", b"0", b"-1"],
26084            &[b"LRANGE", b"l1", b"1", b"2"],
26085            &[b"LRANGE", b"l1", b"5", b"9"],
26086            &[b"LINDEX", b"l1", b"0"],
26087            &[b"LINDEX", b"l1", b"-1"],
26088            &[b"LINDEX", b"l1", b"99"],
26089            &[b"LSET", b"l1", b"0", b"y"],
26090            &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
26091            &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
26092            &[b"LPOS", b"l1", b"b"],
26093            &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
26094            &[b"LPOS", b"l1", b"nothere"],
26095            &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
26096            &[b"LREM", b"l1", b"1", b"aa"],
26097            &[b"LTRIM", b"l1", b"0", b"3"],
26098            &[b"LRANGE", b"l1", b"0", b"-1"],
26099            &[b"LPOP", b"l1"],
26100            &[b"RPOP", b"l1"],
26101            &[b"LPOP", b"l1", b"2"],
26102            &[b"LPOP", b"gone"],
26103            &[b"LPOP", b"gone", b"2"],
26104            &[b"EXISTS", b"l1"],
26105            // The ones that name two keys, and the one that takes a block of
26106            // elements rather than the one on the end.
26107            &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
26108            &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
26109            &[b"RPOPLPUSH", b"src", b"dst"],
26110            &[b"LRANGE", b"dst", b"0", b"-1"],
26111            &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
26112            &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
26113            &[
26114                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
26115            ],
26116            &[
26117                b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
26118            ],
26119            &[b"LRANGE", b"dst", b"0", b"-1"],
26120            &[
26121                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
26122            ],
26123            &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
26124            &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
26125            &[b"LMPOP", b"1", b"gone", b"LEFT"],
26126            // The blocking ones, first with something there to answer them and
26127            // then with nothing, which parks the client and writes nothing.
26128            &[b"RPUSH", b"q", b"a", b"b", b"c"],
26129            &[b"BLPOP", b"gone", b"q", b"0"],
26130            &[b"BRPOP", b"q", b"0"],
26131            &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
26132            &[b"RPUSH", b"q", b"x", b"y", b"z"],
26133            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
26134            &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
26135            &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
26136            &[b"BLPOP", b"q", b"0"],
26137            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
26138            // The errors, which have to be the same errors.
26139            &[b"SET", b"plain", b"v"],
26140            &[b"LPUSH", b"plain", b"a"],
26141            &[b"LLEN", b"plain"],
26142            &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
26143            &[b"LRANGE", b"dst", b"0", b"-1"],
26144            &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
26145            &[b"LSET", b"gone", b"0", b"v"],
26146            &[b"LSET", b"dst", b"99", b"v"],
26147            &[b"LPOP", b"dst", b"-1"],
26148            &[b"LMPOP", b"0", b"dst", b"LEFT"],
26149            &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
26150        ];
26151
26152        let mut one = Fixture::new();
26153        let mut many = Fixture::striped(8);
26154        for parts in script {
26155            let a = one.run(parts);
26156            let b = many.run(parts);
26157            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26158        }
26159    }
26160
26161    /// An `LMOVE` and an `LMOVEM` whose two keys are on two stripes.
26162    #[test]
26163    fn a_list_move_across_stripes_takes_the_elements_with_it() {
26164        let mut f = Fixture::striped(8);
26165        let other = apart(&mut f, "src");
26166        let third = apart(&mut f, &other);
26167        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
26168
26169        f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
26170        assert_eq!(
26171            f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
26172            "$1\r\na\r\n"
26173        );
26174        assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
26175        assert_eq!(
26176            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
26177            "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
26178            "one went on each end of the destination"
26179        );
26180        assert_eq!(
26181            f.run(&[b"LRANGE", src, b"0", b"-1"]),
26182            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
26183        );
26184
26185        // A block of them, which under BULK arrives in the order it left.
26186        assert_eq!(
26187            f.run(&[
26188                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
26189            ]),
26190            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
26191        );
26192        assert_eq!(
26193            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
26194            "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
26195        );
26196        assert_eq!(
26197            f.run(&[b"EXISTS", src]),
26198            ":0\r\n",
26199            "and the source is gone with its last element"
26200        );
26201
26202        // An `EXACTLY` the source cannot fill moves nothing, and a source that
26203        // is not there at all is the two kinds of nothing the two commands have.
26204        f.run(&[b"RPUSH", src, b"e", b"f"]);
26205        assert_eq!(
26206            f.run(&[
26207                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
26208            ]),
26209            "*-1\r\n"
26210        );
26211        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
26212        assert_eq!(
26213            f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
26214            "$-1\r\n"
26215        );
26216        assert_eq!(
26217            f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
26218            "*-1\r\n"
26219        );
26220
26221        // A destination of the wrong type is refused before anything is taken,
26222        // which is the order that matters most here, since an element already
26223        // out of the source would have nowhere to go back to.
26224        f.run(&[b"SET", plain, b"v"]);
26225        assert_eq!(
26226            f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
26227            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
26228        );
26229        assert_eq!(
26230            f.run(&[b"LLEN", src]),
26231            ":2\r\n",
26232            "and left the source alone"
26233        );
26234        assert_eq!(
26235            f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
26236            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
26237        );
26238        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
26239    }
26240
26241    /// A parked client served by a push that landed on another stripe.
26242    ///
26243    /// A waiter remembers the database and not the stripe, which is the point:
26244    /// serving it runs the same attempt the command ran, and the attempt finds
26245    /// the stripe each of its keys is on for itself.
26246    #[test]
26247    fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
26248        let mut f = Fixture::striped(8);
26249        let other = apart(&mut f, "q");
26250        let (q, far) = (b"q".as_slice(), other.as_bytes());
26251
26252        assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
26253        assert_eq!(f.server.parked(), 1);
26254        f.run(&[b"RPUSH", far, b"v"]);
26255        let mut out = Out::new(Proto::Resp2);
26256        assert!(f.server.serve_waiter(7, 0, &mut out));
26257        let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
26258        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
26259        assert_eq!(
26260            f.run(&[b"EXISTS", far]),
26261            ":0\r\n",
26262            "and it took the element with it"
26263        );
26264
26265        // And a move across two stripes is served the same way, by the push
26266        // that fills its source.
26267        f.server.forget_waiters(7);
26268        assert_eq!(
26269            f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
26270            Flow::Block
26271        );
26272        f.run(&[b"RPUSH", q, b"w"]);
26273        let mut out = Out::new(Proto::Resp2);
26274        assert!(f.server.serve_waiter(7, 0, &mut out));
26275        assert_eq!(
26276            core::str::from_utf8(out.as_slice()).expect("ascii"),
26277            "$1\r\nw\r\n"
26278        );
26279        assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
26280    }
26281
26282    /// Every stream command, on one stripe and on eight.
26283    ///
26284    /// Every ID is written out rather than left to the clock, so the two servers
26285    /// are being compared on what they store and not on how long the test took
26286    /// to get from one of them to the other.
26287    #[test]
26288    fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
26289        let script: &[&[&[u8]]] = &[
26290            &[b"XADD", b"s", b"1-1", b"a", b"1"],
26291            &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
26292            &[b"XADD", b"s", b"3-1", b"d", b"4"],
26293            &[b"XADD", b"s", b"1-1", b"e", b"5"],
26294            &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
26295            &[b"XLEN", b"s"],
26296            &[b"XLEN", b"gone"],
26297            &[b"XRANGE", b"s", b"-", b"+"],
26298            &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
26299            &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
26300            &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
26301            &[b"XREVRANGE", b"s", b"+", b"-"],
26302            &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
26303            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
26304            &[b"XREAD", b"STREAMS", b"s", b"$"],
26305            // The groups, which is where most of the state is.
26306            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
26307            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
26308            &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
26309            &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
26310            &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
26311            &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
26312            &[
26313                b"XREADGROUP",
26314                b"GROUP",
26315                b"g",
26316                b"c1",
26317                b"COUNT",
26318                b"1",
26319                b"STREAMS",
26320                b"s",
26321                b"0",
26322            ],
26323            &[
26324                b"XREADGROUP",
26325                b"GROUP",
26326                b"nope",
26327                b"c1",
26328                b"STREAMS",
26329                b"s",
26330                b">",
26331            ],
26332            &[b"XPENDING", b"s", b"g"],
26333            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
26334            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
26335            &[b"XPENDING", b"s", b"nope"],
26336            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
26337            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
26338            &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
26339            &[b"XACK", b"s", b"g", b"1-1"],
26340            &[b"XACK", b"s", b"g", b"1-1"],
26341            &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
26342            &[b"XPENDING", b"s", b"g"],
26343            &[b"XINFO", b"STREAM", b"s"],
26344            &[b"XINFO", b"GROUPS", b"s"],
26345            &[b"XINFO", b"CONSUMERS", b"s", b"g"],
26346            &[b"XINFO", b"STREAM", b"gone"],
26347            // Deleting, trimming and moving the ID on.
26348            &[b"XDEL", b"s", b"3-1"],
26349            &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
26350            &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
26351            &[b"XADD", b"s", b"9-1", b"z", b"9"],
26352            &[b"XTRIM", b"s", b"MAXLEN", b"1"],
26353            &[b"XTRIM", b"s", b"MINID", b"9"],
26354            &[b"XSETID", b"s", b"99-1"],
26355            &[b"XSETID", b"s", b"1-1"],
26356            &[b"XLEN", b"s"],
26357            &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
26358            &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
26359            &[b"XGROUP", b"DESTROY", b"s", b"g"],
26360            &[b"XGROUP", b"DESTROY", b"s", b"g"],
26361            // And the errors.
26362            &[b"SET", b"plain", b"v"],
26363            &[b"XADD", b"plain", b"1-1", b"a", b"1"],
26364            &[b"XLEN", b"plain"],
26365            &[b"XREAD", b"STREAMS", b"plain", b"0"],
26366            &[b"XRANGE", b"s", b"bogus", b"+"],
26367            &[b"XADD", b"s", b"1-1", b"a"],
26368            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
26369            &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
26370        ];
26371
26372        let mut one = Fixture::new();
26373        let mut many = Fixture::striped(8);
26374        for parts in script {
26375            let a = one.run(parts);
26376            let b = many.run(parts);
26377            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26378        }
26379    }
26380
26381    /// An `XREAD` and an `XREADGROUP` naming two keys on two stripes.
26382    ///
26383    /// Nothing is shared between the two streams, so the only thing this can go
26384    /// wrong at is looking both of them up, which is exactly what a read that
26385    /// held one database and walked it would get wrong.
26386    #[test]
26387    fn a_stream_read_across_stripes_reads_every_key() {
26388        let mut f = Fixture::striped(8);
26389        let other = apart(&mut f, "s1");
26390        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
26391
26392        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
26393        f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
26394        let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
26395        assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
26396        assert!(got.contains("1-1"), "the first one is in there: {got}");
26397        assert!(got.contains("2-1"), "and so is the second: {got}");
26398
26399        // A group read looks its group up on every key before it reads any of
26400        // them, so a group that is missing on the far key stops the near one.
26401        f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
26402        let got = f.run(&[
26403            b"XREADGROUP",
26404            b"GROUP",
26405            b"g",
26406            b"c",
26407            b"STREAMS",
26408            s1,
26409            s2,
26410            b">",
26411            b">",
26412        ]);
26413        assert!(got.starts_with("-NOGROUP"), "{got}");
26414        assert_eq!(
26415            f.run(&[b"XPENDING", s1, b"g"]),
26416            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
26417            "and read nothing from the key that did have the group"
26418        );
26419
26420        f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
26421        let got = f.run(&[
26422            b"XREADGROUP",
26423            b"GROUP",
26424            b"g",
26425            b"c",
26426            b"STREAMS",
26427            s1,
26428            s2,
26429            b">",
26430            b">",
26431        ]);
26432        assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
26433    }
26434
26435    /// A client parked on an `XREAD` woken by an entry on another stripe.
26436    #[test]
26437    fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
26438        let mut f = Fixture::striped(8);
26439        let other = apart(&mut f, "s1");
26440        let (s1, far) = (b"s1".as_slice(), other.as_bytes());
26441        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
26442        f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
26443
26444        assert_eq!(
26445            f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
26446                .0,
26447            Flow::Block
26448        );
26449        f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
26450        let mut out = Out::new(Proto::Resp2);
26451        assert!(f.server.serve_waiter(7, 0, &mut out));
26452        let want = format!(
26453            "*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",
26454            other.len()
26455        );
26456        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
26457    }
26458
26459    /// Every JSON command, on one stripe and on eight.
26460    #[test]
26461    fn the_json_group_answers_the_same_however_many_stripes_there_are() {
26462        let script: &[&[&[u8]]] = &[
26463            &[
26464                b"JSON.SET",
26465                b"d",
26466                b"$",
26467                br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
26468            ],
26469            &[b"JSON.SET", b"d", b"$.a", b"2"],
26470            &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
26471            &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
26472            &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
26473            &[b"JSON.GET", b"d"],
26474            &[b"JSON.GET", b"d", b"$.b"],
26475            &[b"JSON.GET", b"gone", b"$"],
26476            &[b"JSON.TYPE", b"d", b"$.b"],
26477            &[b"JSON.TYPE", b"d", b"$.s"],
26478            &[b"JSON.TOGGLE", b"d", b"$.t"],
26479            &[b"JSON.ARRLEN", b"d", b"$.b"],
26480            &[b"JSON.OBJLEN", b"d", b"$"],
26481            &[b"JSON.OBJKEYS", b"d", b"$"],
26482            &[b"JSON.STRLEN", b"d", b"$.s"],
26483            &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
26484            &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
26485            &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
26486            &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
26487            &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
26488            &[b"JSON.ARRPOP", b"d", b"$.b"],
26489            &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
26490            &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
26491            &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
26492            &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
26493            &[b"JSON.RESP", b"d", b"$.b"],
26494            &[b"JSON.DEBUG", b"MEMORY", b"d"],
26495            &[b"JSON.CLEAR", b"d", b"$.b"],
26496            &[b"JSON.DEL", b"d", b"$.m"],
26497            &[b"JSON.FORGET", b"d", b"$.nothere"],
26498            // The two that name more than one key.
26499            &[
26500                b"JSON.MSET",
26501                b"m1",
26502                b"$",
26503                b"1",
26504                b"m2",
26505                b"$",
26506                b"2",
26507                b"m3",
26508                b"$",
26509                b"3",
26510            ],
26511            &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
26512            &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
26513            &[b"JSON.GET", b"m1", b"$"],
26514            &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
26515            &[b"JSON.GET", b"m2", b"$"],
26516            // And the errors.
26517            &[b"SET", b"plain", b"v"],
26518            &[b"JSON.GET", b"plain", b"$"],
26519            &[b"JSON.SET", b"plain", b"$", b"1"],
26520            &[b"JSON.MGET", b"m1", b"plain", b"$"],
26521            &[b"JSON.SET", b"d", b"$.b", b"["],
26522            &[b"JSON.DEL", b"plain"],
26523        ];
26524
26525        let mut one = Fixture::new();
26526        let mut many = Fixture::striped(8);
26527        for parts in script {
26528            let a = one.run(parts);
26529            let b = many.run(parts);
26530            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26531        }
26532    }
26533
26534    /// A `JSON.MSET` and a `JSON.MGET` whose keys are on several stripes.
26535    ///
26536    /// `JSON.MSET` works every triple out against the keyspace as it was before
26537    /// the command and writes nothing until all of them are known to work, so
26538    /// the thing to check is that a triple that cannot be written stops the
26539    /// ones on other stripes as well as the ones on its own.
26540    #[test]
26541    fn a_json_multi_write_across_stripes_reaches_every_key() {
26542        let mut f = Fixture::striped(8);
26543        let second = apart(&mut f, "m1");
26544        let third = apart(&mut f, &second);
26545        let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
26546
26547        assert_eq!(
26548            f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
26549            "+OK\r\n"
26550        );
26551        assert_eq!(
26552            f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
26553            "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
26554        );
26555
26556        // A value that is not JSON is refused before anything is written, and
26557        // the key on the far stripe keeps what it had.
26558        assert_eq!(
26559            f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
26560            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
26561        );
26562        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
26563
26564        // A path that names nowhere is not an error. That triple is skipped,
26565        // the ones on the other stripes are still written, and the reply is a
26566        // nil rather than OK.
26567        assert_eq!(
26568            f.run(&[
26569                b"JSON.MSET",
26570                m1,
26571                b"$",
26572                b"9",
26573                m2,
26574                b"$.deep",
26575                b"9",
26576                m3,
26577                b"$",
26578                b"7"
26579            ]),
26580            "$-1\r\n"
26581        );
26582        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
26583        assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
26584        assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
26585    }
26586
26587    /// Every geospatial command, on one stripe and on eight.
26588    #[test]
26589    fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
26590        let script: &[&[&[u8]]] = &[
26591            &[
26592                b"GEOADD",
26593                b"g",
26594                b"13.361389",
26595                b"38.115556",
26596                b"palermo",
26597                b"15.087269",
26598                b"37.502669",
26599                b"catania",
26600            ],
26601            &[
26602                b"GEOADD",
26603                b"g",
26604                b"NX",
26605                b"13.361389",
26606                b"38.115556",
26607                b"palermo",
26608            ],
26609            &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
26610            &[b"GEOPOS", b"g", b"palermo", b"nothere"],
26611            &[b"GEOHASH", b"g", b"palermo", b"catania"],
26612            &[b"GEODIST", b"g", b"palermo", b"catania"],
26613            &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
26614            &[b"GEODIST", b"g", b"palermo", b"nothere"],
26615            &[
26616                b"GEOSEARCH",
26617                b"g",
26618                b"FROMLONLAT",
26619                b"15",
26620                b"37",
26621                b"BYRADIUS",
26622                b"200",
26623                b"KM",
26624                b"ASC",
26625                b"WITHCOORD",
26626                b"WITHDIST",
26627                b"WITHHASH",
26628            ],
26629            &[
26630                b"GEOSEARCH",
26631                b"g",
26632                b"FROMMEMBER",
26633                b"palermo",
26634                b"BYBOX",
26635                b"400",
26636                b"400",
26637                b"KM",
26638                b"DESC",
26639            ],
26640            &[
26641                b"GEORADIUS",
26642                b"g",
26643                b"15",
26644                b"37",
26645                b"200",
26646                b"KM",
26647                b"COUNT",
26648                b"1",
26649            ],
26650            &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
26651            &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
26652            &[
26653                b"GEOSEARCHSTORE",
26654                b"dst",
26655                b"g",
26656                b"FROMLONLAT",
26657                b"15",
26658                b"37",
26659                b"BYRADIUS",
26660                b"200",
26661                b"KM",
26662            ],
26663            &[b"ZRANGE", b"dst", b"0", b"-1"],
26664            &[
26665                b"GEOSEARCHSTORE",
26666                b"dst",
26667                b"g",
26668                b"FROMLONLAT",
26669                b"15",
26670                b"37",
26671                b"BYRADIUS",
26672                b"1",
26673                b"M",
26674                b"STOREDIST",
26675            ],
26676            &[b"EXISTS", b"dst"],
26677            &[
26678                b"GEORADIUS",
26679                b"g",
26680                b"15",
26681                b"37",
26682                b"200",
26683                b"KM",
26684                b"STORE",
26685                b"dst",
26686            ],
26687            &[b"ZCARD", b"dst"],
26688            // And the errors.
26689            &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
26690            &[b"SET", b"plain", b"v"],
26691            &[b"GEOPOS", b"plain", b"a"],
26692            &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
26693            &[
26694                b"GEOSEARCHSTORE",
26695                b"dst",
26696                b"g",
26697                b"FROMLONLAT",
26698                b"15",
26699                b"37",
26700                b"BYRADIUS",
26701                b"200",
26702                b"KM",
26703                b"WITHCOORD",
26704            ],
26705        ];
26706
26707        let mut one = Fixture::new();
26708        let mut many = Fixture::striped(8);
26709        for parts in script {
26710            let a = one.run(parts);
26711            let b = many.run(parts);
26712            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26713        }
26714    }
26715
26716    /// A `GEOSEARCHSTORE` whose two keys are on two stripes.
26717    #[test]
26718    fn a_geo_search_store_across_stripes_writes_what_it_found() {
26719        let mut f = Fixture::striped(8);
26720        let other = apart(&mut f, "g");
26721        let third = apart(&mut f, &other);
26722        let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
26723
26724        f.run(&[
26725            b"GEOADD",
26726            g,
26727            b"13.361389",
26728            b"38.115556",
26729            b"palermo",
26730            b"15.087269",
26731            b"37.502669",
26732            b"catania",
26733        ]);
26734        assert_eq!(
26735            f.run(&[
26736                b"GEOSEARCHSTORE",
26737                dst,
26738                g,
26739                b"FROMLONLAT",
26740                b"15",
26741                b"37",
26742                b"BYRADIUS",
26743                b"200",
26744                b"KM",
26745                b"ASC",
26746            ]),
26747            ":2\r\n"
26748        );
26749        assert_eq!(
26750            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
26751            "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
26752            "the geohash is the score, so the order is not the search order"
26753        );
26754        assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
26755
26756        // `STOREDIST` stores the distance in the unit the search was asked in,
26757        // which is the destination stripe's sorted set and not the source's.
26758        assert_eq!(
26759            f.run(&[
26760                b"GEOSEARCHSTORE",
26761                dst,
26762                g,
26763                b"FROMMEMBER",
26764                b"palermo",
26765                b"BYRADIUS",
26766                b"200",
26767                b"KM",
26768                b"STOREDIST",
26769            ]),
26770            ":2\r\n"
26771        );
26772        assert_eq!(
26773            f.run(&[b"ZSCORE", dst, b"palermo"]),
26774            "$1\r\n0\r\n",
26775            "the centre is nought away from itself"
26776        );
26777
26778        // A search that found nothing deletes the destination on its own
26779        // stripe, and a source of the wrong type is refused with the
26780        // destination left alone.
26781        assert_eq!(
26782            f.run(&[
26783                b"GEOSEARCHSTORE",
26784                dst,
26785                g,
26786                b"FROMLONLAT",
26787                b"0",
26788                b"0",
26789                b"BYRADIUS",
26790                b"1",
26791                b"M",
26792            ]),
26793            ":0\r\n"
26794        );
26795        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
26796        f.run(&[
26797            b"GEOSEARCHSTORE",
26798            dst,
26799            g,
26800            b"FROMLONLAT",
26801            b"15",
26802            b"37",
26803            b"BYRADIUS",
26804            b"200",
26805            b"KM",
26806        ]);
26807        f.run(&[b"SET", plain, b"v"]);
26808        assert_eq!(
26809            f.run(&[
26810                b"GEOSEARCHSTORE",
26811                dst,
26812                plain,
26813                b"FROMLONLAT",
26814                b"15",
26815                b"37",
26816                b"BYRADIUS",
26817                b"200",
26818                b"KM",
26819            ]),
26820            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
26821        );
26822        assert_eq!(
26823            f.run(&[b"ZCARD", dst]),
26824            ":2\r\n",
26825            "and left the destination"
26826        );
26827    }
26828
26829    /// Every time series command, on one stripe and on eight.
26830    ///
26831    /// Every timestamp is written out rather than left to the clock, so the two
26832    /// servers are compared on the samples they hold and not on how long the
26833    /// test took to get from one of them to the other.
26834    #[test]
26835    fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
26836        let script: &[&[&[u8]]] = &[
26837            &[
26838                b"TS.CREATE",
26839                b"ts:a",
26840                b"LABELS",
26841                b"sensor",
26842                b"a",
26843                b"room",
26844                b"1",
26845            ],
26846            &[b"TS.CREATE", b"ts:a"],
26847            &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
26848            &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
26849            &[
26850                b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
26851            ],
26852            &[
26853                b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
26854            ],
26855            &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
26856            &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
26857            &[b"TS.GET", b"ts:a"],
26858            &[b"TS.GET", b"gone"],
26859            &[b"TS.RANGE", b"ts:a", b"-", b"+"],
26860            &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
26861            &[
26862                b"TS.RANGE",
26863                b"ts:a",
26864                b"-",
26865                b"+",
26866                b"AGGREGATION",
26867                b"avg",
26868                b"2000",
26869            ],
26870            &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
26871            &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
26872            &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
26873            &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
26874            &[b"TS.READ", b"ts:a", b"0"],
26875            &[b"TS.READ", b"ts:a", b"+"],
26876            // The filters, which are the ones that have to walk every stripe.
26877            &[b"TS.QUERYINDEX", b"sensor=a"],
26878            &[b"TS.QUERYINDEX", b"room=1"],
26879            &[b"TS.QUERYINDEX", b"room=9"],
26880            &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
26881            &[
26882                b"TS.QUERYLABELS",
26883                b"VALUES",
26884                b"sensor",
26885                b"FILTER",
26886                b"room=1",
26887            ],
26888            &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
26889            &[
26890                b"TS.MGET",
26891                b"SELECTED_LABELS",
26892                b"sensor",
26893                b"FILTER",
26894                b"sensor=a",
26895            ],
26896            &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
26897            &[
26898                b"TS.MREVRANGE",
26899                b"-",
26900                b"+",
26901                b"WITHLABELS",
26902                b"FILTER",
26903                b"sensor=a",
26904            ],
26905            &[
26906                b"TS.MRANGE",
26907                b"-",
26908                b"+",
26909                b"FILTER",
26910                b"room=1",
26911                b"GROUPBY",
26912                b"room",
26913                b"REDUCE",
26914                b"max",
26915            ],
26916            &[b"TS.INFO", b"ts:a"],
26917            // And a rule, which is the one thing here that names two keys.
26918            &[
26919                b"TS.CREATERULE",
26920                b"ts:a",
26921                b"ts:down",
26922                b"AGGREGATION",
26923                b"avg",
26924                b"1000",
26925            ],
26926            &[b"TS.CREATE", b"ts:down"],
26927            &[
26928                b"TS.CREATERULE",
26929                b"ts:a",
26930                b"ts:down",
26931                b"AGGREGATION",
26932                b"avg",
26933                b"1000",
26934            ],
26935            &[b"TS.ADD", b"ts:a", b"5000", b"4"],
26936            &[b"TS.ADD", b"ts:a", b"6000", b"5"],
26937            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
26938            &[b"TS.GET", b"ts:down", b"LATEST"],
26939            &[b"TS.INFO", b"ts:down"],
26940            &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
26941            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
26942            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
26943            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
26944            &[b"TS.DEL", b"ts:a", b"0", b"1000"],
26945            // And the errors.
26946            &[b"SET", b"plain", b"v"],
26947            &[b"TS.ADD", b"plain", b"1", b"1"],
26948            &[b"TS.GET", b"plain"],
26949            &[b"TS.READ", b"plain", b"0"],
26950            &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
26951            &[b"TS.RANGE", b"gone", b"-", b"+"],
26952            &[b"TS.INFO", b"gone"],
26953        ];
26954
26955        let mut one = Fixture::new();
26956        let mut many = Fixture::striped(8);
26957        for parts in script {
26958            let a = one.run(parts);
26959            let b = many.run(parts);
26960            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26961        }
26962    }
26963
26964    /// A compaction rule whose two ends are on two stripes.
26965    ///
26966    /// This is the one thing in the family that walks from a key to another key,
26967    /// and it walks it in both directions: a sample on the source closes a
26968    /// bucket on the destination, a `LATEST` read on the destination folds the
26969    /// bucket the source is still filling, and a delete on the source rewrites
26970    /// what the destination already held. The same script is run against a
26971    /// server one stripe wide, where the two keys share a store, and against one
26972    /// eight stripes wide, where they do not.
26973    #[test]
26974    fn a_compaction_rule_across_stripes_reaches_both_ends() {
26975        let mut many = Fixture::striped(8);
26976        let other = apart(&mut many, "src");
26977        let (src, dst) = (b"src".as_slice(), other.as_bytes());
26978        let mut one = Fixture::new();
26979        let mut both = |parts: &[&[u8]]| {
26980            let a = one.run(parts);
26981            let b = many.run(parts);
26982            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26983            a
26984        };
26985
26986        both(&[b"TS.CREATE", src]);
26987        both(&[b"TS.CREATE", dst]);
26988        assert_eq!(
26989            both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
26990            "+OK\r\n"
26991        );
26992        both(&[b"TS.ADD", src, b"1000", b"1"]);
26993        both(&[b"TS.ADD", src, b"1500", b"3"]);
26994        // The bucket the source is filling is not written down yet, and asking
26995        // for it works it out off the source.
26996        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
26997        let open = both(&[b"TS.GET", dst, b"LATEST"]);
26998        assert!(open.contains(":1000"), "the open bucket is folded: {open}");
26999
27000        // A sample past the bucket closes it, which is the write that has to
27001        // land on the other stripe.
27002        both(&[b"TS.ADD", src, b"2000", b"5"]);
27003        let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
27004        assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
27005        assert!(got.contains(":1000"), "{got}");
27006
27007        // And a delete on the source takes it away again.
27008        both(&[b"TS.DEL", src, b"1000", b"1999"]);
27009        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
27010
27011        // Both ends still know about each other, and the link comes apart from
27012        // the source.
27013        assert!(
27014            both(&[b"TS.INFO", dst]).contains("src"),
27015            "the source is named"
27016        );
27017        assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
27018        assert_eq!(
27019            both(&[b"TS.DELETERULE", src, dst]),
27020            "-ERR TSDB: compaction rule does not exist\r\n"
27021        );
27022    }
27023
27024    /// A label filter takes the series it names wherever they landed.
27025    #[test]
27026    fn a_label_query_across_stripes_finds_every_series() {
27027        let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
27028        let mut many = Fixture::striped(8);
27029        let mut homes: Vec<usize> = names
27030            .iter()
27031            .map(|name| many.server.striped(0).stripe_of(name))
27032            .collect();
27033        homes.sort_unstable();
27034        homes.dedup();
27035        assert!(homes.len() > 1, "the six keys are not all on one stripe");
27036
27037        let mut one = Fixture::new();
27038        let mut both = |parts: &[&[u8]]| {
27039            let a = one.run(parts);
27040            let b = many.run(parts);
27041            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
27042            a
27043        };
27044        for name in &names {
27045            both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
27046            both(&[b"TS.ADD", name, b"1000", b"1"]);
27047        }
27048
27049        let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
27050        assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
27051        assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
27052        assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
27053        assert_eq!(
27054            both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
27055            "*1\r\n$4\r\nroom\r\n"
27056        );
27057    }
27058
27059    /// Every hash command, and the field import beside it, on one stripe and on
27060    /// eight.
27061    ///
27062    /// `HRANDFIELD` with a count draws from the stripe's own generator and two
27063    /// stripes do not draw the same numbers, so the only draw here is off a hash
27064    /// holding one field, where every generator gives the same answer.
27065    #[test]
27066    fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
27067        let script: &[&[&[u8]]] = &[
27068            &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
27069            &[b"HMSET", b"h", b"c", b"3"],
27070            &[b"HSETNX", b"h", b"a", b"9"],
27071            &[b"HSETNX", b"h", b"d", b"4"],
27072            &[b"HGET", b"h", b"a"],
27073            &[b"HGET", b"h", b"nope"],
27074            &[b"HMGET", b"h", b"a", b"nope"],
27075            &[b"HLEN", b"h"],
27076            &[b"HEXISTS", b"h", b"a"],
27077            &[b"HSTRLEN", b"h", b"a"],
27078            &[b"HGETALL", b"h"],
27079            &[b"HKEYS", b"h"],
27080            &[b"HVALS", b"h"],
27081            &[b"HINCRBY", b"h", b"a", b"5"],
27082            &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
27083            &[b"HSCAN", b"h", b"0"],
27084            &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
27085            &[b"HSCAN", b"h", b"0", b"NOVALUES"],
27086            &[b"HDEL", b"h", b"d"],
27087            &[b"HSET", b"one", b"f", b"v"],
27088            &[b"HRANDFIELD", b"one"],
27089            &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
27090            // The field deadlines.
27091            &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
27092            &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
27093            &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
27094            &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
27095            &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
27096            &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
27097            &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
27098            &[b"HGET", b"h", b"b"],
27099            // The three that came later and word everything their own way.
27100            &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
27101            &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
27102            &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
27103            &[b"HGET", b"h", b"e"],
27104            // And the import, whose key is the third word.
27105            &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
27106            &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
27107            &[b"HGETALL", b"imp"],
27108            &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
27109            &[b"HIMPORT", b"DISCARD", b"fs"],
27110            // And the errors.
27111            &[b"SET", b"plain", b"v"],
27112            &[b"HSET", b"plain", b"a", b"1"],
27113            &[b"HGETALL", b"plain"],
27114            &[b"HGET", b"gone", b"a"],
27115            &[b"HINCRBY", b"h", b"a", b"nan"],
27116        ];
27117
27118        let mut one = Fixture::new();
27119        let mut many = Fixture::striped(8);
27120        // The field deadlines are absolute milliseconds worked out from the
27121        // clock, so both servers are put on the same one rather than left to
27122        // read the wall a moment apart.
27123        one.server.set_clock_ms(1_700_000_000_000);
27124        many.server.set_clock_ms(1_700_000_000_000);
27125        for parts in script {
27126            let a = one.run(parts);
27127            let b = many.run(parts);
27128            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
27129        }
27130    }
27131
27132    /// Every array command, on one stripe and on eight.
27133    #[test]
27134    fn the_array_group_answers_the_same_however_many_stripes_there_are() {
27135        let script: &[&[&[u8]]] = &[
27136            &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
27137            &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
27138            &[b"ARGET", b"a", b"1"],
27139            &[b"ARGET", b"a", b"99"],
27140            &[b"ARMGET", b"a", b"0", b"5", b"99"],
27141            &[b"ARGETRANGE", b"a", b"0", b"7"],
27142            &[b"ARLEN", b"a"],
27143            &[b"ARCOUNT", b"a"],
27144            &[b"ARINSERT", b"a", b"m", b"n"],
27145            &[b"ARSCAN", b"a", b"0", b"20"],
27146            &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
27147            &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
27148            &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
27149            &[b"ARLASTITEMS", b"a", b"2"],
27150            &[b"ARLASTITEMS", b"a", b"2", b"REV"],
27151            &[b"ARNEXT", b"a"],
27152            &[b"ARSEEK", b"a", b"3"],
27153            &[b"AROP", b"a", b"0", b"20", b"USED"],
27154            &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
27155            &[b"ARINFO", b"a"],
27156            &[b"ARINFO", b"a", b"FULL"],
27157            &[b"ARDEL", b"a", b"0"],
27158            &[b"ARDELRANGE", b"a", b"1", b"2"],
27159            &[b"ARCOUNT", b"a"],
27160            &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
27161            &[b"ARGETRANGE", b"r", b"0", b"9"],
27162            // And the errors.
27163            &[b"SET", b"plain", b"v"],
27164            &[b"ARGET", b"plain", b"0"],
27165            &[b"ARSET", b"plain", b"0", b"v"],
27166            &[b"ARGET", b"gone", b"0"],
27167            &[b"ARSET", b"a", b"bad", b"v"],
27168        ];
27169
27170        let mut one = Fixture::new();
27171        let mut many = Fixture::striped(8);
27172        for parts in script {
27173            let a = one.run(parts);
27174            let b = many.run(parts);
27175            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
27176        }
27177    }
27178
27179    /// Every graph and vector set command, on one stripe and on eight.
27180    ///
27181    /// `VRANDMEMBER` is not in here for the reason `HRANDFIELD` with a count is
27182    /// not: it draws from the stripe's generator, and the stripes do not share
27183    /// one.
27184    #[test]
27185    fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
27186        let script: &[&[&[u8]]] = &[
27187            &[b"G.NADD", b"g", b"n1", b"name", b"one"],
27188            &[b"G.NADD", b"g", b"n2", b"name", b"two"],
27189            &[b"G.NADD", b"g", b"n3"],
27190            &[b"G.NGET", b"g", b"n1"],
27191            &[b"G.NGET", b"g", b"gone"],
27192            &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
27193            &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
27194            &[b"G.OUT", b"g", b"n1", b"knows"],
27195            &[b"G.IN", b"g", b"n2", b"knows"],
27196            &[b"G.DEG", b"g", b"n1", b"knows"],
27197            &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
27198            &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
27199            &[b"G.PATH", b"g", b"n1", b"n3"],
27200            &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
27201            &[b"G.NDEL", b"g", b"n3"],
27202            &[b"G.NGET", b"g", b"n3"],
27203            // The vector set, which is one index under one key.
27204            &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
27205            &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
27206            &[b"VCARD", b"v"],
27207            &[b"VDIM", b"v"],
27208            &[b"VEMB", b"v", b"e1"],
27209            &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
27210            &[b"VSIM", b"v", b"ELE", b"e1"],
27211            &[b"VISMEMBER", b"v", b"e1"],
27212            &[b"VISMEMBER", b"v", b"gone"],
27213            &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
27214            &[b"VGETATTR", b"v", b"e1"],
27215            &[b"VRANGE", b"v", b"-", b"+"],
27216            &[b"VLINKS", b"v", b"e1"],
27217            &[b"VINFO", b"v"],
27218            &[b"VREM", b"v", b"e2"],
27219            &[b"VCARD", b"v"],
27220            // And the errors.
27221            &[b"SET", b"plain", b"v"],
27222            &[b"G.NGET", b"plain", b"n1"],
27223            &[b"VCARD", b"plain"],
27224            &[b"G.NADD", b"gone2", b"n"],
27225            &[b"VEMB", b"gone3", b"e"],
27226        ];
27227
27228        let mut one = Fixture::new();
27229        let mut many = Fixture::striped(8);
27230        for parts in script {
27231            let a = one.run(parts);
27232            let b = many.run(parts);
27233            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
27234        }
27235    }
27236
27237    /// Every bloom filter, cuckoo filter, count min sketch, top k and t digest
27238    /// command, on one stripe and on eight.
27239    #[test]
27240    fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
27241        let script: &[&[&[u8]]] = &[
27242            // The bloom filter.
27243            &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
27244            &[b"BF.ADD", b"bf", b"a"],
27245            &[b"BF.ADD", b"bf", b"a"],
27246            &[b"BF.MADD", b"bf", b"b", b"c"],
27247            &[b"BF.EXISTS", b"bf", b"a"],
27248            &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
27249            &[b"BF.CARD", b"bf"],
27250            &[b"BF.INFO", b"bf"],
27251            &[b"BF.INFO", b"bf", b"CAPACITY"],
27252            &[b"BF.DEBUG", b"bf"],
27253            &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
27254            &[b"BF.EXISTS", b"made", b"x"],
27255            &[b"BF.SCANDUMP", b"bf", b"0"],
27256            // The cuckoo filter.
27257            &[b"CF.RESERVE", b"cf", b"100"],
27258            &[b"CF.ADD", b"cf", b"a"],
27259            &[b"CF.ADDNX", b"cf", b"a"],
27260            &[b"CF.COUNT", b"cf", b"a"],
27261            &[b"CF.EXISTS", b"cf", b"a"],
27262            &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
27263            &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
27264            &[b"CF.DEL", b"cf", b"a"],
27265            &[b"CF.COMPACT", b"cf"],
27266            &[b"CF.INFO", b"cf"],
27267            &[b"CF.DEBUG", b"cf"],
27268            &[b"CF.SCANDUMP", b"cf", b"0"],
27269            // The count min sketch.
27270            &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
27271            &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
27272            &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
27273            &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
27274            &[b"CMS.INFO", b"cms"],
27275            // The top k sketch.
27276            &[b"TOPK.RESERVE", b"tk", b"3"],
27277            &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
27278            &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
27279            &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
27280            &[b"TOPK.COUNT", b"tk", b"a", b"c"],
27281            &[b"TOPK.LIST", b"tk"],
27282            &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
27283            &[b"TOPK.INFO", b"tk"],
27284            // The t digest.
27285            &[b"TDIGEST.CREATE", b"td"],
27286            &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
27287            &[b"TDIGEST.MIN", b"td"],
27288            &[b"TDIGEST.MAX", b"td"],
27289            &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
27290            &[b"TDIGEST.CDF", b"td", b"3"],
27291            &[b"TDIGEST.RANK", b"td", b"3"],
27292            &[b"TDIGEST.REVRANK", b"td", b"3"],
27293            &[b"TDIGEST.BYRANK", b"td", b"0"],
27294            &[b"TDIGEST.BYREVRANK", b"td", b"0"],
27295            &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
27296            &[b"TDIGEST.INFO", b"td"],
27297            &[b"TDIGEST.RESET", b"td"],
27298            &[b"TDIGEST.MIN", b"td"],
27299            // And the errors.
27300            &[b"SET", b"plain", b"v"],
27301            &[b"BF.ADD", b"plain", b"a"],
27302            &[b"CF.ADD", b"plain", b"a"],
27303            &[b"CMS.QUERY", b"plain", b"a"],
27304            &[b"TOPK.ADD", b"plain", b"a"],
27305            &[b"TDIGEST.ADD", b"plain", b"1"],
27306            &[b"CMS.INFO", b"gone"],
27307            &[b"TOPK.INFO", b"gone"],
27308            &[b"TDIGEST.INFO", b"gone"],
27309        ];
27310
27311        let mut one = Fixture::new();
27312        let mut many = Fixture::striped(8);
27313        for parts in script {
27314            let a = one.run(parts);
27315            let b = many.run(parts);
27316            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
27317        }
27318    }
27319
27320    /// The two sketch merges, with their sources on stripes of their own.
27321    ///
27322    /// These are the only two commands in the ten groups that name more than one
27323    /// key, and both read a run of sources and write a destination, so both go
27324    /// wrong in the same way if a merge holds one store and looks every source up
27325    /// in it.
27326    #[test]
27327    fn a_sketch_merge_across_stripes_reads_every_source() {
27328        let mut many = Fixture::striped(8);
27329        let other = apart(&mut many, "s1");
27330        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
27331        let mut one = Fixture::new();
27332        let mut both = |parts: &[&[u8]]| {
27333            let a = one.run(parts);
27334            let b = many.run(parts);
27335            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
27336            a
27337        };
27338
27339        // The count min sketch. The destination has to be the sources' shape,
27340        // and it is named first, so all three keys are read before anything is
27341        // written.
27342        for key in [b"cd".as_slice(), s1, s2] {
27343            both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
27344        }
27345        both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
27346        both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
27347        assert_eq!(
27348            both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
27349            "+OK\r\n",
27350            "the merge took both sources"
27351        );
27352        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
27353        // And with weights, which are read against the sources in order.
27354        both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
27355        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
27356        // A source that is not a sketch is answered before anything is written.
27357        both(&[b"SET", b"plain", b"v"]);
27358        assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
27359        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
27360
27361        // The t digest, which builds its destination and then puts it in place.
27362        // The two source keys are used again here, so what they held goes first.
27363        both(&[b"FLUSHALL"]);
27364        both(&[b"TDIGEST.CREATE", b"td"]);
27365        both(&[b"TDIGEST.CREATE", s1]);
27366        both(&[b"TDIGEST.CREATE", s2]);
27367        both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
27368        both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
27369        assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
27370        assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
27371        assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
27372    }
27373
27374    /// Every shape of `SORT`, on one stripe and on eight.
27375    ///
27376    /// The key it sorts, the keys a `BY` names, the keys a `GET` names and the
27377    /// destination are four different names and nothing lines them up, so on
27378    /// eight stripes this script is reading and writing all over the database
27379    /// while on one it is doing what it always did.
27380    #[test]
27381    fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
27382        let script: &[&[&[u8]]] = &[
27383            &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
27384            &[b"SORT", b"l"],
27385            &[b"SORT", b"l", b"DESC"],
27386            &[b"SORT", b"l", b"ALPHA"],
27387            &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
27388            &[b"SORT_RO", b"l"],
27389            // A weight per element, so the order comes off keys the command
27390            // never named.
27391            &[
27392                b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
27393            ],
27394            &[b"SORT", b"l", b"BY", b"w_*"],
27395            &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
27396            &[b"DEL", b"w_2"],
27397            &[b"SORT", b"l", b"BY", b"w_*"],
27398            // And the answer off another set of keys again, with `#` mixed in
27399            // so the rows are not all lookups.
27400            &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
27401            &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
27402            // A pattern that reaches into a hash, which is another key again.
27403            &[b"HSET", b"h_1", b"f", b"9"],
27404            &[b"HSET", b"h_2", b"f", b"8"],
27405            &[b"HSET", b"h_3", b"f", b"7"],
27406            &[b"HSET", b"h_10", b"f", b"6"],
27407            &[b"SORT", b"l", b"BY", b"h_*->f"],
27408            &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
27409            // The destination, which is a fourth place to land.
27410            &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
27411            &[b"LRANGE", b"out", b"0", b"-1"],
27412            &[b"SORT", b"l", b"STORE", b"l"],
27413            &[b"LRANGE", b"l", b"0", b"-1"],
27414            // An empty result takes the destination away rather than leaving a
27415            // list of nothing behind.
27416            &[b"SORT", b"missing", b"STORE", b"out"],
27417            &[b"EXISTS", b"out"],
27418            // A set and a sorted set sort the same way a list does, and a set
27419            // written to a destination is sorted even when nothing asked.
27420            &[b"SADD", b"s", b"c", b"a", b"b"],
27421            &[b"SORT", b"s", b"ALPHA"],
27422            &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
27423            &[b"LRANGE", b"out", b"0", b"-1"],
27424            &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
27425            &[b"SORT", b"z", b"BY", b"nosort"],
27426            &[b"SORT", b"z", b"ALPHA", b"DESC"],
27427            // And the two ways it refuses: a key of the wrong type, and an
27428            // element that is not a number under a numeric sort.
27429            &[b"SET", b"str", b"v"],
27430            &[b"SORT", b"str"],
27431            &[b"RPUSH", b"words", b"one", b"two"],
27432            &[b"SORT", b"words"],
27433            &[b"SORT_RO", b"l", b"STORE", b"out"],
27434        ];
27435
27436        let mut one = Fixture::new();
27437        let mut many = Fixture::striped(8);
27438        for parts in script {
27439            let a = one.run(parts);
27440            let b = many.run(parts);
27441            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
27442        }
27443    }
27444
27445    /// One `SORT` whose four kinds of key are on stripes of their own.
27446    ///
27447    /// The script above spreads keys around by writing enough of them, and this
27448    /// one checks the spread rather than trusting it: the list, the weight key
27449    /// for one of its elements and the destination are asserted to be in three
27450    /// places before the command runs.
27451    #[test]
27452    fn a_sort_across_stripes_reads_every_pattern_key() {
27453        let mut f = Fixture::striped(8);
27454        let out = apart(&mut f, "l");
27455        let (list, dest) = (b"l".as_slice(), out.as_bytes());
27456
27457        f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
27458        f.run(&[
27459            b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
27460        ]);
27461        f.run(&[
27462            b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
27463        ]);
27464
27465        // The weights are four keys and they are not all in one place, which is
27466        // the thing that would go unnoticed if the command held a stripe.
27467        let db = f.server.striped(0);
27468        let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
27469            .iter()
27470            .map(|k| db.stripe_of(k.as_slice()))
27471            .collect();
27472        assert!(
27473            weights.iter().any(|s| *s != weights[0]),
27474            "the four weight keys all landed on one stripe, so this proves nothing"
27475        );
27476
27477        assert_eq!(
27478            f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
27479            "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
27480            "the order came off the weights and the answer off the data keys"
27481        );
27482        assert_eq!(
27483            f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
27484            ":4\r\n"
27485        );
27486        assert_eq!(
27487            f.run(&[b"LRANGE", dest, b"0", b"-1"]),
27488            "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
27489            "the destination is on a stripe of its own and got the whole answer"
27490        );
27491    }
27492
27493    /// A `CONFIG SET` reaches every stripe, so where a key landed does not
27494    /// decide what shape it is stored in.
27495    ///
27496    /// This is the setting that would go wrong quietly. A stripe that kept the
27497    /// old ladder would hold the same hash in a different encoding from the
27498    /// stripe next to it, and the only thing that would ever say so is
27499    /// `OBJECT ENCODING`, which is why the check is on that.
27500    #[test]
27501    fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
27502        let mut f = Fixture::striped(8);
27503        let other = apart(&mut f, "h");
27504        let (first, second) = (b"h".as_slice(), other.as_bytes());
27505
27506        assert_eq!(
27507            f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
27508            "+OK\r\n"
27509        );
27510        assert_eq!(
27511            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
27512            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
27513            "the read comes off one stripe and has to answer for all of them"
27514        );
27515        for key in [first, second] {
27516            f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
27517            assert_eq!(
27518                f.run(&[b"OBJECT", b"ENCODING", key]),
27519                "$8\r\nlistpack\r\n",
27520                "two fields is still under the ladder"
27521            );
27522            f.run(&[b"HSET", key, b"c", b"3"]);
27523            assert_eq!(
27524                f.run(&[b"OBJECT", b"ENCODING", key]),
27525                "$9\r\nhashtable\r\n",
27526                "three fields is over it, on whichever stripe the key is on"
27527            );
27528        }
27529
27530        // And the policy, which every stripe has to agree about for the same
27531        // reason: an eviction draws from one stripe at a time.
27532        assert_eq!(
27533            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
27534            "+OK\r\n"
27535        );
27536        let db = f.server.striped(0);
27537        assert!(
27538            (0..db.width()).all(|i| db.hold_stripe(i).policy().name() == "allkeys-lru"),
27539            "a stripe kept the old policy"
27540        );
27541    }
27542
27543    /// What an index holds, as the two numbers `FT.INFO` reports about it.
27544    ///
27545    /// Read off the registry rather than parsed back out of an `FT.INFO` reply,
27546    /// because the reply is thirty odd fields and these two are the ones the
27547    /// keyspace hook moves.
27548    fn held(f: &Fixture, name: &[u8]) -> (usize, u32) {
27549        let search = f.server.search.lock();
27550        let index = search.named(name).expect("the index is there");
27551        (index.held.docs.len(), index.held.docs.last())
27552    }
27553
27554    /// A hash written under an index's prefix reaches it, and one written
27555    /// outside the prefix does not.
27556    #[test]
27557    fn a_hash_that_is_written_reaches_the_index_that_follows_it() {
27558        let mut f = Fixture::new();
27559        f.run(&[
27560            b"FT.CREATE",
27561            b"ix",
27562            b"PREFIX",
27563            b"1",
27564            b"p:",
27565            b"SCHEMA",
27566            b"t",
27567            b"TEXT",
27568        ]);
27569        f.run(&[b"HSET", b"p:1", b"t", b"running dogs"]);
27570        assert_eq!(held(&f, b"ix"), (1, 1));
27571        f.run(&[b"HSET", b"other:1", b"t", b"running dogs"]);
27572        assert_eq!(held(&f, b"ix"), (1, 1));
27573
27574        // Every field of the key and not the one the command named, since a
27575        // document is read from nothing every time.
27576        f.run(&[b"HSET", b"p:1", b"u", b"beta"]);
27577        f.run(&[b"HDEL", b"p:1", b"u"]);
27578        assert_eq!(held(&f, b"ix"), (1, 3));
27579        let search = f.server.search.lock();
27580        let index = search.named(b"ix").expect("there");
27581        assert_eq!(index.held.docs.id(b"p:1"), Some(3));
27582    }
27583
27584    /// A fresh index reads the keys that were already there, and walks past a
27585    /// key of the wrong type without counting a failure.
27586    #[test]
27587    fn a_fresh_index_reads_the_keys_that_were_already_there() {
27588        let mut f = Fixture::new();
27589        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27590        f.run(&[b"SET", b"p:str", b"not a hash"]);
27591        f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
27592        f.run(&[
27593            b"FT.CREATE",
27594            b"ix",
27595            b"PREFIX",
27596            b"1",
27597            b"p:",
27598            b"SCHEMA",
27599            b"t",
27600            b"TEXT",
27601        ]);
27602
27603        assert_eq!(held(&f, b"ix"), (1, 1));
27604        let search = f.server.search.lock();
27605        let index = search.named(b"ix").expect("there");
27606        assert_eq!(index.trouble.whole().failures(), 0);
27607    }
27608
27609    /// `SKIPINITIALSCAN` leaves what was there alone, and a later write to one
27610    /// of those keys still lands.
27611    #[test]
27612    fn an_index_that_skipped_the_scan_fills_up_on_the_next_write() {
27613        let mut f = Fixture::new();
27614        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27615        f.run(&[
27616            b"FT.CREATE",
27617            b"ix",
27618            b"PREFIX",
27619            b"1",
27620            b"p:",
27621            b"SKIPINITIALSCAN",
27622            b"SCHEMA",
27623            b"t",
27624            b"TEXT",
27625        ]);
27626        assert_eq!(held(&f, b"ix"), (0, 0));
27627        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27628        assert_eq!(held(&f, b"ix"), (1, 1));
27629    }
27630
27631    /// A command that changed nothing leaves the document where it was, which
27632    /// is not the same as a command that was not a write.
27633    ///
27634    /// All five of these were measured against 8.10.1. Writing the same value
27635    /// again moves the number and a deadline set for later does not, which is
27636    /// the pair that makes the rule "the fields are not what they were" rather
27637    /// than "this was a write".
27638    #[test]
27639    fn only_a_real_change_gives_the_document_a_new_number() {
27640        let mut f = Fixture::new();
27641        f.run(&[
27642            b"FT.CREATE",
27643            b"ix",
27644            b"PREFIX",
27645            b"1",
27646            b"p:",
27647            b"SCHEMA",
27648            b"t",
27649            b"TEXT",
27650        ]);
27651        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27652        assert_eq!(held(&f, b"ix"), (1, 1));
27653
27654        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27655        assert_eq!(held(&f, b"ix"), (1, 2), "the same value still rewrites");
27656
27657        for quiet in [
27658            vec![b"HSETNX".as_slice(), b"p:1", b"t", b"other"],
27659            vec![b"HDEL".as_slice(), b"p:1", b"nosuch"],
27660            vec![b"HGET".as_slice(), b"p:1", b"t"],
27661            vec![b"HGETALL".as_slice(), b"p:1"],
27662            vec![b"HEXPIRE".as_slice(), b"p:1", b"100", b"FIELDS", b"1", b"t"],
27663            vec![b"HPERSIST".as_slice(), b"p:1", b"FIELDS", b"1", b"t"],
27664            vec![
27665                b"HGETEX".as_slice(),
27666                b"p:1",
27667                b"EX",
27668                b"100",
27669                b"FIELDS",
27670                b"1",
27671                b"t",
27672            ],
27673            vec![b"HGETDEL".as_slice(), b"p:1", b"FIELDS", b"1", b"nosuch"],
27674        ] {
27675            f.run(&quiet);
27676            assert_eq!(held(&f, b"ix"), (1, 2), "{:?} moved the document", quiet[0]);
27677        }
27678
27679        // And the ones that do change something.
27680        f.run(&[b"HSET", b"p:2", b"n", b"1"]);
27681        f.run(&[b"HINCRBY", b"p:2", b"n", b"1"]);
27682        assert_eq!(held(&f, b"ix"), (2, 4));
27683        // A deadline that has already passed takes the field away, and taking
27684        // the last field away takes the key and the document with it. The
27685        // number still moves on the way past, because the field going and the
27686        // key going are two separate pieces of news and the first of them
27687        // writes the document one last time.
27688        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"n"]);
27689        assert_eq!(held(&f, b"ix"), (1, 5));
27690    }
27691
27692    /// The two ways of emptying a hash, which do not leave the same thing
27693    /// behind. `HDEL` of the last field spends no number and is counted as a
27694    /// refusal, and a deadline that has already passed spends one on a document
27695    /// nobody sees and is counted as nothing. Measured against 8.10.1 and not
27696    /// something anyone would guess.
27697    #[test]
27698    fn a_key_emptied_by_a_deadline_spends_a_number_and_one_emptied_by_hdel_does_not() {
27699        /// The index's own failure count.
27700        fn refused(f: &Fixture, name: &[u8]) -> u64 {
27701            let search = f.server.search.lock();
27702            let index = search.named(name).expect("the index is there");
27703            index.trouble.whole().failures()
27704        }
27705
27706        let mut f = Fixture::new();
27707        f.run(&[
27708            b"FT.CREATE",
27709            b"ix",
27710            b"PREFIX",
27711            b"1",
27712            b"p:",
27713            b"SCHEMA",
27714            b"t",
27715            b"TEXT",
27716        ]);
27717        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27718        assert_eq!(held(&f, b"ix"), (1, 1));
27719        f.run(&[b"HDEL", b"p:1", b"t"]);
27720        assert_eq!(
27721            held(&f, b"ix"),
27722            (0, 1),
27723            "HDEL of the last field spends none"
27724        );
27725        assert_eq!(refused(&f, b"ix"), 1, "and is counted as a refusal");
27726
27727        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
27728        assert_eq!(held(&f, b"ix"), (1, 2));
27729        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"t"]);
27730        assert_eq!(held(&f, b"ix"), (0, 3), "a deadline spends one");
27731        assert_eq!(refused(&f, b"ix"), 1, "and is counted as nothing");
27732
27733        f.run(&[b"HSET", b"p:3", b"t", b"alpha"]);
27734        assert_eq!(held(&f, b"ix"), (1, 4));
27735        f.run(&[b"HGETDEL", b"p:3", b"FIELDS", b"1", b"t"]);
27736        assert_eq!(held(&f, b"ix"), (0, 5), "and so does HGETDEL");
27737
27738        // Two fields and one command is one rewrite and not two, whichever way
27739        // the fields go.
27740        f.run(&[b"HSET", b"p:4", b"t", b"alpha", b"u", b"beta"]);
27741        assert_eq!(held(&f, b"ix"), (1, 6));
27742        f.run(&[b"HEXPIRE", b"p:4", b"0", b"FIELDS", b"2", b"t", b"u"]);
27743        assert_eq!(held(&f, b"ix"), (0, 7));
27744        assert_eq!(refused(&f, b"ix"), 1);
27745    }
27746
27747    /// `HSETEX` with a deadline that has already passed is two pieces of news
27748    /// from one command, so the number moves twice and the value never reaches
27749    /// the index.
27750    #[test]
27751    fn a_field_written_already_past_its_deadline_moves_the_number_twice() {
27752        let mut f = Fixture::new();
27753        f.run(&[
27754            b"FT.CREATE",
27755            b"ix",
27756            b"PREFIX",
27757            b"1",
27758            b"p:",
27759            b"SCHEMA",
27760            b"t",
27761            b"TEXT",
27762            b"u",
27763            b"TEXT",
27764        ]);
27765        f.run(&[b"HSET", b"p:1", b"u", b"keepme"]);
27766        assert_eq!(held(&f, b"ix"), (1, 1));
27767        f.run(&[
27768            b"HSETEX", b"p:1", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
27769        ]);
27770        assert_eq!(
27771            held(&f, b"ix"),
27772            (1, 3),
27773            "the key lived and the field did not"
27774        );
27775
27776        // And the same when the key does not survive it.
27777        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
27778        assert_eq!(held(&f, b"ix"), (2, 4));
27779        f.run(&[
27780            b"HSETEX", b"p:2", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
27781        ]);
27782        assert_eq!(held(&f, b"ix"), (1, 6));
27783    }
27784
27785    /// The number one key is indexed under, or `None` when it holds no
27786    /// document.
27787    fn number(f: &Fixture, name: &[u8], key: &[u8]) -> Option<u32> {
27788        let search = f.server.search.lock();
27789        let index = search.named(name).expect("the index is there");
27790        index.held.docs.id(key)
27791    }
27792
27793    /// An index over `p:` with one document under `p:1`, which is where four of
27794    /// the tests below start.
27795    fn indexed() -> Fixture {
27796        let mut f = Fixture::new();
27797        f.run(&[
27798            b"FT.CREATE",
27799            b"ix",
27800            b"PREFIX",
27801            b"1",
27802            b"p:",
27803            b"SCHEMA",
27804            b"t",
27805            b"TEXT",
27806        ]);
27807        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27808        f
27809    }
27810
27811    /// Every way a keyspace command takes a key away leaves no document behind,
27812    /// and none of them spends a number or is counted as a refusal.
27813    #[test]
27814    fn a_key_a_keyspace_command_takes_away_loses_its_document() {
27815        for take in [
27816            vec![b"DEL".as_slice(), b"p:1"],
27817            vec![b"UNLINK".as_slice(), b"p:1"],
27818            vec![b"PEXPIREAT".as_slice(), b"p:1", b"1"],
27819            vec![b"EXPIRE".as_slice(), b"p:1", b"-1"],
27820        ] {
27821            let mut f = indexed();
27822            assert_eq!(held(&f, b"ix"), (1, 1));
27823            f.run(&take);
27824            assert_eq!(held(&f, b"ix"), (0, 1), "{:?} left something", take[0]);
27825            let search = f.server.search.lock();
27826            let index = search.named(b"ix").expect("the index is there");
27827            assert_eq!(index.trouble.whole().failures(), 0, "{:?}", take[0]);
27828        }
27829
27830        // A deadline that has not passed yet is not one of them.
27831        let mut f = indexed();
27832        f.run(&[b"EXPIRE", b"p:1", b"1000"]);
27833        assert_eq!(held(&f, b"ix"), (1, 1));
27834        f.run(&[b"PERSIST", b"p:1"]);
27835        assert_eq!(held(&f, b"ix"), (1, 1));
27836    }
27837
27838    /// A rename inside the prefix keeps the number the document had, which is
27839    /// the one write on a followed key that does not spend one. Out of the
27840    /// prefix is an erase and into it is a fresh reading, both measured.
27841    #[test]
27842    fn a_rename_inside_the_prefix_keeps_the_number_the_document_had() {
27843        let mut f = indexed();
27844        f.run(&[b"RENAME", b"p:1", b"p:2"]);
27845        assert_eq!(held(&f, b"ix"), (1, 1), "nothing was read again");
27846        assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
27847        assert_eq!(number(&f, b"ix", b"p:1"), None);
27848
27849        f.run(&[b"RENAME", b"p:2", b"q:1"]);
27850        assert_eq!(held(&f, b"ix"), (0, 1), "out of the prefix is an erase");
27851
27852        f.run(&[b"RENAME", b"q:1", b"p:3"]);
27853        assert_eq!(held(&f, b"ix"), (1, 2), "and into it is a reading");
27854        assert_eq!(number(&f, b"ix", b"p:3"), Some(2));
27855
27856        // `RENAMENX` goes the same way, and the one that answers zero changes
27857        // nothing.
27858        f.run(&[b"HSET", b"p:4", b"t", b"beta"]);
27859        assert_eq!(f.run(&[b"RENAMENX", b"p:3", b"p:4"]), ":0\r\n");
27860        assert_eq!(held(&f, b"ix"), (2, 3));
27861        f.run(&[b"RENAMENX", b"p:3", b"p:5"]);
27862        assert_eq!(number(&f, b"ix", b"p:5"), Some(2));
27863    }
27864
27865    /// A rename over a key that already had a document leaves one document and
27866    /// not two. A real server leaves both, and D-64 is that difference.
27867    #[test]
27868    fn a_rename_over_a_document_leaves_one_of_them() {
27869        let mut f = indexed();
27870        f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
27871        assert_eq!(held(&f, b"ix"), (2, 2));
27872        f.run(&[b"RENAME", b"p:1", b"p:2"]);
27873        assert_eq!(held(&f, b"ix"), (1, 2));
27874        assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
27875    }
27876
27877    /// A key that arrives under the prefix by being copied or restored is read
27878    /// as a new document, and one that is written over by something that is not
27879    /// a hash is erased without a word.
27880    #[test]
27881    fn a_key_that_arrives_under_the_prefix_is_read_and_one_overwritten_is_erased() {
27882        let mut f = indexed();
27883        f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
27884        f.run(&[b"COPY", b"q:1", b"p:2"]);
27885        assert_eq!(held(&f, b"ix"), (2, 2));
27886        assert_eq!(number(&f, b"ix", b"p:2"), Some(2));
27887
27888        // Out of the prefix, where the source keeps the document it had.
27889        f.run(&[b"COPY", b"p:1", b"q:2"]);
27890        assert_eq!(held(&f, b"ix"), (2, 2));
27891
27892        // Over a key that has one, which is a new reading and not a rename.
27893        f.run(&[b"COPY", b"q:1", b"p:1", b"REPLACE"]);
27894        assert_eq!(held(&f, b"ix"), (2, 3));
27895        assert_eq!(number(&f, b"ix", b"p:1"), Some(3));
27896
27897        // And a string landing on top of a document takes it away, spending no
27898        // number and counting no failure.
27899        f.run(&[b"SET", b"s:1", b"plain"]);
27900        f.run(&[b"COPY", b"s:1", b"p:1", b"REPLACE"]);
27901        assert_eq!(held(&f, b"ix"), (1, 3));
27902        let dump = f.run(&[b"DUMP", b"q:1"]);
27903        assert!(dump.starts_with('$'), "{dump}");
27904    }
27905
27906    /// The keyspace group reads a key back on database zero whatever database
27907    /// the command ran on, which is measured and is not what the hash commands
27908    /// do. A `COPY` into another database indexes nothing and takes away
27909    /// whatever the destination had, and a `RESTORE` anywhere else is invisible.
27910    #[test]
27911    fn the_keyspace_group_reads_database_zero_whatever_database_it_ran_on() {
27912        let mut f = indexed();
27913        f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
27914        assert_eq!(held(&f, b"ix"), (2, 2));
27915        // Into database one, so the indexes look for `p:2` on database zero,
27916        // find the one that is still there and read it again.
27917        f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
27918        assert_eq!(held(&f, b"ix"), (2, 3));
27919        // And with nothing under that name on database zero, the copy leaves
27920        // the index one document lighter than it found it.
27921        f.run(&[b"DEL", b"p:2"]);
27922        assert_eq!(held(&f, b"ix"), (1, 3));
27923        f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
27924        assert_eq!(held(&f, b"ix"), (1, 3), "the copy landed out of sight");
27925
27926        // A restore on another database is the same story.
27927        let dump = f.run(&[b"DUMP", b"p:1"]);
27928        assert!(dump.starts_with('$'), "{dump}");
27929        f.run(&[b"SELECT", b"1"]);
27930        f.run(&[b"HSET", b"q:1", b"t", b"gamma"]);
27931        f.run(&[b"RENAME", b"q:1", b"p:3"]);
27932        assert_eq!(held(&f, b"ix"), (1, 3), "and so is a rename");
27933    }
27934
27935    /// `MOVE` is not a change at all, because an index follows a key by name
27936    /// and a write on any database still reaches it.
27937    #[test]
27938    fn a_move_leaves_the_document_where_it_is() {
27939        let mut f = indexed();
27940        f.run(&[b"MOVE", b"p:1", b"1"]);
27941        assert_eq!(held(&f, b"ix"), (1, 1), "the key moved and nothing else");
27942        assert_eq!(number(&f, b"ix", b"p:1"), Some(1));
27943
27944        f.run(&[b"SELECT", b"1"]);
27945        f.run(&[b"HSET", b"p:1", b"t", b"beta"]);
27946        assert_eq!(held(&f, b"ix"), (1, 2), "and a write there still lands");
27947        f.run(&[b"DEL", b"p:1"]);
27948        assert_eq!(held(&f, b"ix"), (0, 2));
27949    }
27950
27951    /// A flush takes every index with it, whichever database it flushed.
27952    #[test]
27953    fn a_flush_drops_the_indexes() {
27954        for flush in [b"FLUSHALL".as_slice(), b"FLUSHDB"] {
27955            let mut f = indexed();
27956            f.run(&[flush]);
27957            assert!(f.server.search.lock().is_empty(), "{flush:?} kept an index");
27958            assert_eq!(f.run(&[b"FT._LIST"]), "*0\r\n");
27959        }
27960
27961        // Even on a database no index ever read, which is what a real server
27962        // does and is not what anyone would guess.
27963        let mut f = indexed();
27964        f.run(&[b"SELECT", b"9"]);
27965        f.run(&[b"FLUSHDB"]);
27966        assert!(f.server.search.lock().is_empty());
27967    }
27968
27969    /// An index whose schema has one tag field of each kind, plus a number so
27970    /// there is something for `FT.TAGVALS` to refuse.
27971    fn tagged() -> Fixture {
27972        let mut f = Fixture::new();
27973        f.run(&[
27974            b"FT.CREATE",
27975            b"tv",
27976            b"PREFIX",
27977            b"1",
27978            b"tv:",
27979            b"SCHEMA",
27980            b"g",
27981            b"AS",
27982            b"gg",
27983            b"TAG",
27984            b"h",
27985            b"TAG",
27986            b"SEPARATOR",
27987            b"|",
27988            b"CASESENSITIVE",
27989            b"n",
27990            b"NUMERIC",
27991        ]);
27992        f.run(&[
27993            b"HSET",
27994            b"tv:1",
27995            b"g",
27996            b"Red, BLUE ",
27997            b"h",
27998            b"Aa|bB",
27999            b"n",
28000            b"1",
28001        ]);
28002        f.run(&[b"HSET", b"tv:2", b"g", b"red", b"h", b"aa", b"n", b"2"]);
28003        f
28004    }
28005
28006    /// The values come back as they are stored, so an ordinary tag field
28007    /// answers them folded and trimmed and a `CASESENSITIVE` one answers what
28008    /// it was given. Byte order either way, which puts the capital first.
28009    #[test]
28010    fn tag_values_come_back_as_they_are_stored_and_sorted_by_their_bytes() {
28011        let mut f = tagged();
28012        assert_eq!(
28013            f.run(&[b"FT.TAGVALS", b"tv", b"gg"]),
28014            "*2\r\n$4\r\nblue\r\n$3\r\nred\r\n"
28015        );
28016        assert_eq!(
28017            f.run(&[b"FT.TAGVALS", b"tv", b"h"]),
28018            "*3\r\n$2\r\nAa\r\n$2\r\naa\r\n$2\r\nbB\r\n"
28019        );
28020    }
28021
28022    /// The name asked about is the attribute, so the identifier of a field
28023    /// declared `AS` is not a name this knows.
28024    #[test]
28025    fn tag_values_are_asked_for_by_the_attribute_and_not_the_identifier() {
28026        let mut f = tagged();
28027        for (name, want) in [
28028            (b"g".as_slice(), "-SEARCH_ATTR_BAD No such field\r\n"),
28029            (b"zz", "-SEARCH_ATTR_BAD No such field\r\n"),
28030            (b"n", "-SEARCH_ATTR_BAD Not a tag field\r\n"),
28031        ] {
28032            assert_eq!(f.run(&[b"FT.TAGVALS", b"tv", name]), want);
28033        }
28034        assert_eq!(
28035            f.run(&[b"FT.TAGVALS", b"nope", b"g"]),
28036            "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n"
28037        );
28038    }
28039
28040    /// Looking up the index counts as a use of it on the roads that refuse the
28041    /// field as well as on the one that answers, which is measured.
28042    #[test]
28043    fn asking_for_tag_values_counts_a_use_of_the_index() {
28044        let mut f = tagged();
28045        let uses = |f: &mut Fixture| {
28046            let reply = f.run(&[b"FT.INFO", b"tv"]);
28047            let at = reply.find("number_of_uses").expect("the field is reported");
28048            let value = reply[at..].split("\r\n").nth(1).unwrap();
28049            value.trim_start_matches(':').parse::<i64>().unwrap()
28050        };
28051        let before = uses(&mut f);
28052        f.run(&[b"FT.TAGVALS", b"tv", b"gg"]);
28053        f.run(&[b"FT.TAGVALS", b"tv", b"zz"]);
28054        // Three more than before: two tag lookups and the second `FT.INFO`.
28055        assert_eq!(uses(&mut f), before + 3);
28056    }
28057
28058    /// A tag field nothing was ever written to has no list at all, which
28059    /// answers the same empty set a list that has been emptied does.
28060    #[test]
28061    fn a_tag_field_with_nothing_in_it_answers_empty() {
28062        let mut f = Fixture::new();
28063        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"g", b"TAG"]);
28064        assert_eq!(f.run(&[b"FT.TAGVALS", b"e", b"g"]), "*0\r\n");
28065    }
28066
28067    /// A dictionary is module state and not a key, so nothing in the keyspace
28068    /// can see one.
28069    #[test]
28070    fn a_dictionary_is_not_a_key() {
28071        let mut f = Fixture::new();
28072        assert_eq!(f.run(&[b"FT.DICTADD", b"d", b"a", b"b"]), ":2\r\n");
28073        assert_eq!(f.run(&[b"TYPE", b"d"]), "+none\r\n");
28074        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
28075        assert_eq!(f.run(&[b"KEYS", b"d"]), "*0\r\n");
28076    }
28077
28078    /// The count is how many terms were new, an empty term is not a term, and
28079    /// the dump is sorted by bytes rather than folded.
28080    #[test]
28081    fn a_dictionary_counts_the_terms_it_had_not_seen() {
28082        let mut f = Fixture::new();
28083        assert_eq!(
28084            f.run(&[b"FT.DICTADD", b"d", b"zeta", b"alpha", b"Beta", b"alpha"]),
28085            ":3\r\n"
28086        );
28087        assert_eq!(f.run(&[b"FT.DICTADD", b"d", b"alpha"]), ":0\r\n");
28088        assert_eq!(f.run(&[b"FT.DICTADD", b"d", b""]), ":0\r\n");
28089        assert_eq!(
28090            f.run(&[b"FT.DICTDUMP", b"d"]),
28091            "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nzeta\r\n"
28092        );
28093        assert_eq!(f.run(&[b"FT.DICTDEL", b"d", b"alpha", b"nope"]), ":1\r\n");
28094    }
28095
28096    /// A name nobody ever added to is not an error on either of the two
28097    /// commands that will take one, which is the only place in the group where
28098    /// a missing name is forgiven.
28099    #[test]
28100    fn a_dictionary_nobody_made_dumps_empty_rather_than_failing() {
28101        let mut f = Fixture::new();
28102        assert_eq!(f.run(&[b"FT.DICTDUMP", b"nope"]), "*0\r\n");
28103        assert_eq!(f.run(&[b"FT.DICTDEL", b"nope", b"a"]), ":0\r\n");
28104    }
28105
28106    /// The dictionaries go when the keyspace does, the same way the indexes do.
28107    #[test]
28108    fn a_flush_drops_the_dictionaries() {
28109        for flush in [b"FLUSHALL".as_slice(), b"FLUSHDB"] {
28110            let mut f = Fixture::new();
28111            f.run(&[b"FT.DICTADD", b"d", b"a"]);
28112            f.run(&[flush]);
28113            assert_eq!(f.run(&[b"FT.DICTDUMP", b"d"]), "*0\r\n", "{flush:?}");
28114        }
28115    }
28116
28117    // -------------------------------------------------------------- profile
28118
28119    /// A fixture holding one index over three documents, two of which hold the
28120    /// first word and two the second.
28121    fn profiling() -> Fixture {
28122        let mut f = Fixture::new();
28123        f.run(&[
28124            b"FT.CREATE",
28125            b"ix",
28126            b"PREFIX",
28127            b"1",
28128            b"p:",
28129            b"SCHEMA",
28130            b"t",
28131            b"TEXT",
28132            b"n",
28133            b"NUMERIC",
28134        ]);
28135        f.run(&[b"HSET", b"p:1", b"t", b"alpha", b"n", b"1"]);
28136        f.run(&[b"HSET", b"p:2", b"t", b"alpha beta", b"n", b"2"]);
28137        f.run(&[b"HSET", b"p:3", b"t", b"beta", b"n", b"3"]);
28138        f
28139    }
28140
28141    /// The reply with every time taken out of it, since no two runs agree on
28142    /// those and everything else about a profile is exact.
28143    fn timeless(reply: &str) -> String {
28144        const KEYS: &[&str] = &[
28145            "+Total profile time",
28146            "+Parsing time",
28147            "+Workers queue time",
28148            "+Pipeline creation time",
28149            "+Time",
28150        ];
28151        let mut out = String::new();
28152        let mut parts = reply.split("\r\n").peekable();
28153        while let Some(part) = parts.next() {
28154            out.push_str(part);
28155            out.push_str("\r\n");
28156            if !KEYS.contains(&part) {
28157                continue;
28158            }
28159            // A double is one line on RESP3 and a bulk header and its digits on
28160            // RESP2, and both of them stand for the same one value.
28161            match parts.next() {
28162                Some(head) if head.starts_with('$') => {
28163                    parts.next();
28164                }
28165                _ => {}
28166            }
28167            out.push_str("<t>\r\n");
28168        }
28169        // The split leaves an empty piece past the last line ending.
28170        out.truncate(out.len() - 2);
28171        out
28172    }
28173
28174    /// The whole envelope on both protocols, which is a two element array on
28175    /// one and a two key map on the other.
28176    #[test]
28177    fn a_profile_wraps_the_reply_it_would_have_answered_anyway() {
28178        let mut f = profiling();
28179        assert_eq!(
28180            timeless(&f.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY", b"alpha"])),
28181            "*2\r\n\
28182             *5\r\n:2\r\n$3\r\np:1\r\n*4\r\n$1\r\nt\r\n$5\r\nalpha\r\n$1\r\nn\r\n$1\r\n1\r\n\
28183             $3\r\np:2\r\n*4\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n$1\r\nn\r\n$1\r\n2\r\n\
28184             *4\r\n+Shards\r\n*1\r\n*14\r\n\
28185             +Total profile time\r\n<t>\r\n+Parsing time\r\n<t>\r\n\
28186             +Workers queue time\r\n<t>\r\n+Pipeline creation time\r\n<t>\r\n\
28187             +Warning\r\n*1\r\n+None\r\n\
28188             +Iterators profile\r\n*10\r\n+Type\r\n+TEXT\r\n+Term\r\n$5\r\nalpha\r\n\
28189             +Time\r\n<t>\r\n+Number of reading operations\r\n:2\r\n\
28190             +Estimated number of matches\r\n:2\r\n\
28191             +Result processors profile\r\n*4\r\n\
28192             *6\r\n+Type\r\n+Index\r\n+Time\r\n<t>\r\n+Results processed\r\n:2\r\n\
28193             *6\r\n+Type\r\n+Scorer\r\n+Time\r\n<t>\r\n+Results processed\r\n:2\r\n\
28194             *6\r\n+Type\r\n+Sorter\r\n+Time\r\n<t>\r\n+Results processed\r\n:2\r\n\
28195             *6\r\n+Type\r\n+Loader\r\n+Time\r\n<t>\r\n+Results processed\r\n:2\r\n\
28196             +Coordinator\r\n*0\r\n"
28197        );
28198        let mut g = profiling();
28199        g.run(&[b"HELLO", b"3"]);
28200        let three = timeless(&g.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY", b"alpha"]));
28201        assert!(three.starts_with("%2\r\n+Results\r\n"), "{three}");
28202        assert!(
28203            three.contains("+Profile\r\n%2\r\n+Shards\r\n*1\r\n%7\r\n"),
28204            "{three}"
28205        );
28206        assert!(three.ends_with("+Coordinator\r\n%0\r\n"), "{three}");
28207        assert!(
28208            three.contains(
28209                "+Iterators profile\r\n%5\r\n+Type\r\n+TEXT\r\n+Term\r\n$5\r\nalpha\r\n\
28210                 +Time\r\n<t>\r\n+Number of reading operations\r\n:2\r\n\
28211                 +Estimated number of matches\r\n:2\r\n"
28212            ),
28213            "{three}"
28214        );
28215    }
28216
28217    /// Every kind of step names itself, and the three that hold other steps say
28218    /// so in the singular or the plural depending on how many they hold.
28219    #[test]
28220    fn each_kind_of_step_writes_the_keys_that_belong_to_it() {
28221        let mut f = profiling();
28222        let tree = |f: &mut Fixture, query: &[u8]| {
28223            let reply = timeless(&f.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY", query]));
28224            let at = reply.find("+Iterators profile").expect("a tree");
28225            let end = reply.find("+Result processors").expect("a list of steps");
28226            reply[at..end].to_string()
28227        };
28228        assert_eq!(
28229            tree(&mut f, b"alpha beta"),
28230            "+Iterators profile\r\n*8\r\n+Type\r\n+INTERSECT\r\n+Time\r\n<t>\r\n\
28231             +Number of reading operations\r\n:1\r\n+Child iterators\r\n*2\r\n\
28232             *10\r\n+Type\r\n+TEXT\r\n+Term\r\n$5\r\nalpha\r\n+Time\r\n<t>\r\n\
28233             +Number of reading operations\r\n:2\r\n+Estimated number of matches\r\n:2\r\n\
28234             *10\r\n+Type\r\n+TEXT\r\n+Term\r\n$4\r\nbeta\r\n+Time\r\n<t>\r\n\
28235             +Number of reading operations\r\n:1\r\n+Estimated number of matches\r\n:2\r\n"
28236        );
28237        assert!(tree(&mut f, b"alpha|beta").starts_with(
28238            "+Iterators profile\r\n*10\r\n+Type\r\n+UNION\r\n+Query type\r\n+UNION\r\n\
28239             +Time\r\n<t>\r\n+Number of reading operations\r\n:3\r\n+Child iterators\r\n*2\r\n"
28240        ));
28241        // One thing under it, named in the singular, which is a different key
28242        // and not a list holding one.
28243        assert!(tree(&mut f, b"-alpha").starts_with(
28244            "+Iterators profile\r\n*8\r\n+Type\r\n+NOT\r\n+Time\r\n<t>\r\n\
28245             +Number of reading operations\r\n:1\r\n+Child iterator\r\n*10\r\n"
28246        ));
28247        assert!(tree(&mut f, b"~alpha").starts_with(
28248            "+Iterators profile\r\n*8\r\n+Type\r\n+OPTIONAL\r\n+Time\r\n<t>\r\n\
28249             +Number of reading operations\r\n:3\r\n+Child iterator\r\n*10\r\n"
28250        ));
28251        // No guess at how many, which is the one leaf that leaves it off.
28252        assert_eq!(
28253            tree(&mut f, b"*"),
28254            "+Iterators profile\r\n*6\r\n+Type\r\n+WILDCARD\r\n+Time\r\n<t>\r\n\
28255             +Number of reading operations\r\n:3\r\n"
28256        );
28257        assert!(tree(&mut f, b"@n:[1 2]").starts_with(
28258            "+Iterators profile\r\n*10\r\n+Type\r\n+NUMERIC\r\n+Term\r\n\
28259             $19\r\n1.000000 - 2.000000\r\n"
28260        ));
28261    }
28262
28263    /// A union an expansion made folds into a count of its branches and a union
28264    /// a client wrote with a bar does not.
28265    #[test]
28266    fn limited_folds_the_branches_an_expansion_made_and_leaves_a_bar_alone() {
28267        let mut f = profiling();
28268        f.run(&[b"HSET", b"p:4", b"t", b"alps"]);
28269        let tree = |f: &mut Fixture, words: &[&[u8]]| {
28270            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"ix", b"SEARCH"];
28271            argv.extend_from_slice(words);
28272            let reply = timeless(&f.run(&argv));
28273            let at = reply.find("+Iterators profile").expect("a tree");
28274            let end = reply.find("+Result processors").expect("a list of steps");
28275            reply[at..end].to_string()
28276        };
28277        assert_eq!(
28278            tree(&mut f, &[b"LIMITED", b"QUERY", b"al*"]),
28279            "+Iterators profile\r\n*10\r\n+Type\r\n+UNION\r\n\
28280             +Query type\r\n$11\r\nPREFIX - al\r\n+Time\r\n<t>\r\n\
28281             +Number of reading operations\r\n:3\r\n+Child iterators\r\n\
28282             +The number of iterators in the union is 2\r\n"
28283        );
28284        assert!(tree(&mut f, &[b"QUERY", b"al*"]).contains("+Child iterators\r\n*2\r\n"));
28285        assert!(
28286            tree(&mut f, &[b"LIMITED", b"QUERY", b"alpha|beta"])
28287                .contains("+Child iterators\r\n*2\r\n")
28288        );
28289        // A union that says nothing but its own name says it as a status, and
28290        // one that says what it stood for says that as a string. Measured, and
28291        // it is the one place in this reply where the two are told apart.
28292        assert!(tree(&mut f, &[b"QUERY", b"alpha|beta"]).contains("+Query type\r\n+UNION\r\n"));
28293        assert!(
28294            tree(&mut f, &[b"QUERY", b"al*"]).contains("+Query type\r\n$11\r\nPREFIX - al\r\n")
28295        );
28296    }
28297
28298    /// Which steps a search runs the rows through, which turns on the window,
28299    /// on whether anything asked for the fields and on what the order is.
28300    #[test]
28301    fn the_steps_a_search_runs_depend_on_what_was_asked_for() {
28302        let mut f = profiling();
28303        let steps = |f: &mut Fixture, words: &[&[u8]]| {
28304            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY", b"alpha"];
28305            argv.extend_from_slice(words);
28306            let reply = timeless(&f.run(&argv));
28307            let at = reply.find("+Result processors").expect("a list of steps");
28308            let end = reply.find("+Coordinator").expect("an end");
28309            let mut out = Vec::new();
28310            let mut parts = reply[at..end].split("\r\n").peekable();
28311            while let Some(part) = parts.next() {
28312                if part == "+Type" {
28313                    out.push(parts.next().unwrap_or_default().to_string());
28314                }
28315            }
28316            out
28317        };
28318        assert_eq!(
28319            steps(&mut f, &[]),
28320            ["+Index", "+Scorer", "+Sorter", "+Loader"]
28321        );
28322        assert_eq!(
28323            steps(&mut f, &[b"NOCONTENT"]),
28324            ["+Index", "+Scorer", "+Sorter"]
28325        );
28326        // A window of nothing is a client asking for the total and nothing
28327        // else, so nothing is scored and nothing is sorted.
28328        assert_eq!(
28329            steps(&mut f, &[b"LIMIT", b"0", b"0"]),
28330            ["+Index", "+Counter"]
28331        );
28332        // A sort by a field does not need a score, and asking for the scores
28333        // puts the step back.
28334        assert_eq!(
28335            steps(&mut f, &[b"SORTBY", b"n"]),
28336            ["+Index", "+Sorter", "+Loader"]
28337        );
28338        assert_eq!(
28339            steps(&mut f, &[b"SORTBY", b"n", b"WITHSCORES"]),
28340            ["+Index", "+Scorer", "+Sorter", "+Loader"]
28341        );
28342        assert_eq!(
28343            steps(&mut f, &[b"HIGHLIGHT"]),
28344            ["+Index", "+Scorer", "+Sorter", "+Loader", "+Highlighter"]
28345        );
28346        assert_eq!(
28347            steps(&mut f, &[b"SUMMARIZE", b"NOCONTENT"]),
28348            ["+Index", "+Scorer", "+Sorter"]
28349        );
28350    }
28351
28352    /// A pipeline names each of its steps after the expression it runs, which
28353    /// is what a real server prints beside them.
28354    #[test]
28355    fn a_pipeline_names_every_step_after_what_it_runs() {
28356        let mut f = profiling();
28357        let steps = |f: &mut Fixture, words: &[&[u8]]| {
28358            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"ix", b"AGGREGATE", b"QUERY", b"*"];
28359            argv.extend_from_slice(words);
28360            let reply = timeless(&f.run(&argv));
28361            let at = reply.find("+Result processors").expect("a list of steps");
28362            let end = reply.find("+Coordinator").expect("an end");
28363            let mut out = Vec::new();
28364            let mut parts = reply[at..end].split("\r\n").peekable();
28365            while let Some(part) = parts.next() {
28366                if part == "+Type" {
28367                    out.push(parts.next().unwrap_or_default().to_string());
28368                }
28369            }
28370            out
28371        };
28372        assert_eq!(steps(&mut f, &[]), ["+Index"]);
28373        assert_eq!(
28374            steps(&mut f, &[b"APPLY", b"1", b"AS", b"one"]),
28375            ["+Index", "+Projector - Literal 1"]
28376        );
28377        assert_eq!(
28378            steps(
28379                &mut f,
28380                &[b"LOAD", b"1", b"@n", b"APPLY", b"@n * 2", b"AS", b"d"]
28381            ),
28382            ["+Index", "+Loader", "+Projector - Operator *"]
28383        );
28384        assert_eq!(
28385            steps(&mut f, &[b"LOAD", b"1", b"@n", b"FILTER", b"@n > 1"]),
28386            ["+Index", "+Loader", "+Filter - Predicate >"]
28387        );
28388        assert_eq!(
28389            steps(
28390                &mut f,
28391                &[b"GROUPBY", b"1", b"@n", b"REDUCE", b"COUNT", b"0"]
28392            ),
28393            ["+Index", "+Loader", "+Grouper"]
28394        );
28395        assert_eq!(
28396            steps(&mut f, &[b"SORTBY", b"1", b"@n"]),
28397            ["+Index", "+Loader", "+Sorter"]
28398        );
28399        assert_eq!(
28400            steps(&mut f, &[b"LIMIT", b"0", b"2"]),
28401            ["+Index", "+Pager/Limiter"]
28402        );
28403        // Asking for the score by name is a step of its own, and it goes in
28404        // front of the read rather than after it.
28405        assert_eq!(
28406            steps(
28407                &mut f,
28408                &[
28409                    b"ADDSCORES",
28410                    b"LOAD",
28411                    b"1",
28412                    b"@n",
28413                    b"APPLY",
28414                    b"@__score",
28415                    b"AS",
28416                    b"s"
28417                ]
28418            ),
28419            [
28420                "+Index",
28421                "+Scorer",
28422                "+Loader",
28423                "+Projector - Property __score"
28424            ]
28425        );
28426    }
28427
28428    /// A field the schema marked sortable is held beside the document number,
28429    /// so a pipeline that only names those never opens a key and never reports
28430    /// a read.
28431    ///
28432    /// Measured: on a schema of `n NUMERIC SORTABLE g TAG`, `LOAD 1 @n` has no
28433    /// `Loader` step and `LOAD 1 @g` has one. So does `LOAD *`, because what a
28434    /// key turns out to hold is not knowable without opening it.
28435    #[test]
28436    fn a_sortable_field_is_read_without_the_key_being_opened() {
28437        let mut f = Fixture::new();
28438        f.run(&[
28439            b"FT.CREATE",
28440            b"sx",
28441            b"PREFIX",
28442            b"1",
28443            b"s:",
28444            b"SCHEMA",
28445            b"n",
28446            b"NUMERIC",
28447            b"SORTABLE",
28448            b"g",
28449            b"TAG",
28450        ]);
28451        f.run(&[b"HSET", b"s:1", b"n", b"1", b"g", b"one"]);
28452        f.run(&[b"HSET", b"s:2", b"n", b"2", b"g", b"two"]);
28453        let loads = |f: &mut Fixture, words: &[&[u8]]| {
28454            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"sx", b"AGGREGATE", b"QUERY", b"*"];
28455            argv.extend_from_slice(words);
28456            f.run(&argv).contains("+Loader")
28457        };
28458        assert!(!loads(&mut f, &[b"LOAD", b"1", b"@n"]));
28459        assert!(!loads(&mut f, &[b"SORTBY", b"1", b"@n"]));
28460        assert!(!loads(&mut f, &[b"APPLY", b"@n * 2", b"AS", b"d"]));
28461        assert!(loads(&mut f, &[b"LOAD", b"1", b"@g"]));
28462        assert!(loads(&mut f, &[b"LOAD", b"2", b"@n", b"@g"]));
28463        assert!(loads(
28464            &mut f,
28465            &[b"GROUPBY", b"1", b"@g", b"REDUCE", b"COUNT", b"0"]
28466        ));
28467        assert!(loads(&mut f, &[b"LOAD", b"*"]));
28468    }
28469
28470    /// The four ways the words can be wrong, none of which reaches the search
28471    /// underneath.
28472    #[test]
28473    fn a_profile_checks_its_own_words_before_it_runs_anything() {
28474        let mut f = profiling();
28475        assert_eq!(
28476            f.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY"]),
28477            "-ERR wrong number of arguments for 'FT.PROFILE' command\r\n"
28478        );
28479        assert_eq!(
28480            f.run(&[b"FT.PROFILE", b"ix", b"BOGUS", b"QUERY", b"alpha"]),
28481            "-No `SEARCH`, `AGGREGATE`, or `HYBRID` provided\r\n"
28482        );
28483        // The word goes between the two and nowhere else, so one written in
28484        // front of them is not the word at all.
28485        assert_eq!(
28486            f.run(&[
28487                b"FT.PROFILE",
28488                b"ix",
28489                b"LIMITED",
28490                b"SEARCH",
28491                b"QUERY",
28492                b"alpha"
28493            ]),
28494            "-No `SEARCH`, `AGGREGATE`, or `HYBRID` provided\r\n"
28495        );
28496        assert_eq!(
28497            f.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"BOGUS", b"alpha"]),
28498            "-The QUERY keyword is expected\r\n"
28499        );
28500        assert_eq!(
28501            f.run(&[
28502                b"FT.PROFILE",
28503                b"ix",
28504                b"AGGREGATE",
28505                b"QUERY",
28506                b"alpha",
28507                b"WITHCURSOR"
28508            ]),
28509            "-FT.PROFILE does not support cursor\r\n"
28510        );
28511        // And what the search itself complains about comes back on its own,
28512        // without an envelope around it saying the command worked.
28513        assert_eq!(
28514            f.run(&[b"FT.PROFILE", b"nope", b"SEARCH", b"QUERY", b"alpha"]),
28515            "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n"
28516        );
28517        assert_eq!(
28518            f.run(&[
28519                b"FT.PROFILE",
28520                b"ix",
28521                b"SEARCH",
28522                b"QUERY",
28523                b"alpha",
28524                b"extra"
28525            ]),
28526            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `extra` at position 1 for <main>\r\n"
28527        );
28528    }
28529
28530    /// Every word of the command's own is read without regard to case.
28531    #[test]
28532    fn the_words_of_a_profile_are_read_the_way_every_other_word_is() {
28533        let mut f = profiling();
28534        let one = f.run(&[
28535            b"FT.PROFILE",
28536            b"ix",
28537            b"search",
28538            b"limited",
28539            b"query",
28540            b"alpha",
28541        ]);
28542        let two = f.run(&[
28543            b"FT.PROFILE",
28544            b"ix",
28545            b"SEARCH",
28546            b"LIMITED",
28547            b"QUERY",
28548            b"alpha",
28549        ]);
28550        assert_eq!(timeless(&one), timeless(&two));
28551    }
28552
28553    // -------------------------------------------------------------- dropping
28554
28555    /// The two spellings take opposite defaults, which is measured and is the
28556    /// only difference between them that a client can see.
28557    #[test]
28558    fn the_two_ways_of_dropping_an_index_disagree_about_the_documents() {
28559        let mut f = profiling();
28560        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix"]), "+OK\r\n");
28561        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":3\r\n");
28562
28563        let mut f = profiling();
28564        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix", b"DD"]), "+OK\r\n");
28565        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":0\r\n");
28566
28567        let mut f = profiling();
28568        assert_eq!(f.run(&[b"FT.DROP", b"ix"]), "+OK\r\n");
28569        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":0\r\n");
28570
28571        let mut f = profiling();
28572        assert_eq!(f.run(&[b"FT.DROP", b"ix", b"KEEPDOCS"]), "+OK\r\n");
28573        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":3\r\n");
28574    }
28575
28576    /// Each spelling takes its own word and refuses the other one's, which
28577    /// reads as an oversight and is what a real server answers.
28578    #[test]
28579    fn neither_way_of_dropping_an_index_takes_the_other_ones_word() {
28580        let mut f = profiling();
28581        let line = "-SEARCH_ARG_UNRECOGNIZED Unknown argument\r\n";
28582        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix", b"KEEPDOCS"]), line);
28583        assert_eq!(f.run(&[b"FT.DROP", b"ix", b"DD"]), line);
28584        // Refused rather than half done, so the index is still there.
28585        assert_eq!(f.run(&[b"FT._LIST"]), "*1\r\n+ix\r\n");
28586    }
28587
28588    /// Only what the index read is deleted, which is not the same as
28589    /// everything under its prefix.
28590    #[test]
28591    fn dropping_the_documents_leaves_a_key_the_index_never_read() {
28592        let mut f = profiling();
28593        f.run(&[b"SET", b"p:4", b"alpha"]);
28594        f.run(&[b"HSET", b"q:1", b"t", b"alpha"]);
28595        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix", b"DD"]), "+OK\r\n");
28596        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":0\r\n");
28597        assert_eq!(f.run(&[b"EXISTS", b"p:4", b"q:1"]), ":2\r\n");
28598    }
28599
28600    /// An index still standing over the same keys hears about them going,
28601    /// rather than answering later with keys that are not there.
28602    #[test]
28603    fn another_index_over_the_same_keys_loses_the_documents_too() {
28604        let mut f = profiling();
28605        f.run(&[
28606            b"FT.CREATE",
28607            b"other",
28608            b"PREFIX",
28609            b"1",
28610            b"p:",
28611            b"SCHEMA",
28612            b"t",
28613            b"TEXT",
28614        ]);
28615        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix", b"DD"]), "+OK\r\n");
28616        assert_eq!(
28617            f.run(&[b"FT.SEARCH", b"other", b"alpha", b"NOCONTENT"]),
28618            "*1\r\n:0\r\n"
28619        );
28620    }
28621
28622    /// A drop that found nothing to drop deletes nothing either, which is the
28623    /// one case where the shortcut spelling answers `OK` without a sweep.
28624    #[test]
28625    fn a_drop_of_an_index_that_is_not_there_touches_no_keys() {
28626        let mut f = profiling();
28627        assert_eq!(f.run(&[b"FT._DROPINDEXIFX", b"nope", b"DD"]), "+OK\r\n");
28628        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":3\r\n");
28629        assert_eq!(f.run(&[b"FT._DROPIFX", b"nope"]), "+OK\r\n");
28630        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":3\r\n");
28631    }
28632
28633    // --------------------------------------------------------------- config
28634
28635    /// The two shapes a dump comes back in, which are the one mix of simple
28636    /// strings and bulk strings the group sends.
28637    #[test]
28638    fn a_setting_reads_back_as_a_pair_on_one_protocol_and_a_map_on_the_other() {
28639        let mut f = Fixture::new();
28640        assert_eq!(
28641            f.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
28642            "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n500\r\n"
28643        );
28644        assert_eq!(
28645            f.run(&[b"FT.CONFIG", b"GET", b"EXTLOAD"]),
28646            "*1\r\n*2\r\n+EXTLOAD\r\n$-1\r\n"
28647        );
28648        let mut g = Fixture::new();
28649        g.run(&[b"HELLO", b"3"]);
28650        assert_eq!(
28651            g.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
28652            "%1\r\n+TIMEOUT\r\n$3\r\n500\r\n"
28653        );
28654        assert_eq!(
28655            g.run(&[b"FT.CONFIG", b"GET", b"EXTLOAD"]),
28656            "%1\r\n+EXTLOAD\r\n_\r\n"
28657        );
28658    }
28659
28660    /// The help text rides along in the middle of the same row, flat on RESP2
28661    /// and as a map of its own on RESP3.
28662    #[test]
28663    fn a_help_row_carries_the_description_and_the_value_together() {
28664        let mut f = Fixture::new();
28665        assert_eq!(
28666            f.run(&[b"FT.CONFIG", b"HELP", b"TIMEOUT"]),
28667            "*1\r\n*5\r\n+TIMEOUT\r\n+Description\r\n+Query (search) timeout\r\n\
28668             +Value\r\n$3\r\n500\r\n"
28669        );
28670        let mut g = Fixture::new();
28671        g.run(&[b"HELLO", b"3"]);
28672        assert_eq!(
28673            g.run(&[b"FT.CONFIG", b"HELP", b"TIMEOUT"]),
28674            "%1\r\n+TIMEOUT\r\n%2\r\n+Description\r\n+Query (search) timeout\r\n\
28675             +Value\r\n$3\r\n500\r\n"
28676        );
28677    }
28678
28679    /// A name is matched whole, ignoring case, and the single word star is the
28680    /// only thing that means all of them.
28681    #[test]
28682    fn only_a_bare_star_asks_for_every_setting_and_nothing_else_globs() {
28683        let mut f = Fixture::new();
28684        assert_eq!(
28685            f.run(&[b"FT.CONFIG", b"GET", b"timeout"]),
28686            "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n500\r\n"
28687        );
28688        for name in [
28689            b"TIMEOUT*".as_slice(),
28690            b"?IMEOUT",
28691            b"*TIMEOUT*",
28692            b"TIME",
28693            b"NOSUCH",
28694            b"",
28695        ] {
28696            assert_eq!(f.run(&[b"FT.CONFIG", b"GET", name]), "*0\r\n", "{name:?}");
28697        }
28698        assert!(f.run(&[b"FT.CONFIG", b"GET", b"*"]).starts_with("*69\r\n"));
28699        assert!(f.run(&[b"FT.CONFIG", b"HELP", b"*"]).starts_with("*69\r\n"));
28700    }
28701
28702    /// Words after the name are stepped over rather than refused, on both of
28703    /// the two reads.
28704    #[test]
28705    fn a_read_ignores_whatever_follows_the_name() {
28706        let mut f = Fixture::new();
28707        assert_eq!(
28708            f.run(&[b"FT.CONFIG", b"GET", b"timeout", b"extra", b"more"]),
28709            "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n500\r\n"
28710        );
28711        assert_eq!(
28712            f.run(&[b"FT.CONFIG", b"HELP", b"timeout", b"extra"]),
28713            "*1\r\n*5\r\n+TIMEOUT\r\n+Description\r\n+Query (search) timeout\r\n\
28714             +Value\r\n$3\r\n500\r\n"
28715        );
28716    }
28717
28718    /// The container reports its own name and the subcommand it was given in
28719    /// the two lines the dispatcher writes.
28720    #[test]
28721    fn a_missing_subcommand_and_a_missing_name_are_told_apart() {
28722        let mut f = Fixture::new();
28723        assert_eq!(
28724            f.run(&[b"FT.CONFIG"]),
28725            "-ERR wrong number of arguments for 'FT.CONFIG' command\r\n"
28726        );
28727        for sub in [b"GET".as_slice(), b"SET", b"HELP"] {
28728            let want = format!(
28729                "-ERR wrong number of arguments for 'FT.CONFIG|{}' command\r\n",
28730                String::from_utf8_lossy(sub)
28731            );
28732            assert_eq!(f.run(&[b"FT.CONFIG", sub]), want);
28733        }
28734        assert_eq!(
28735            f.run(&[b"ft.config", b"get"]),
28736            "-ERR wrong number of arguments for 'FT.CONFIG|GET' command\r\n"
28737        );
28738        assert_eq!(
28739            f.run(&[b"FT.CONFIG", b"bogus"]),
28740            "-ERR unknown subcommand 'bogus'. Try FT.CONFIG HELP.\r\n"
28741        );
28742    }
28743
28744    /// The name, then whether it can move, then the value, then the count of
28745    /// words, and each of the first three answers before the next is looked at.
28746    #[test]
28747    fn a_write_checks_the_name_then_the_setting_then_the_value() {
28748        let mut f = Fixture::new();
28749        for tail in [vec![b"1".as_slice()], vec![], vec![b"1", b"2", b"3"]] {
28750            let mut cmd: Vec<&[u8]> = vec![b"FT.CONFIG", b"SET", b"NOSUCH"];
28751            cmd.extend(tail);
28752            assert_eq!(f.run(&cmd), "-SEARCH_OPTION_INVALID Invalid option\r\n");
28753        }
28754        for tail in [vec![b"1000".as_slice()], vec![], vec![b"x", b"y"]] {
28755            let mut cmd: Vec<&[u8]> = vec![b"FT.CONFIG", b"SET", b"MAXDOCTABLESIZE"];
28756            cmd.extend(tail);
28757            assert_eq!(
28758                f.run(&cmd),
28759                "-SEARCH_OPTION_BAD Not modifiable at runtime\r\n"
28760            );
28761        }
28762        assert_eq!(
28763            f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", b"x", b"y", b"z"]),
28764            "-SEARCH_PARSE_ARGS Could not convert argument to expected type\r\n"
28765        );
28766    }
28767
28768    /// Too many words is a status and not an error, and the value has already
28769    /// been written by the time it goes out.
28770    #[test]
28771    fn an_excess_of_words_is_noticed_after_the_value_is_kept() {
28772        let mut f = Fixture::new();
28773        assert_eq!(
28774            f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", b"500"]),
28775            "+OK\r\n"
28776        );
28777        assert_eq!(
28778            f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", b"600", b"junk"]),
28779            "+EXCESSARGS\r\n"
28780        );
28781        assert_eq!(
28782            f.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
28783            "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n600\r\n"
28784        );
28785    }
28786
28787    /// Strictly first and loosely second, so a hexadecimal and a leading zero
28788    /// and an exponent all land and a fraction does not.
28789    #[test]
28790    fn a_number_is_read_the_strict_way_and_then_the_loose_one() {
28791        let mut f = Fixture::new();
28792        for (given, want) in [
28793            (b"0x10".as_slice(), "16"),
28794            (b"0X1f", "31"),
28795            (b"+0x10", "16"),
28796            (b"+5", "5"),
28797            (b"010", "10"),
28798            (b"08", "8"),
28799            (b"0777", "777"),
28800            (b"1e3", "1000"),
28801            (b"0.0", "0"),
28802            (b"-0.0", "0"),
28803        ] {
28804            assert_eq!(
28805                f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", given]),
28806                "+OK\r\n",
28807                "{given:?}"
28808            );
28809            let want = format!("*1\r\n*2\r\n+TIMEOUT\r\n${}\r\n{want}\r\n", want.len());
28810            assert_eq!(
28811                f.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
28812                want,
28813                "{given:?}"
28814            );
28815        }
28816        for given in [
28817            b" 5".as_slice(),
28818            b"5 ",
28819            b"1.5",
28820            b"1e-3",
28821            b"x",
28822            b"",
28823            b"0b11",
28824            b"0xg",
28825            b"nan",
28826            b"inf",
28827            b"1e100",
28828            b"99999999999999999999",
28829        ] {
28830            assert_eq!(
28831                f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", given]),
28832                "-SEARCH_PARSE_ARGS Could not convert argument to expected type\r\n",
28833                "{given:?}"
28834            );
28835        }
28836    }
28837
28838    /// Which of the two readers found a negative decides what it is told, and
28839    /// on a setting with no range at all neither of them is refused.
28840    #[test]
28841    fn a_negative_is_answered_by_whichever_reader_found_it() {
28842        let mut f = Fixture::new();
28843        for given in [b"-1".as_slice(), b"-16"] {
28844            assert_eq!(
28845                f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", given]),
28846                "-SEARCH_PARSE_ARGS Value is outside acceptable bounds\r\n",
28847                "{given:?}"
28848            );
28849        }
28850        for given in [b"-0x10".as_slice(), b"-1e3", b"-010", b"-2.0"] {
28851            assert_eq!(
28852                f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", given]),
28853                "-SEARCH_PARSE_ARGS Could not convert argument to expected type\r\n",
28854                "{given:?}"
28855            );
28856        }
28857        let unlimited = "*1\r\n*2\r\n+MAXSEARCHRESULTS\r\n$9\r\nunlimited\r\n";
28858        for given in [b"-1".as_slice(), b"-0x10", b"-1e3", b"-010"] {
28859            assert_eq!(
28860                f.run(&[b"FT.CONFIG", b"SET", b"MAXSEARCHRESULTS", given]),
28861                "+OK\r\n",
28862                "{given:?}"
28863            );
28864            assert_eq!(
28865                f.run(&[b"FT.CONFIG", b"GET", b"MAXSEARCHRESULTS"]),
28866                unlimited,
28867                "{given:?}"
28868            );
28869        }
28870    }
28871
28872    /// The two settings with no range truncate into a signed thirty two bit
28873    /// slot and say so once the number has gone under.
28874    #[test]
28875    fn a_wide_setting_wraps_into_its_slot_before_it_is_read_back() {
28876        let mut f = Fixture::new();
28877        for (given, want) in [
28878            (b"2147483647".as_slice(), "2147483647"),
28879            (b"2147483648", "unlimited"),
28880            (b"4294967295", "unlimited"),
28881            (b"9223372036854775806", "unlimited"),
28882            (b"0", "0"),
28883        ] {
28884            assert_eq!(
28885                f.run(&[b"FT.CONFIG", b"SET", b"MAXSEARCHRESULTS", given]),
28886                "+OK\r\n",
28887                "{given:?}"
28888            );
28889            let want = format!(
28890                "*1\r\n*2\r\n+MAXSEARCHRESULTS\r\n${}\r\n{want}\r\n",
28891                want.len()
28892            );
28893            assert_eq!(
28894                f.run(&[b"FT.CONFIG", b"GET", b"MAXSEARCHRESULTS"]),
28895                want,
28896                "{given:?}"
28897            );
28898        }
28899    }
28900
28901    /// A number past what a setting will take says which way it went, and the
28902    /// ones with a softer roof of their own say what that roof is about.
28903    #[test]
28904    fn a_number_out_of_range_names_the_limit_it_crossed() {
28905        let mut f = Fixture::new();
28906        let bounds = "-SEARCH_PARSE_ARGS Value is outside acceptable bounds\r\n";
28907        for (name, given) in [
28908            (b"MINPREFIX".as_slice(), b"0".as_slice()),
28909            (b"MAX_AGGREGATE_GROUPS", b"0"),
28910            (b"BM25STD_TANH_FACTOR", b"0"),
28911            (b"DEFAULT_DIALECT", b"0"),
28912            (b"MINSTEMLEN", b"4294967296"),
28913            (b"_BG_INDEX_OOM_PAUSE_TIME", b"4294967296"),
28914            (b"INDEXER_YIELD_EVERY_OPS", b"4294967296"),
28915            (b"CONNECT_TIMEOUT", b"2147483648"),
28916        ] {
28917            assert_eq!(
28918                f.run(&[b"FT.CONFIG", b"SET", name, given]),
28919                bounds,
28920                "{name:?}"
28921            );
28922        }
28923        for (name, given, want) in [
28924            (
28925                b"MINSTEMLEN".as_slice(),
28926                b"1".as_slice(),
28927                "-SEARCH_SYNTAX Minimum stem length cannot be lower than 2\r\n",
28928            ),
28929            (
28930                b"MAX_AGGREGATE_GROUPS",
28931                b"67108865",
28932                "-SEARCH_LIMIT_OVER Value exceeds maximum possible aggregate groups\r\n",
28933            ),
28934            (
28935                b"WORKERS",
28936                b"17",
28937                "-SEARCH_LIMIT_OVER Number of worker threads cannot exceed 16\r\n",
28938            ),
28939            (
28940                b"_NUMERIC_RANGES_PARENTS",
28941                b"3",
28942                "-SEARCH_PARSE_ARGS Max depth for range cannot be higher than max \
28943                 depth for balance\r\n",
28944            ),
28945            (
28946                b"DEFAULT_DIALECT",
28947                b"5",
28948                "-SEARCH_VALUE_BAD Default dialect version cannot be higher than 4\r\n",
28949            ),
28950            (
28951                b"_BG_INDEX_MEM_PCT_THR",
28952                b"101",
28953                "-SEARCH_LIMIT_OVER Memory limit for indexing cannot be greater then \
28954                 100%\r\n",
28955            ),
28956            (
28957                b"BM25STD_TANH_FACTOR",
28958                b"10001",
28959                "-SEARCH_LIMIT_OVER BM25STD_TANH_FACTOR must be between 1 and 10000 \
28960                 inclusive\r\n",
28961            ),
28962            (
28963                b"BG_INDEX_SLEEP_DURATION_US",
28964                b"1000000",
28965                "-SEARCH_LIMIT_OVER BG_INDEX_SLEEP_DURATION_US must be between 1 and \
28966                 999999 (usleep POSIX limit)\r\n",
28967            ),
28968        ] {
28969            assert_eq!(
28970                f.run(&[b"FT.CONFIG", b"SET", name, given]),
28971                want,
28972                "{name:?}"
28973            );
28974        }
28975    }
28976
28977    /// The two trimming delays are measured against each other, and the answer
28978    /// names both settings and both numbers.
28979    #[test]
28980    fn the_trimming_delays_are_checked_against_one_another() {
28981        let mut f = Fixture::new();
28982        assert_eq!(
28983            f.run(&[b"FT.CONFIG", b"SET", b"_MIN_TRIM_DELAY_MS", b"5000"]),
28984            "-SEARCH_PARSE_ARGS _MIN_TRIM_DELAY_MS (5000) must be less than \
28985             _MAX_TRIM_DELAY_MS (5000)\r\n"
28986        );
28987        assert_eq!(
28988            f.run(&[b"FT.CONFIG", b"SET", b"_MAX_TRIM_DELAY_MS", b"1999"]),
28989            "-SEARCH_PARSE_ARGS _MAX_TRIM_DELAY_MS (1999) must be greater than \
28990             _MIN_TRIM_DELAY_MS (2000)\r\n"
28991        );
28992        assert_eq!(
28993            f.run(&[b"FT.CONFIG", b"SET", b"_MIN_TRIM_DELAY_MS", b"4999"]),
28994            "+OK\r\n"
28995        );
28996    }
28997
28998    /// Two of the word settings fold the spelling on the way in and the scorer
28999    /// does not, which is the one place in the table case counts.
29000    #[test]
29001    fn a_word_setting_folds_where_a_real_server_folds_and_not_otherwise() {
29002        let mut f = Fixture::new();
29003        assert_eq!(
29004            f.run(&[b"FT.CONFIG", b"SET", b"ON_TIMEOUT", b"RETURN"]),
29005            "+OK\r\n"
29006        );
29007        assert_eq!(
29008            f.run(&[b"FT.CONFIG", b"GET", b"ON_TIMEOUT"]),
29009            "*1\r\n*2\r\n+ON_TIMEOUT\r\n$6\r\nreturn\r\n"
29010        );
29011        assert_eq!(
29012            f.run(&[b"FT.CONFIG", b"SET", b"ON_TIMEOUT", b"nope"]),
29013            "-SEARCH_VALUE_BAD Invalid ON_TIMEOUT value\r\n"
29014        );
29015        assert_eq!(
29016            f.run(&[b"FT.CONFIG", b"SET", b"ON_OOM", b"IGNORE"]),
29017            "+OK\r\n"
29018        );
29019        assert_eq!(
29020            f.run(&[b"FT.CONFIG", b"GET", b"ON_OOM"]),
29021            "*1\r\n*2\r\n+ON_OOM\r\n$6\r\nignore\r\n"
29022        );
29023        assert_eq!(
29024            f.run(&[b"FT.CONFIG", b"SET", b"ON_OOM", b"nope"]),
29025            "-SEARCH_VALUE_BAD Invalid ON_OOM value\r\n"
29026        );
29027        let bad = "-SEARCH_VALUE_BAD Invalid default scorer value\r\n";
29028        for given in [b"bm25std".as_slice(), b"Bm25", b"TFIDF.docnorm", b""] {
29029            assert_eq!(
29030                f.run(&[b"FT.CONFIG", b"SET", b"DEFAULT_SCORER", given]),
29031                bad,
29032                "{given:?}"
29033            );
29034        }
29035        assert_eq!(
29036            f.run(&[b"FT.CONFIG", b"SET", b"DEFAULT_SCORER", b"TFIDF.DOCNORM"]),
29037            "+OK\r\n"
29038        );
29039    }
29040
29041    /// True and false, either case, and none of the other words a client might
29042    /// reach for.
29043    #[test]
29044    fn a_yes_or_no_setting_takes_those_two_words_only() {
29045        let mut f = Fixture::new();
29046        assert_eq!(
29047            f.run(&[b"FT.CONFIG", b"SET", b"_NUMERIC_COMPRESS", b"TRUE"]),
29048            "+OK\r\n"
29049        );
29050        assert_eq!(
29051            f.run(&[b"FT.CONFIG", b"GET", b"_NUMERIC_COMPRESS"]),
29052            "*1\r\n*2\r\n+_NUMERIC_COMPRESS\r\n$4\r\ntrue\r\n"
29053        );
29054        for given in [b"yes".as_slice(), b"no", b"1", b"0", b"enabled", b""] {
29055            assert_eq!(
29056                f.run(&[b"FT.CONFIG", b"SET", b"_NUMERIC_COMPRESS", given]),
29057                "-SEARCH_PARSE_ARGS Could not convert argument to expected type\r\n",
29058                "{given:?}"
29059            );
29060        }
29061    }
29062
29063    /// Two pairs of names sit over one number each, and one of that second pair
29064    /// takes no value at all.
29065    #[test]
29066    fn two_names_for_one_setting_move_together() {
29067        let mut f = Fixture::new();
29068        f.run(&[b"FT.CONFIG", b"SET", b"MAXEXPANSIONS", b"300"]);
29069        assert_eq!(
29070            f.run(&[b"FT.CONFIG", b"GET", b"MAXPREFIXEXPANSIONS"]),
29071            "*1\r\n*2\r\n+MAXPREFIXEXPANSIONS\r\n$3\r\n300\r\n"
29072        );
29073        f.run(&[b"FT.CONFIG", b"SET", b"MAXPREFIXEXPANSIONS", b"200"]);
29074        assert_eq!(
29075            f.run(&[b"FT.CONFIG", b"GET", b"MAXEXPANSIONS"]),
29076            "*1\r\n*2\r\n+MAXEXPANSIONS\r\n$3\r\n200\r\n"
29077        );
29078        let long = b"_FORK_GC_CLEAN_NUMERIC_EMPTY_NODES".as_slice();
29079        let short = b"FORK_GC_CLEAN_NUMERIC_EMPTY_NODES".as_slice();
29080        f.run(&[b"FT.CONFIG", b"SET", long, b"false"]);
29081        assert_eq!(
29082            f.run(&[b"FT.CONFIG", b"GET", short]),
29083            "*1\r\n*2\r\n+FORK_GC_CLEAN_NUMERIC_EMPTY_NODES\r\n$5\r\nfalse\r\n"
29084        );
29085        assert_eq!(f.run(&[b"FT.CONFIG", b"SET", short]), "+OK\r\n");
29086        assert_eq!(
29087            f.run(&[b"FT.CONFIG", b"GET", long]),
29088            "*1\r\n*2\r\n+_FORK_GC_CLEAN_NUMERIC_EMPTY_NODES\r\n$4\r\ntrue\r\n"
29089        );
29090    }
29091
29092    /// The one setting that takes a write and never gives it back.
29093    #[test]
29094    fn a_password_reads_back_as_stars_whatever_was_written() {
29095        let mut f = Fixture::new();
29096        assert_eq!(
29097            f.run(&[b"FT.CONFIG", b"SET", b"OSS_GLOBAL_PASSWORD", b"hunter2"]),
29098            "+OK\r\n"
29099        );
29100        assert_eq!(
29101            f.run(&[b"FT.CONFIG", b"GET", b"OSS_GLOBAL_PASSWORD"]),
29102            "*1\r\n*2\r\n+OSS_GLOBAL_PASSWORD\r\n$17\r\nPassword: *******\r\n"
29103        );
29104    }
29105
29106    /// The settings are not in the keyspace, so unlike the dictionaries and the
29107    /// synonym groups beside them they live through an emptied one.
29108    #[test]
29109    fn a_flush_leaves_the_settings_alone() {
29110        for flush in [b"FLUSHALL".as_slice(), b"FLUSHDB"] {
29111            let mut f = Fixture::new();
29112            f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", b"777"]);
29113            f.run(&[flush]);
29114            assert_eq!(
29115                f.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
29116                "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n777\r\n",
29117                "{flush:?}"
29118            );
29119        }
29120    }
29121
29122    // ---------------------------------------------------------------- debug
29123
29124    /// A small index with one of everything a dump can read, so the tests below
29125    /// all name the same three documents and the same four fields.
29126    fn debugging() -> Fixture {
29127        let mut f = Fixture::new();
29128        f.run(&[
29129            b"FT.CREATE",
29130            b"dx",
29131            b"PREFIX",
29132            b"1",
29133            b"d:",
29134            b"SCHEMA",
29135            b"t",
29136            b"TEXT",
29137            b"g",
29138            b"TAG",
29139            b"n",
29140            b"NUMERIC",
29141            b"s",
29142            b"TEXT",
29143            b"SORTABLE",
29144        ]);
29145        f.run(&[
29146            b"HSET",
29147            b"d:1",
29148            b"t",
29149            b"running dogs",
29150            b"g",
29151            b"red,blue",
29152            b"n",
29153            b"1",
29154            b"s",
29155            b"Alpha",
29156        ]);
29157        f.run(&[
29158            b"HSET", b"d:2", b"t", b"running", b"g", b"red", b"n", b"2", b"s", b"beta",
29159        ]);
29160        f.run(&[
29161            b"HSET",
29162            b"d:3",
29163            b"t",
29164            b"dogs alpha",
29165            b"g",
29166            b"green",
29167            b"n",
29168            b"3",
29169        ]);
29170        f
29171    }
29172
29173    /// The whole dictionary in byte order, with the stems in it as entries of
29174    /// their own rather than hidden behind the words they came from.
29175    #[test]
29176    fn a_term_dump_lists_the_stems_beside_the_words() {
29177        let mut f = debugging();
29178        assert_eq!(
29179            f.run(&[b"_FT.DEBUG", b"DUMP_TERMS", b"dx"]),
29180            "*6\r\n$4\r\n+dog\r\n$4\r\n+run\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n\
29181             $4\r\ndogs\r\n$7\r\nrunning\r\n"
29182        );
29183    }
29184
29185    /// A posting list is looked up on the bytes given and nothing folds them, so
29186    /// the term that a query would have found is not the term a dump wants.
29187    #[test]
29188    fn a_posting_list_is_read_by_the_bytes_and_not_by_the_word() {
29189        let mut f = debugging();
29190        assert_eq!(
29191            f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", b"running"]),
29192            "*2\r\n:1\r\n:2\r\n"
29193        );
29194        assert_eq!(
29195            f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", b"+run"]),
29196            "*2\r\n:1\r\n:2\r\n"
29197        );
29198        for term in [b"RUNNING".as_slice(), b"nosuchterm", b""] {
29199            assert_eq!(
29200                f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", term]),
29201                "-Can not find the inverted index\r\n",
29202                "{term:?}"
29203            );
29204        }
29205    }
29206
29207    /// Tag values come back folded and in byte order, each with the documents
29208    /// that hold it, and a document with two values is under both of them.
29209    #[test]
29210    fn a_tag_dump_pairs_every_value_with_its_documents() {
29211        let mut f = debugging();
29212        assert_eq!(
29213            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"dx", b"g"]),
29214            "*3\r\n*2\r\n$4\r\nblue\r\n*1\r\n:1\r\n*2\r\n$5\r\ngreen\r\n*1\r\n:3\r\n\
29215             *2\r\n$3\r\nred\r\n*2\r\n:1\r\n:2\r\n"
29216        );
29217    }
29218
29219    /// One list holding every document in the field, which is D-96: a range tree
29220    /// answers one list per range and this answers the one it keeps.
29221    #[test]
29222    fn a_number_dump_answers_a_single_range() {
29223        let mut f = debugging();
29224        assert_eq!(
29225            f.run(&[b"_FT.DEBUG", b"DUMP_NUMIDX", b"dx", b"n"]),
29226            "*1\r\n*3\r\n:1\r\n:2\r\n:3\r\n"
29227        );
29228    }
29229
29230    /// A point is a number underneath, so the field that holds points answers
29231    /// the subcommand that dumps numbers and not the one that dumps tags.
29232    #[test]
29233    fn a_geo_field_is_dumped_as_a_numeric_one() {
29234        let mut f = Fixture::new();
29235        f.run(&[
29236            b"FT.CREATE",
29237            b"gx",
29238            b"PREFIX",
29239            b"1",
29240            b"q:",
29241            b"SCHEMA",
29242            b"loc",
29243            b"GEO",
29244            b"gg",
29245            b"AS",
29246            b"tag",
29247            b"TAG",
29248        ]);
29249        f.run(&[b"HSET", b"q:1", b"loc", b"1,2", b"gg", b"red"]);
29250        f.run(&[b"HSET", b"q:2", b"loc", b"3,4", b"gg", b"BLUE"]);
29251        assert_eq!(
29252            f.run(&[b"_FT.DEBUG", b"DUMP_NUMIDX", b"gx", b"loc"]),
29253            "*1\r\n*2\r\n:1\r\n:2\r\n"
29254        );
29255        assert_eq!(
29256            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"gx", b"loc"]),
29257            "-Could not find given field in index spec\r\n"
29258        );
29259    }
29260
29261    /// A field is named the way a query names it, so the attribute is the name
29262    /// and the identifier the value was read from is not one.
29263    #[test]
29264    fn a_dump_takes_the_attribute_and_not_the_identifier() {
29265        let mut f = Fixture::new();
29266        f.run(&[
29267            b"FT.CREATE",
29268            b"zx",
29269            b"PREFIX",
29270            b"1",
29271            b"z:",
29272            b"SCHEMA",
29273            b"gg",
29274            b"AS",
29275            b"tag",
29276            b"TAG",
29277        ]);
29278        f.run(&[b"HSET", b"z:1", b"gg", b"red"]);
29279        assert_eq!(
29280            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"zx", b"tag"]),
29281            "*1\r\n*2\r\n$3\r\nred\r\n*1\r\n:1\r\n"
29282        );
29283        assert_eq!(
29284            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"zx", b"gg"]),
29285            "-Could not find given field in index spec\r\n"
29286        );
29287    }
29288
29289    /// The seven keys, with the score as a bulk string here and a double there,
29290    /// and the whole row flat on one protocol and a map on the other.
29291    #[test]
29292    fn a_document_row_is_flat_on_one_protocol_and_a_map_on_the_other() {
29293        let mut f = debugging();
29294        assert_eq!(
29295            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"REVEAL"]),
29296            "*14\r\n+internal_id\r\n:1\r\n$5\r\nflags\r\n\
29297             $36\r\n(0xc):HasSortVector,HasOffsetVector,\r\n+score\r\n$1\r\n1\r\n\
29298             +num_tokens\r\n:3\r\n+max_freq\r\n:1\r\n+refcount\r\n:1\r\n\
29299             +sortables\r\n*1\r\n*6\r\n+index\r\n:0\r\n$5\r\nfield\r\n$6\r\ns AS s\r\n\
29300             $5\r\nvalue\r\n$5\r\nalpha\r\n"
29301        );
29302        let mut g = debugging();
29303        g.run(&[b"HELLO", b"3"]);
29304        assert_eq!(
29305            g.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"REVEAL"]),
29306            "%7\r\n+internal_id\r\n:1\r\n$5\r\nflags\r\n\
29307             $36\r\n(0xc):HasSortVector,HasOffsetVector,\r\n+score\r\n,1\r\n\
29308             +num_tokens\r\n:3\r\n+max_freq\r\n:1\r\n+refcount\r\n:1\r\n\
29309             +sortables\r\n*1\r\n*6\r\n+index\r\n:0\r\n$5\r\nfield\r\n$6\r\ns AS s\r\n\
29310             $5\r\nvalue\r\n$5\r\nalpha\r\n"
29311        );
29312    }
29313
29314    /// A document that wrote nothing into a sortable slot has no sortables key
29315    /// at all, so the row is a key shorter rather than carrying an empty list.
29316    #[test]
29317    fn a_document_with_no_sortable_value_drops_the_key() {
29318        let mut f = debugging();
29319        assert_eq!(
29320            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:3", b"REVEAL"]),
29321            "*12\r\n+internal_id\r\n:3\r\n$5\r\nflags\r\n$22\r\n(0x8):HasOffsetVector,\r\n\
29322             +score\r\n$1\r\n1\r\n+num_tokens\r\n:2\r\n+max_freq\r\n:1\r\n+refcount\r\n:1\r\n"
29323        );
29324    }
29325
29326    /// The flag word is the number and then the names it stands for, and an
29327    /// index built without offsets has none of the three set.
29328    #[test]
29329    fn the_flag_word_spells_out_the_bits_it_carries() {
29330        let mut f = Fixture::new();
29331        f.run(&[
29332            b"FT.CREATE",
29333            b"nx",
29334            b"NOOFFSETS",
29335            b"PREFIX",
29336            b"1",
29337            b"o:",
29338            b"SCHEMA",
29339            b"t",
29340            b"TEXT",
29341        ]);
29342        f.run(&[b"HSET", b"o:1", b"t", b"alpha"]);
29343        assert!(
29344            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"nx", b"o:1", b"REVEAL"])
29345                .contains("$6\r\n(0x0):\r\n")
29346        );
29347    }
29348
29349    /// Obfuscation replaces the field name with where the field sits in the
29350    /// whole schema, which is not where its value sits among the sortables.
29351    #[test]
29352    fn obfuscation_numbers_a_field_by_its_place_in_the_schema() {
29353        let mut f = debugging();
29354        assert!(
29355            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"OBFUSCATE"])
29356                .contains("$22\r\nFieldPath@3 AS Field@3\r\n")
29357        );
29358    }
29359
29360    /// The keyword is read where it belongs and anything after it is stepped
29361    /// over, whatever the line that complains about it says.
29362    #[test]
29363    fn a_document_row_reads_its_keyword_at_a_fixed_place() {
29364        let mut f = debugging();
29365        let want = f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"REVEAL"]);
29366        assert_eq!(
29367            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"REVEAL", b"more"]),
29368            want
29369        );
29370        assert_eq!(
29371            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"more", b"REVEAL"]),
29372            "-Invalid argument. Expected REVEAL or OBFUSCATE as the last argument\r\n"
29373        );
29374        assert_eq!(
29375            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1"]),
29376            "-ERR wrong number of arguments for '_FT.DEBUG|DOCINFO' command\r\n"
29377        );
29378    }
29379
29380    /// The key is looked up before the keyword is read, so a key nobody indexed
29381    /// beats a keyword nobody wrote.
29382    #[test]
29383    fn a_document_row_looks_the_key_up_before_it_reads_the_keyword() {
29384        let mut f = debugging();
29385        assert_eq!(
29386            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"nope", b"zz"]),
29387            "-Document not found in index\r\n"
29388        );
29389        assert_eq!(
29390            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"zz"]),
29391            "-Invalid argument. Expected REVEAL or OBFUSCATE as the last argument\r\n"
29392        );
29393    }
29394
29395    /// The two directions of the document table, and the number nobody handed
29396    /// out reads as one that was given up rather than as one that never was.
29397    #[test]
29398    fn a_document_number_goes_both_ways() {
29399        let mut f = debugging();
29400        assert_eq!(
29401            f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", b"2"]),
29402            "$3\r\nd:2\r\n"
29403        );
29404        assert_eq!(
29405            f.run(&[b"_FT.DEBUG", b"DOCIDTOID", b"dx", b"d:2"]),
29406            ":2\r\n"
29407        );
29408        assert_eq!(
29409            f.run(&[b"_FT.DEBUG", b"DOCIDTOID", b"dx", b"nope"]),
29410            ":0\r\n"
29411        );
29412        assert_eq!(f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"dx"]), ":3\r\n");
29413        for id in [b"9".as_slice(), b"0", b"-1", b"9223372036854775807"] {
29414            assert_eq!(
29415                f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", id]),
29416                "-document was removed\r\n",
29417                "{id:?}"
29418            );
29419        }
29420    }
29421
29422    /// A document number is read the strict way Redis reads an integer, so a
29423    /// leading zero, a leading plus and a leading space are all refused.
29424    #[test]
29425    fn a_document_number_is_read_the_strict_way() {
29426        let mut f = debugging();
29427        for id in [
29428            b"x".as_slice(),
29429            b"1.5",
29430            b" 1",
29431            b"+1",
29432            b"01",
29433            b"0x1",
29434            b"",
29435            b"9223372036854775808",
29436            b"18446744073709551615",
29437        ] {
29438            assert_eq!(
29439                f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", id]),
29440                "-bad id given\r\n",
29441                "{id:?}"
29442            );
29443        }
29444    }
29445
29446    /// A number a document has given up is still in every list it was in, so a
29447    /// dump names documents that the table says are gone.
29448    #[test]
29449    fn a_dump_keeps_a_number_the_table_has_given_up() {
29450        let mut f = debugging();
29451        f.run(&[b"DEL", b"d:2"]);
29452        assert_eq!(
29453            f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", b"running"]),
29454            "*2\r\n:1\r\n:2\r\n"
29455        );
29456        assert_eq!(
29457            f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", b"2"]),
29458            "-document was removed\r\n"
29459        );
29460        assert_eq!(
29461            f.run(&[b"_FT.DEBUG", b"DOCIDTOID", b"dx", b"d:2"]),
29462            ":0\r\n"
29463        );
29464    }
29465
29466    /// A rewrite hands out a new number and leaves the old one behind, so the
29467    /// counter climbs past the number of documents there are.
29468    #[test]
29469    fn a_rewrite_takes_a_number_of_its_own() {
29470        let mut f = debugging();
29471        f.run(&[b"HSET", b"d:1", b"t", b"cats"]);
29472        assert_eq!(
29473            f.run(&[b"_FT.DEBUG", b"DOCIDTOID", b"dx", b"d:1"]),
29474            ":4\r\n"
29475        );
29476        assert_eq!(f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"dx"]), ":4\r\n");
29477        assert_eq!(
29478            f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", b"1"]),
29479            "-document was removed\r\n"
29480        );
29481        assert_eq!(
29482            f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", b"running"]),
29483            "*2\r\n:1\r\n:2\r\n"
29484        );
29485    }
29486
29487    /// An alias reads the index it stands for, the same as a query does.
29488    #[test]
29489    fn a_dump_follows_an_alias() {
29490        let mut f = debugging();
29491        f.run(&[b"FT.ALIASADD", b"da", b"dx"]);
29492        assert_eq!(f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"da"]), ":3\r\n");
29493        assert_eq!(
29494            f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"da", b"1"]),
29495            "$3\r\nd:1\r\n"
29496        );
29497    }
29498
29499    /// The index name is matched as written and the subcommand name is not, and
29500    /// an index nobody made is reported as a context that could not be built.
29501    #[test]
29502    fn an_index_name_is_case_sensitive_and_a_subcommand_name_is_not() {
29503        let mut f = debugging();
29504        assert_eq!(f.run(&[b"_FT.DEBUG", b"get_max_doc_id", b"dx"]), ":3\r\n");
29505        assert_eq!(
29506            f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"DX"]),
29507            "-Can not create a search ctx\r\n"
29508        );
29509        assert_eq!(
29510            f.run(&[b"_FT.DEBUG", b"DUMP_TERMS", b"nope"]),
29511            "-Can not create a search ctx\r\n"
29512        );
29513    }
29514
29515    /// A field with nothing written into it answers an empty dump rather than an
29516    /// error, since the field is in the schema and only the values are missing.
29517    #[test]
29518    fn an_empty_field_dumps_as_nothing_at_all() {
29519        let mut f = Fixture::new();
29520        f.run(&[
29521            b"FT.CREATE",
29522            b"ex",
29523            b"PREFIX",
29524            b"1",
29525            b"e:",
29526            b"SCHEMA",
29527            b"t",
29528            b"TEXT",
29529            b"g",
29530            b"TAG",
29531            b"n",
29532            b"NUMERIC",
29533        ]);
29534        assert_eq!(f.run(&[b"_FT.DEBUG", b"DUMP_TERMS", b"ex"]), "*0\r\n");
29535        assert_eq!(
29536            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"ex", b"g"]),
29537            "*0\r\n"
29538        );
29539        assert_eq!(
29540            f.run(&[b"_FT.DEBUG", b"DUMP_NUMIDX", b"ex", b"n"]),
29541            "*0\r\n"
29542        );
29543        assert_eq!(f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"ex"]), ":0\r\n");
29544    }
29545
29546    /// The two lines the dispatcher owns are the two that carry a code word, and
29547    /// every subcommand but `DOCINFO` counts its arguments exactly.
29548    #[test]
29549    fn the_two_lines_with_a_code_word_are_the_arity_and_the_unknown_one() {
29550        let mut f = debugging();
29551        for (sub, extra) in [
29552            (b"DUMP_TERMS".as_slice(), 1),
29553            (b"GET_MAX_DOC_ID", 1),
29554            (b"DUMP_INVIDX", 2),
29555            (b"DUMP_TAGIDX", 2),
29556            (b"DUMP_NUMIDX", 2),
29557            (b"IDTODOCID", 2),
29558            (b"DOCIDTOID", 2),
29559        ] {
29560            let want = format!(
29561                "-ERR wrong number of arguments for '_FT.DEBUG|{}' command\r\n",
29562                str::from_utf8(sub).unwrap()
29563            );
29564            for given in [extra - 1, extra + 1] {
29565                let mut cmd: Vec<&[u8]> = vec![b"_FT.DEBUG", sub];
29566                cmd.extend(std::iter::repeat_n(b"dx".as_slice(), given));
29567                assert_eq!(f.run(&cmd), want, "{sub:?} {given}");
29568            }
29569            let mut right: Vec<&[u8]> = vec![b"_FT.DEBUG", sub, b"dx"];
29570            right.extend(std::iter::repeat_n(b"g".as_slice(), extra - 1));
29571            assert_ne!(f.run(&right), want, "{sub:?}");
29572        }
29573        assert_eq!(
29574            f.run(&[b"_FT.DEBUG", b"bogus", b"dx"]),
29575            "-ERR unknown subcommand 'bogus'. Try _FT.DEBUG HELP.\r\n"
29576        );
29577    }
29578
29579    /// The eight names that answer rather than the sixty two a real server
29580    /// registers, which is D-97, and anything after the name is stepped over.
29581    #[test]
29582    fn the_help_names_the_subcommands_that_answer() {
29583        let mut f = Fixture::new();
29584        let want = "*8\r\n$11\r\nDUMP_INVIDX\r\n$11\r\nDUMP_NUMIDX\r\n$11\r\nDUMP_TAGIDX\r\n\
29585             $9\r\nIDTODOCID\r\n$9\r\nDOCIDTOID\r\n$7\r\nDOCINFO\r\n$10\r\nDUMP_TERMS\r\n\
29586             $14\r\nGET_MAX_DOC_ID\r\n";
29587        assert_eq!(f.run(&[b"_FT.DEBUG", b"HELP"]), want);
29588        assert_eq!(f.run(&[b"_FT.DEBUG", b"HELP", b"extra"]), want);
29589    }
29590
29591    // ------------------------------------------------------------- synonyms
29592
29593    /// The terms are folded on the way in and the group ids are not, and one
29594    /// term can be in more than one group.
29595    #[test]
29596    fn a_synonym_dump_folds_the_terms_and_keeps_the_ids_as_given() {
29597        let mut f = Fixture::new();
29598        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT"]);
29599        assert_eq!(
29600            f.run(&[b"FT.SYNUPDATE", b"e", b"G1", b"BOY", b"kid"]),
29601            "+OK\r\n"
29602        );
29603        assert_eq!(f.run(&[b"FT.SYNUPDATE", b"e", b"g2", b"boy"]), "+OK\r\n");
29604        assert_eq!(
29605            f.run(&[b"FT.SYNDUMP", b"e"]),
29606            "*4\r\n$3\r\nboy\r\n*2\r\n$2\r\nG1\r\n$2\r\ng2\r\n\
29607             $3\r\nkid\r\n*1\r\n$2\r\nG1\r\n"
29608        );
29609    }
29610
29611    /// A group is not a comparison made at query time. It is a term of its
29612    /// own, so a word in a group reads as a union of the word, the groups it
29613    /// is in and its stem.
29614    #[test]
29615    fn a_word_in_a_group_reads_as_a_union_with_the_group_term() {
29616        let mut f = Fixture::new();
29617        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT"]);
29618        f.run(&[b"FT.SYNUPDATE", b"e", b"gr", b"jogging"]);
29619        assert_eq!(
29620            f.run(&[b"FT.EXPLAIN", b"e", b"jogging"]),
29621            "$69\r\nUNION {\n  jogging\n  ~gr(expanded)\n  +jog(expanded)\n  jog(expanded)\n}\n\r\n"
29622        );
29623    }
29624
29625    /// The lookup on the document side is on the word and never on the stem,
29626    /// and a group written after the documents were still finds them because
29627    /// the index is read again.
29628    ///
29629    /// The group holds `running` and `d2` says `runs`, so a query for another
29630    /// word of the group finds `d1` and leaves `d2` where it is. A query for
29631    /// `running` itself does find `d2`, through the stem branch of the union
29632    /// rather than through the group, which is why the two asserts differ.
29633    #[test]
29634    fn a_group_matches_the_word_it_holds_and_not_a_stem_of_it() {
29635        let mut f = Fixture::new();
29636        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT"]);
29637        f.run(&[b"HSET", b"d1", b"t", b"boy"]);
29638        f.run(&[b"HSET", b"d2", b"t", b"runs"]);
29639        f.run(&[b"FT.SYNUPDATE", b"e", b"gr", b"boy", b"child", b"running"]);
29640        assert_eq!(
29641            f.run(&[b"FT.SEARCH", b"e", b"child", b"NOCONTENT"]),
29642            "*2\r\n:1\r\n$2\r\nd1\r\n"
29643        );
29644        assert_eq!(
29645            f.run(&[b"FT.SEARCH", b"e", b"running", b"NOCONTENT"]),
29646            "*3\r\n:2\r\n$2\r\nd1\r\n$2\r\nd2\r\n"
29647        );
29648    }
29649
29650    /// Neither command makes an index and neither forgives a name that is not
29651    /// there, in the same words the rest of the group uses.
29652    #[test]
29653    fn a_synonym_command_on_a_name_that_is_not_there_fails() {
29654        let mut f = Fixture::new();
29655        let missing = "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n";
29656        assert_eq!(f.run(&[b"FT.SYNDUMP", b"nope"]), missing);
29657        assert_eq!(f.run(&[b"FT.SYNUPDATE", b"nope", b"g", b"a"]), missing);
29658    }
29659
29660    /// The words after `PARAMS n` are counted before their shape is looked at,
29661    /// so a count that reaches past the end of the command and a count that is
29662    /// merely odd are two different errors.
29663    #[test]
29664    fn params_counts_the_words_before_it_pairs_them_up() {
29665        let mut f = Fixture::new();
29666        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT"]);
29667        let none = "-SEARCH_PARSE_ARGS Bad arguments for PARAMS: \
29668                    Expected an argument, but none provided\r\n";
29669        let odd = "-SEARCH_ADD_ARGS Parameters must be specified in PARAM VALUE pairs\r\n";
29670        assert_eq!(f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"1"]), none);
29671        assert_eq!(
29672            f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"3", b"a", b"b"]),
29673            none
29674        );
29675        assert_eq!(
29676            f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"1", b"a"]),
29677            odd
29678        );
29679        assert_eq!(f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"0"]), odd);
29680        assert_eq!(
29681            f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"-1"]),
29682            "-SEARCH_PARSE_ARGS Bad arguments for PARAMS: Value is outside acceptable bounds\r\n"
29683        );
29684    }
29685
29686    // --------------------------------------------------------------- vectors
29687
29688    /// Five documents a unit apart along one axis, written in the opposite
29689    /// order to the one they sit in, so a reply in document order and a reply
29690    /// in distance order are two different replies.
29691    ///
29692    /// `d1` is furthest from the origin and `d5` is on it. The text field
29693    /// splits them so a query can narrow before it measures: `d1`, `d2` and
29694    /// `d4` say `alpha` and the other two say `beta`.
29695    fn vectored(f: &mut Fixture) {
29696        f.run(&[
29697            b"FT.CREATE",
29698            b"h",
29699            b"SCHEMA",
29700            b"t",
29701            b"TEXT",
29702            b"v",
29703            b"VECTOR",
29704            b"FLAT",
29705            b"6",
29706            b"TYPE",
29707            b"FLOAT32",
29708            b"DIM",
29709            b"2",
29710            b"DISTANCE_METRIC",
29711            b"L2",
29712        ]);
29713        let at: [&[u8]; 5] = [
29714            b"\x00\x00\x80\x40\x00\x00\x00\x00",
29715            b"\x00\x00\x40\x40\x00\x00\x00\x00",
29716            b"\x00\x00\x00\x40\x00\x00\x00\x00",
29717            b"\x00\x00\x80\x3f\x00\x00\x00\x00",
29718            b"\x00\x00\x00\x00\x00\x00\x00\x00",
29719        ];
29720        for (n, point) in at.iter().enumerate() {
29721            let key = format!("d{}", n + 1);
29722            let word: &[u8] = match n {
29723                0 | 1 | 3 => b"alpha",
29724                _ => b"beta",
29725            };
29726            f.run(&[b"HSET", key.as_bytes(), b"t", word, b"v", point]);
29727        }
29728    }
29729
29730    /// The origin, which every query below asks about.
29731    const ORIGIN: &[u8] = b"\x00\x00\x00\x00\x00\x00\x00\x00";
29732
29733    /// A `KNN` picks the k nearest and then answers them in document order,
29734    /// which is measured: asking for three of five that were written furthest
29735    /// first answers the last three written and not the first three.
29736    #[test]
29737    fn a_knn_picks_the_nearest_and_answers_them_in_document_order() {
29738        let mut f = Fixture::new();
29739        vectored(&mut f);
29740        assert_eq!(
29741            f.run(&[
29742                b"FT.SEARCH",
29743                b"h",
29744                b"*=>[KNN 5 @v $vec]",
29745                b"PARAMS",
29746                b"2",
29747                b"vec",
29748                ORIGIN,
29749                b"DIALECT",
29750                b"2",
29751                b"NOCONTENT",
29752            ]),
29753            "*6\r\n:5\r\n$2\r\nd1\r\n$2\r\nd2\r\n$2\r\nd3\r\n$2\r\nd4\r\n$2\r\nd5\r\n"
29754        );
29755        assert_eq!(
29756            f.run(&[
29757                b"FT.SEARCH",
29758                b"h",
29759                b"*=>[KNN 3 @v $vec]",
29760                b"PARAMS",
29761                b"2",
29762                b"vec",
29763                ORIGIN,
29764                b"DIALECT",
29765                b"2",
29766                b"NOCONTENT",
29767            ]),
29768            "*4\r\n:3\r\n$2\r\nd3\r\n$2\r\nd4\r\n$2\r\nd5\r\n"
29769        );
29770    }
29771
29772    /// A range takes what is really inside it, where the distances are squared
29773    /// so the five documents sit at 16, 9, 4, 1 and 0.
29774    #[test]
29775    fn a_range_takes_what_is_inside_it_and_the_distance_is_squared() {
29776        let mut f = Fixture::new();
29777        vectored(&mut f);
29778        for (radius, want) in [
29779            ("0", "*2\r\n:1\r\n$2\r\nd5\r\n"),
29780            ("2", "*3\r\n:2\r\n$2\r\nd4\r\n$2\r\nd5\r\n"),
29781            (
29782                "9",
29783                "*5\r\n:4\r\n$2\r\nd2\r\n$2\r\nd3\r\n$2\r\nd4\r\n$2\r\nd5\r\n",
29784            ),
29785        ] {
29786            let query = format!("@v:[VECTOR_RANGE {radius} $vec]");
29787            assert_eq!(
29788                f.run(&[
29789                    b"FT.SEARCH",
29790                    b"h",
29791                    query.as_bytes(),
29792                    b"PARAMS",
29793                    b"2",
29794                    b"vec",
29795                    ORIGIN,
29796                    b"DIALECT",
29797                    b"2",
29798                    b"NOCONTENT",
29799                ]),
29800                want,
29801                "radius {radius}"
29802            );
29803        }
29804    }
29805
29806    /// A `KNN` behind a query is the nearest of what the query matched, so
29807    /// asking for two of the three documents that say `alpha` answers the two
29808    /// of those three that are nearest and not the two nearest overall.
29809    #[test]
29810    fn a_knn_measures_what_the_query_in_front_of_it_matched() {
29811        let mut f = Fixture::new();
29812        vectored(&mut f);
29813        assert_eq!(
29814            f.run(&[
29815                b"FT.SEARCH",
29816                b"h",
29817                b"alpha=>[KNN 2 @v $vec]",
29818                b"PARAMS",
29819                b"2",
29820                b"vec",
29821                ORIGIN,
29822                b"DIALECT",
29823                b"2",
29824                b"NOCONTENT",
29825            ]),
29826            "*3\r\n:2\r\n$2\r\nd2\r\n$2\r\nd4\r\n"
29827        );
29828    }
29829
29830    /// A `KNN` counts in whole numbers and a range measures from zero, and the
29831    /// two are refused in their own words.
29832    ///
29833    /// The count is a token of its own and is checked where it stands, ahead of
29834    /// the field and ahead of the vector. A count that arrives through `PARAMS`
29835    /// is read by looser rules than one written into the query, which is
29836    /// measured: a leading plus is fine in a parameter and a syntax error in
29837    /// the query text.
29838    #[test]
29839    fn a_count_and_a_radius_are_refused_in_their_own_words() {
29840        let mut f = Fixture::new();
29841        vectored(&mut f);
29842        let ask = |f: &mut Fixture, query: &str| {
29843            f.run(&[
29844                b"FT.SEARCH",
29845                b"h",
29846                query.as_bytes(),
29847                b"PARAMS",
29848                b"2",
29849                b"vec",
29850                ORIGIN,
29851                b"DIALECT",
29852                b"2",
29853                b"NOCONTENT",
29854            ])
29855        };
29856        for (query, at, near) in [
29857            ("*=>[KNN -1 @v $vec]", 8, "-1"),
29858            ("*=>[KNN 1.5 @v $vec]", 8, "1.5"),
29859            ("*=>[KNN +3 @v $vec]", 8, "+3"),
29860            ("*=>[KNN 0x10 @v $vec]", 8, "0x10"),
29861            ("*=>[KNN abc @v $vec]", 8, "abc"),
29862            ("*=>[KNN 3 $vec]", 10, "vec"),
29863            ("*=>[KNN 3 @v vec]", 13, "vec"),
29864            ("@v:[VECTOR_RANGE 2 -1]", 19, "-1"),
29865        ] {
29866            assert_eq!(
29867                ask(&mut f, query),
29868                format!("-SEARCH_SYNTAX Syntax error at offset {at} near {near}\r\n"),
29869                "{query}"
29870            );
29871        }
29872
29873        // Read as a double the way a real server reads it, so the bound plus
29874        // thirty two rounds back onto the bound and gets in.
29875        let large = "-SEARCH_QUERY_BAD Error parsing vector similarity query: \
29876                     query KNN K parameter is too large, must not exceed 288230376151711744\r\n";
29877        assert_eq!(
29878            ask(&mut f, "*=>[KNN 288230376151711776 @v $vec]"),
29879            "*6\r\n:5\r\n$2\r\nd1\r\n$2\r\nd2\r\n$2\r\nd3\r\n$2\r\nd4\r\n$2\r\nd5\r\n"
29880        );
29881        assert_eq!(ask(&mut f, "*=>[KNN 288230376151711777 @v $vec]"), large);
29882        assert_eq!(ask(&mut f, "*=>[KNN 99999999999999999999 @v $vec]"), large);
29883
29884        for (radius, printed) in [("-1", "-1"), ("-0.5", "-0.5"), ("-1e2", "-100")] {
29885            let query = format!("@v:[VECTOR_RANGE {radius} $vec]");
29886            assert_eq!(
29887                ask(&mut f, &query),
29888                format!(
29889                    "-SEARCH_QUERY_BAD Error parsing vector similarity query: \
29890                     negative radius ({printed}) given in a range query\r\n"
29891                ),
29892                "{query}"
29893            );
29894        }
29895        // A radius of minus zero is not below zero and is a radius of zero.
29896        assert_eq!(
29897            ask(&mut f, "@v:[VECTOR_RANGE -0 $vec]"),
29898            "*2\r\n:1\r\n$2\r\nd5\r\n"
29899        );
29900    }
29901
29902    /// A count passed with `PARAMS` is read the way a real server reads one,
29903    /// which is not the way the same digits are read in the query text.
29904    #[test]
29905    fn a_count_that_came_from_params_is_read_by_its_own_rules() {
29906        let mut f = Fixture::new();
29907        vectored(&mut f);
29908        let ask = |f: &mut Fixture, count: &[u8]| {
29909            f.run(&[
29910                b"FT.SEARCH",
29911                b"h",
29912                b"*=>[KNN $k @v $vec]",
29913                b"PARAMS",
29914                b"4",
29915                b"vec",
29916                ORIGIN,
29917                b"k",
29918                count,
29919                b"DIALECT",
29920                b"2",
29921                b"NOCONTENT",
29922            ])
29923        };
29924        let three = "*4\r\n:3\r\n$2\r\nd3\r\n$2\r\nd4\r\n$2\r\nd5\r\n";
29925        assert_eq!(ask(&mut f, b"3"), three);
29926        assert_eq!(ask(&mut f, b"  3"), three);
29927        assert_eq!(ask(&mut f, b"+3"), three);
29928        for bad in [
29929            &b"3.0"[..],
29930            b"0x3",
29931            b"-1",
29932            b"abc",
29933            b"",
29934            b"99999999999999999999",
29935        ] {
29936            let value = String::from_utf8_lossy(bad).into_owned();
29937            assert_eq!(
29938                ask(&mut f, bad),
29939                format!(
29940                    "-SEARCH_NUMERIC_VALUE_INVALID Invalid numeric value ({value}) \
29941                     for parameter `k`\r\n"
29942                ),
29943                "{value}"
29944            );
29945        }
29946        assert_eq!(
29947            ask(&mut f, b"288230376151711777"),
29948            "-SEARCH_QUERY_BAD Error parsing vector similarity query: \
29949             query KNN K parameter is too large, must not exceed 288230376151711744\r\n"
29950        );
29951    }
29952
29953    /// A vector the wrong size is refused against the field it was passed to,
29954    /// naming both sizes in bytes.
29955    #[test]
29956    fn a_vector_the_wrong_size_is_refused_by_the_field_it_reached() {
29957        let mut f = Fixture::new();
29958        vectored(&mut f);
29959        assert_eq!(
29960            f.run(&[
29961                b"FT.SEARCH",
29962                b"h",
29963                b"*=>[KNN 5 @v $vec]",
29964                b"PARAMS",
29965                b"2",
29966                b"vec",
29967                b"abc",
29968                b"DIALECT",
29969                b"2",
29970                b"NOCONTENT",
29971            ]),
29972            "-SEARCH_QUERY_BAD Error parsing vector similarity query: \
29973             query vector blob size (3) does not match index's expected size (8).\r\n"
29974        );
29975    }
29976
29977    /// A nearest neighbour clause puts its distance on every row it answers,
29978    /// under `__v_score` unless the query renamed it. A range clause puts
29979    /// nothing there at all unless the query named it, which is what
29980    /// `YIELD_DISTANCE_AS` is for.
29981    #[test]
29982    fn a_vector_clause_yields_its_distance_under_the_name_it_was_given() {
29983        let mut f = Fixture::new();
29984        vectored(&mut f);
29985        let ask = |f: &mut Fixture, query: &str| {
29986            f.run(&[
29987                b"FT.SEARCH",
29988                b"h",
29989                query.as_bytes(),
29990                b"PARAMS",
29991                b"2",
29992                b"vec",
29993                ORIGIN,
29994                b"DIALECT",
29995                b"2",
29996                b"LIMIT",
29997                b"0",
29998                b"1",
29999            ])
30000        };
30001        assert_eq!(
30002            ask(&mut f, "*=>[KNN 3 @v $vec]"),
30003            "*3\r\n:3\r\n$2\r\nd3\r\n*6\r\n$9\r\n__v_score\r\n$1\r\n4\r\n$1\r\nt\r\n$4\r\nbeta\r\n$1\r\nv\r\n$8\r\n\0\0\0@\0\0\0\0\r\n"
30004        );
30005        assert_eq!(
30006            ask(&mut f, "*=>[KNN 3 @v $vec AS d]"),
30007            "*3\r\n:3\r\n$2\r\nd3\r\n*6\r\n$1\r\nd\r\n$1\r\n4\r\n$1\r\nt\r\n$4\r\nbeta\r\n$1\r\nv\r\n$8\r\n\0\0\0@\0\0\0\0\r\n"
30008        );
30009        assert_eq!(
30010            ask(&mut f, "@v:[VECTOR_RANGE 4 $vec]"),
30011            "*3\r\n:3\r\n$2\r\nd3\r\n*4\r\n$1\r\nt\r\n$4\r\nbeta\r\n$1\r\nv\r\n$8\r\n\0\0\0@\0\0\0\0\r\n"
30012        );
30013        assert_eq!(
30014            ask(&mut f, "@v:[VECTOR_RANGE 4 $vec]=>{$YIELD_DISTANCE_AS: d}"),
30015            "*3\r\n:3\r\n$2\r\nd3\r\n*6\r\n$1\r\nd\r\n$1\r\n4\r\n$1\r\nt\r\n$4\r\nbeta\r\n$1\r\nv\r\n$8\r\n\0\0\0@\0\0\0\0\r\n"
30016        );
30017    }
30018
30019    /// What decides whether a `RETURN` answers the distance is the name the row
30020    /// would carry it under and not the field it would have been read from,
30021    /// because it is on the row before any key is read.
30022    ///
30023    /// So naming it answers it, renaming it answers nothing at all, and giving
30024    /// its name to another field answers the distance under that name.
30025    #[test]
30026    fn a_return_answers_the_distance_by_the_name_the_row_carries_it_under() {
30027        let mut f = Fixture::new();
30028        vectored(&mut f);
30029        let ask = |f: &mut Fixture, ret: &[&[u8]]| {
30030            let mut args: Vec<&[u8]> = vec![b"FT.SEARCH", b"h", b"*=>[KNN 1 @v $vec]"];
30031            args.extend_from_slice(ret);
30032            args.extend_from_slice(&[b"PARAMS", b"2", b"vec", ORIGIN, b"DIALECT", b"2"]);
30033            f.run(&args)
30034        };
30035        assert_eq!(
30036            ask(&mut f, &[b"RETURN", b"1", b"__v_score"]),
30037            "*3\r\n:1\r\n$2\r\nd5\r\n*2\r\n$9\r\n__v_score\r\n$1\r\n0\r\n"
30038        );
30039        assert_eq!(
30040            ask(&mut f, &[b"RETURN", b"3", b"__v_score", b"AS", b"x"]),
30041            "*3\r\n:1\r\n$2\r\nd5\r\n*0\r\n"
30042        );
30043        assert_eq!(
30044            ask(&mut f, &[b"RETURN", b"3", b"t", b"AS", b"__v_score"]),
30045            "*3\r\n:1\r\n$2\r\nd5\r\n*2\r\n$9\r\n__v_score\r\n$1\r\n0\r\n"
30046        );
30047        assert_eq!(
30048            ask(&mut f, &[b"RETURN", b"1", b"t"]),
30049            "*3\r\n:1\r\n$2\r\nd5\r\n*2\r\n$1\r\nt\r\n$4\r\nbeta\r\n"
30050        );
30051        // The distance goes in front of the rest whatever order they were
30052        // named in, and `NOCONTENT` takes it away with everything else.
30053        assert_eq!(
30054            ask(&mut f, &[b"RETURN", b"2", b"t", b"__v_score"]),
30055            "*3\r\n:1\r\n$2\r\nd5\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n0\r\n$1\r\nt\r\n$4\r\nbeta\r\n"
30056        );
30057        assert_eq!(ask(&mut f, &[b"NOCONTENT"]), "*2\r\n:1\r\n$2\r\nd5\r\n");
30058    }
30059
30060    /// A `SORTBY` can name a distance the query yielded, which sorts by the
30061    /// number rather than by anything the key holds. A name the query did not
30062    /// yield is refused the way any other unknown property is.
30063    #[test]
30064    fn a_sortby_can_name_a_distance_the_query_yielded() {
30065        let mut f = Fixture::new();
30066        vectored(&mut f);
30067        let ask = |f: &mut Fixture, query: &str, by: &[u8], desc: bool| {
30068            let mut args: Vec<&[u8]> = vec![b"FT.SEARCH", b"h", query.as_bytes(), b"SORTBY", by];
30069            if desc {
30070                args.push(b"DESC");
30071            }
30072            args.extend_from_slice(&[
30073                b"PARAMS",
30074                b"2",
30075                b"vec",
30076                ORIGIN,
30077                b"DIALECT",
30078                b"2",
30079                b"NOCONTENT",
30080            ]);
30081            f.run(&args)
30082        };
30083        assert_eq!(
30084            ask(&mut f, "*=>[KNN 3 @v $vec]", b"__v_score", false),
30085            "*4\r\n:3\r\n$2\r\nd5\r\n$2\r\nd4\r\n$2\r\nd3\r\n"
30086        );
30087        assert_eq!(
30088            ask(&mut f, "*=>[KNN 3 @v $vec]", b"__v_score", true),
30089            "*4\r\n:3\r\n$2\r\nd3\r\n$2\r\nd4\r\n$2\r\nd5\r\n"
30090        );
30091        assert_eq!(
30092            ask(&mut f, "*=>[KNN 3 @v $vec AS d]", b"d", false),
30093            "*4\r\n:3\r\n$2\r\nd5\r\n$2\r\nd4\r\n$2\r\nd3\r\n"
30094        );
30095        // Renaming it takes the old name away, and a query with no vector
30096        // clause in it never had the property at all.
30097        let missing = "-SEARCH_PROP_NOT_FOUND Property `__v_score` \
30098                       not loaded nor in schema\r\n";
30099        assert_eq!(
30100            ask(&mut f, "*=>[KNN 3 @v $vec AS d]", b"__v_score", false),
30101            missing
30102        );
30103        assert_eq!(ask(&mut f, "alpha", b"__v_score", false), missing);
30104        // The query is read before the property is looked up, which is
30105        // measured: a query that will not parse is answered first.
30106        assert_eq!(
30107            ask(&mut f, "foo(", b"zz", false),
30108            "-SEARCH_SYNTAX Syntax error at offset 3 near foo\r\n"
30109        );
30110    }
30111
30112    /// Two vector clauses in one query answer two distances, outermost first.
30113    #[test]
30114    fn two_vector_clauses_answer_two_distances() {
30115        let mut f = Fixture::new();
30116        vectored(&mut f);
30117        assert_eq!(
30118            f.run(&[
30119                b"FT.SEARCH",
30120                b"h",
30121                b"@v:[VECTOR_RANGE 9 $vec]=>{$YIELD_DISTANCE_AS: rr}=>[KNN 2 @v $vec]",
30122                b"RETURN",
30123                b"2",
30124                b"rr",
30125                b"__v_score",
30126                b"PARAMS",
30127                b"2",
30128                b"vec",
30129                ORIGIN,
30130                b"DIALECT",
30131                b"2",
30132            ]),
30133            "*5\r\n:2\r\n$2\r\nd4\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n1\r\n$2\r\nrr\r\n$1\r\n1\r\n$2\r\nd5\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n0\r\n$2\r\nrr\r\n$1\r\n0\r\n"
30134        );
30135    }
30136
30137    /// An aggregation carries the distance on every row whether or not the
30138    /// pipeline ever mentions it, and carries it in front of everything a
30139    /// `LOAD` asked for.
30140    #[test]
30141    fn an_aggregation_answers_a_distance_nothing_asked_for() {
30142        let mut f = Fixture::new();
30143        vectored(&mut f);
30144        let ask = |f: &mut Fixture, query: &str, rest: &[&[u8]]| {
30145            let mut args: Vec<&[u8]> = vec![b"FT.AGGREGATE", b"h", query.as_bytes()];
30146            args.extend_from_slice(rest);
30147            args.extend_from_slice(&[b"PARAMS", b"2", b"vec", ORIGIN, b"DIALECT", b"2"]);
30148            f.run(&args)
30149        };
30150        assert_eq!(
30151            ask(&mut f, "*=>[KNN 2 @v $vec]", &[]),
30152            "*3\r\n:1\r\n*2\r\n$9\r\n__v_score\r\n$1\r\n0\r\n*2\r\n$9\r\n__v_score\r\n$1\r\n1\r\n"
30153        );
30154        assert_eq!(
30155            ask(&mut f, "*=>[KNN 2 @v $vec]", &[b"LOAD", b"1", b"@t"]),
30156            "*3\r\n:1\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n0\r\n$1\r\nt\r\n$4\r\nbeta\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n1\r\n$1\r\nt\r\n$5\r\nalpha\r\n"
30157        );
30158        assert_eq!(
30159            ask(&mut f, "*=>[KNN 2 @v $vec AS d]", &[]),
30160            "*3\r\n:1\r\n*2\r\n$1\r\nd\r\n$1\r\n0\r\n*2\r\n$1\r\nd\r\n$1\r\n1\r\n"
30161        );
30162        // A range shows nothing until the query names it.
30163        assert_eq!(
30164            ask(&mut f, "@v:[VECTOR_RANGE 1 $vec]", &[]),
30165            "*3\r\n:1\r\n*0\r\n*0\r\n"
30166        );
30167        assert_eq!(
30168            ask(
30169                &mut f,
30170                "@v:[VECTOR_RANGE 1 $vec]=>{$YIELD_DISTANCE_AS: rr}",
30171                &[]
30172            ),
30173            "*3\r\n:1\r\n*2\r\n$2\r\nrr\r\n$1\r\n1\r\n*2\r\n$2\r\nrr\r\n$1\r\n0\r\n"
30174        );
30175    }
30176
30177    /// A nearest neighbour clause hands its documents back nearest first and an
30178    /// aggregation keeps them that way, where a search sorts them into document
30179    /// order. A tie goes to the document written first.
30180    #[test]
30181    fn an_aggregation_keeps_the_order_a_nearest_neighbour_clause_made() {
30182        let mut f = Fixture::new();
30183        vectored(&mut f);
30184        // Sitting on `d3`, so `d2` and `d4` are the same distance away.
30185        const MIDDLE: &[u8] = b"\x00\x00\x00\x40\x00\x00\x00\x00";
30186        let ask = |f: &mut Fixture, query: &str, vec: &[u8]| {
30187            f.run(&[
30188                b"FT.AGGREGATE",
30189                b"h",
30190                query.as_bytes(),
30191                b"LOAD",
30192                b"1",
30193                b"@t",
30194                b"PARAMS",
30195                b"2",
30196                b"vec",
30197                vec,
30198                b"DIALECT",
30199                b"2",
30200            ])
30201        };
30202        assert_eq!(
30203            ask(&mut f, "*=>[KNN 3 @v $vec]", MIDDLE),
30204            "*4\r\n:1\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n0\r\n$1\r\nt\r\n$4\r\nbeta\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n1\r\n$1\r\nt\r\n$5\r\nalpha\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n1\r\n$1\r\nt\r\n$5\r\nalpha\r\n"
30205        );
30206        // A range does no ordering, so those rows stay in document order.
30207        assert_eq!(
30208            ask(
30209                &mut f,
30210                "@v:[VECTOR_RANGE 1 $vec]=>{$YIELD_DISTANCE_AS: rr}",
30211                MIDDLE
30212            ),
30213            "*4\r\n:1\r\n*4\r\n$2\r\nrr\r\n$1\r\n1\r\n$1\r\nt\r\n$5\r\nalpha\r\n*4\r\n$2\r\nrr\r\n$1\r\n0\r\n$1\r\nt\r\n$4\r\nbeta\r\n*4\r\n$2\r\nrr\r\n$1\r\n1\r\n$1\r\nt\r\n$5\r\nalpha\r\n"
30214        );
30215    }
30216
30217    /// Every step of the pipeline can name a distance the query yielded, and a
30218    /// query with no vector clause in it is refused for the name three
30219    /// different ways depending on which step asked.
30220    #[test]
30221    fn a_pipeline_step_can_name_a_distance_the_query_yielded() {
30222        let mut f = Fixture::new();
30223        vectored(&mut f);
30224        let ask = |f: &mut Fixture, query: &str, rest: &[&[u8]]| {
30225            let mut args: Vec<&[u8]> = vec![b"FT.AGGREGATE", b"h", query.as_bytes()];
30226            args.extend_from_slice(rest);
30227            args.extend_from_slice(&[b"PARAMS", b"2", b"vec", ORIGIN, b"DIALECT", b"2"]);
30228            f.run(&args)
30229        };
30230        let knn = "*=>[KNN 2 @v $vec]";
30231        assert_eq!(
30232            ask(&mut f, knn, &[b"APPLY", b"@__v_score * 2", b"AS", b"x"]),
30233            "*3\r\n:1\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n0\r\n$1\r\nx\r\n$1\r\n0\r\n*4\r\n$9\r\n__v_score\r\n$1\r\n1\r\n$1\r\nx\r\n$1\r\n2\r\n"
30234        );
30235        assert_eq!(
30236            ask(&mut f, knn, &[b"FILTER", b"@__v_score > 0"]),
30237            "*2\r\n:1\r\n*2\r\n$9\r\n__v_score\r\n$1\r\n1\r\n"
30238        );
30239        assert_eq!(
30240            ask(&mut f, knn, &[b"SORTBY", b"2", b"@__v_score", b"DESC"]),
30241            "*3\r\n:2\r\n*2\r\n$9\r\n__v_score\r\n$1\r\n1\r\n*2\r\n$9\r\n__v_score\r\n$1\r\n0\r\n"
30242        );
30243        assert_eq!(
30244            ask(
30245                &mut f,
30246                knn,
30247                &[
30248                    b"GROUPBY",
30249                    b"1",
30250                    b"@t",
30251                    b"REDUCE",
30252                    b"MAX",
30253                    b"1",
30254                    b"@__v_score",
30255                    b"AS",
30256                    b"m"
30257                ]
30258            ),
30259            "*3\r\n:2\r\n*4\r\n$1\r\nt\r\n$4\r\nbeta\r\n$1\r\nm\r\n$1\r\n0\r\n*4\r\n$1\r\nt\r\n$5\r\nalpha\r\n$1\r\nm\r\n$1\r\n1\r\n"
30260        );
30261        assert_eq!(
30262            ask(&mut f, "*", &[b"APPLY", b"@__v_score", b"AS", b"x"]),
30263            "-SEARCH_PROP_NOT_FOUND Property not loaded nor in pipeline: \
30264             `__v_score`\r\n"
30265        );
30266        assert_eq!(
30267            ask(&mut f, "*", &[b"GROUPBY", b"1", b"@__v_score"]),
30268            "-SEARCH_PROP_NOT_FOUND No such property `__v_score`\r\n"
30269        );
30270        assert_eq!(
30271            ask(&mut f, "*", &[b"SORTBY", b"2", b"@__v_score", b"ASC"]),
30272            "-SEARCH_PROP_NOT_FOUND Property `__v_score` not loaded nor in \
30273             schema\r\n"
30274        );
30275    }
30276
30277    /// An aggregation reads every word before it reads the query, and reads the
30278    /// query before it ties anything on the pipeline to a place on the row.
30279    ///
30280    /// So a command with a fault in all three answers the one about the words,
30281    /// a command with a fault in the last two answers the one about the query,
30282    /// and the pipeline speaks last. That is measured, and it is the whole
30283    /// reason the arguments are read twice.
30284    #[test]
30285    fn the_words_come_before_the_query_and_the_query_before_the_pipeline() {
30286        let mut f = Fixture::new();
30287        vectored(&mut f);
30288        let ask = |f: &mut Fixture, rest: &[&[u8]]| {
30289            let mut args: Vec<&[u8]> = vec![b"FT.AGGREGATE", b"h"];
30290            args.extend_from_slice(rest);
30291            f.run(&args)
30292        };
30293        assert_eq!(
30294            ask(
30295                &mut f,
30296                &[b"foo(", b"APPLY", b"@zz", b"AS", b"x", b"LIMIT", b"x", b"1"]
30297            ),
30298            "-SEARCH_PARSE_ARGS LIMIT needs two numeric arguments\r\n"
30299        );
30300        assert_eq!(
30301            ask(&mut f, &[b"foo(", b"APPLY", b"@zz", b"AS", b"x"]),
30302            "-SEARCH_SYNTAX Syntax error at offset 3 near foo\r\n"
30303        );
30304        assert_eq!(
30305            ask(&mut f, &[b"*", b"APPLY", b"@zz", b"AS", b"x"]),
30306            "-SEARCH_PROP_NOT_FOUND Property not loaded nor in pipeline: `zz`\r\n"
30307        );
30308        // An expression that will not read is the pipeline's fault too, so it
30309        // speaks after the query and after a property named before it.
30310        assert_eq!(
30311            ask(&mut f, &[b"foo(", b"APPLY", b"@@@", b"AS", b"x"]),
30312            "-SEARCH_SYNTAX Syntax error at offset 3 near foo\r\n"
30313        );
30314        assert_eq!(
30315            ask(
30316                &mut f,
30317                &[
30318                    b"*", b"APPLY", b"@zz", b"AS", b"x", b"APPLY", b"@@@", b"AS", b"y"
30319                ]
30320            ),
30321            "-SEARCH_PROP_NOT_FOUND Property not loaded nor in pipeline: `zz`\r\n"
30322        );
30323        assert_eq!(
30324            ask(&mut f, &[b"*", b"APPLY", b"@@@", b"AS", b"x"]),
30325            "-SEARCH_EXPR Syntax error at offset 0 near ''\r\n"
30326        );
30327    }
30328
30329    /// A vector clause says which of the ways of answering one it took, and a
30330    /// range says nothing at all when there is no distance to hand back.
30331    #[test]
30332    fn a_vector_step_says_which_way_it_was_answered() {
30333        let mut f = Fixture::new();
30334        vectored(&mut f);
30335        let tree = |f: &mut Fixture, query: &[u8]| {
30336            let reply = timeless(&f.run(&[
30337                b"FT.PROFILE",
30338                b"h",
30339                b"AGGREGATE",
30340                b"QUERY",
30341                query,
30342                b"PARAMS",
30343                b"2",
30344                b"vec",
30345                ORIGIN,
30346                b"DIALECT",
30347                b"2",
30348            ]));
30349            let at = reply.find("+Iterators profile").expect("a tree");
30350            let end = reply.find("+Result processors").expect("a list of steps");
30351            reply[at..end].to_string()
30352        };
30353        assert_eq!(
30354            tree(&mut f, b"*=>[KNN 3 @v $vec]"),
30355            "+Iterators profile\r\n*8\r\n+Type\r\n+VECTOR\r\n+Time\r\n<t>\r\n\
30356             +Number of reading operations\r\n:3\r\n\
30357             +Vector search mode\r\n+STANDARD_KNN\r\n"
30358        );
30359        // Renaming the distance changes nothing about how it was answered.
30360        assert_eq!(
30361            tree(&mut f, b"*=>[KNN 3 @v $vec AS d]"),
30362            tree(&mut f, b"*=>[KNN 3 @v $vec]")
30363        );
30364        // A range with nothing to yield is not a vector step at all, and one
30365        // that yields names the distance in its own type.
30366        assert_eq!(
30367            tree(&mut f, b"@v:[VECTOR_RANGE 9 $vec]"),
30368            "+Iterators profile\r\n*6\r\n+Type\r\n+ID-LIST-SORTED\r\n+Time\r\n<t>\r\n\
30369             +Number of reading operations\r\n:4\r\n"
30370        );
30371        assert_eq!(
30372            tree(
30373                &mut f,
30374                b"@v:[VECTOR_RANGE 9 $vec]=>{$YIELD_DISTANCE_AS: rr}"
30375            ),
30376            "+Iterators profile\r\n*8\r\n\
30377             +Type\r\n+METRIC SORTED BY ID - VECTOR DISTANCE\r\n+Time\r\n<t>\r\n\
30378             +Number of reading operations\r\n:4\r\n\
30379             +Vector search mode\r\n+RANGE_QUERY\r\n"
30380        );
30381    }
30382
30383    /// What a vector clause narrowed itself down with hangs under it as a
30384    /// single child, and the step that works the distances out is behind the
30385    /// index whenever the query yields one.
30386    #[test]
30387    fn a_clause_in_front_of_a_vector_hangs_under_it_as_one_child() {
30388        let mut f = Fixture::new();
30389        vectored(&mut f);
30390        let ask = |f: &mut Fixture, query: &[u8]| {
30391            timeless(&f.run(&[
30392                b"FT.PROFILE",
30393                b"h",
30394                b"AGGREGATE",
30395                b"QUERY",
30396                query,
30397                b"PARAMS",
30398                b"2",
30399                b"vec",
30400                ORIGIN,
30401                b"DIALECT",
30402                b"2",
30403            ]))
30404        };
30405        let cut = |reply: &str| {
30406            let at = reply.find("+Iterators profile").expect("a tree");
30407            reply[at..].to_string()
30408        };
30409        assert_eq!(
30410            cut(&ask(&mut f, b"@t:alpha=>[KNN 3 @v $vec]")),
30411            "+Iterators profile\r\n*10\r\n+Type\r\n+VECTOR\r\n+Time\r\n<t>\r\n\
30412             +Number of reading operations\r\n:3\r\n\
30413             +Vector search mode\r\n+HYBRID_ADHOC_BF\r\n+Child iterator\r\n\
30414             *10\r\n+Type\r\n+TEXT\r\n+Term\r\n$5\r\nalpha\r\n+Time\r\n<t>\r\n\
30415             +Number of reading operations\r\n:3\r\n\
30416             +Estimated number of matches\r\n:3\r\n\
30417             +Result processors profile\r\n*2\r\n\
30418             *6\r\n+Type\r\n+Index\r\n+Time\r\n<t>\r\n+Results processed\r\n:3\r\n\
30419             *6\r\n+Type\r\n+Metrics Applier\r\n+Time\r\n<t>\r\n\
30420             +Results processed\r\n:3\r\n+Coordinator\r\n*0\r\n"
30421        );
30422        // A range nobody named yields nothing, so nothing works a distance out
30423        // and the step is not there.
30424        assert!(ask(&mut f, b"@v:[VECTOR_RANGE 9 $vec]").ends_with(
30425            "+Result processors profile\r\n*1\r\n*6\r\n+Type\r\n+Index\r\n\
30426             +Time\r\n<t>\r\n+Results processed\r\n:4\r\n+Coordinator\r\n*0\r\n"
30427        ));
30428        // A nearest neighbour clause with nothing in front of it yields all
30429        // the same, so the step is there without a child above it.
30430        assert!(ask(&mut f, b"*=>[KNN 3 @v $vec]").contains("+Type\r\n+Metrics Applier\r\n"));
30431    }
30432
30433    /// A `LIMIT 0 0` on an aggregation is a client asking for the total and
30434    /// nothing else, so the step that would have paged the rows counts them
30435    /// instead, whether or not a `SORTBY` put an order in front of it.
30436    #[test]
30437    fn a_window_of_nothing_on_an_aggregation_counts_rather_than_pages() {
30438        let mut f = profiling();
30439        let steps = |f: &mut Fixture, words: &[&[u8]]| {
30440            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"ix", b"AGGREGATE", b"QUERY", b"*"];
30441            argv.extend_from_slice(words);
30442            let reply = timeless(&f.run(&argv));
30443            let at = reply.find("+Result processors").expect("a list of steps");
30444            reply[at..].to_string()
30445        };
30446        assert_eq!(
30447            steps(&mut f, &[b"LIMIT", b"0", b"0"]),
30448            "+Result processors profile\r\n*2\r\n\
30449             *6\r\n+Type\r\n+Index\r\n+Time\r\n<t>\r\n+Results processed\r\n:3\r\n\
30450             *6\r\n+Type\r\n+Counter\r\n+Time\r\n<t>\r\n+Results processed\r\n:1\r\n\
30451             +Coordinator\r\n*0\r\n"
30452        );
30453        assert!(
30454            steps(
30455                &mut f,
30456                &[b"SORTBY", b"2", b"@n", b"ASC", b"LIMIT", b"0", b"0"]
30457            )
30458            .contains("+Type\r\n+Counter\r\n")
30459        );
30460        // A window that keeps something is still a window.
30461        assert!(steps(&mut f, &[b"LIMIT", b"0", b"2"]).contains(
30462            "+Type\r\n+Pager/Limiter\r\n+Time\r\n<t>\r\n\
30463             +Results processed\r\n:2\r\n"
30464        ));
30465    }
30466
30467    // ----------------------------------------------------------- spellcheck
30468
30469    /// The score is how many documents hold the suggestion over how many
30470    /// documents there are, and how close the suggestion is to the word does
30471    /// not come into it at all, so the nearer of the two words here is second.
30472    #[test]
30473    fn a_spellcheck_scores_a_suggestion_by_how_common_it_is() {
30474        let mut f = Fixture::new();
30475        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT", b"NOSTEM"]);
30476        f.run(&[b"HSET", b"d1", b"t", b"hello"]);
30477        f.run(&[b"HSET", b"d2", b"t", b"hallo hello"]);
30478        assert_eq!(
30479            f.run(&[b"FT.SPELLCHECK", b"e", b"hellp", b"DISTANCE", b"2"]),
30480            "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nhellp\r\n\
30481             *2\r\n*2\r\n$1\r\n1\r\n$5\r\nhello\r\n*2\r\n$3\r\n0.5\r\n$5\r\nhallo\r\n"
30482        );
30483    }
30484
30485    /// On RESP3 the whole thing is wrapped in a map under one name, a word
30486    /// carries a list of one pair maps, and the score is a double rather than
30487    /// a string.
30488    #[test]
30489    fn a_spellcheck_answers_a_map_of_maps_on_resp3() {
30490        let mut f = Fixture::new();
30491        f.run(&[b"HELLO", b"3"]);
30492        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT", b"NOSTEM"]);
30493        f.run(&[b"HSET", b"d1", b"t", b"hello"]);
30494        assert_eq!(
30495            f.run(&[b"FT.SPELLCHECK", b"e", b"hellp"]),
30496            "%1\r\n$7\r\nresults\r\n%1\r\n$5\r\nhellp\r\n\
30497             *1\r\n%1\r\n$5\r\nhello\r\n,1\r\n"
30498        );
30499    }
30500
30501    /// A word the index already holds is not a mistake and is left out of the
30502    /// answer, and that check never looks at the field the query named, while
30503    /// the search for candidates does.
30504    #[test]
30505    fn a_word_the_index_holds_is_never_asked_about_whatever_field_it_names() {
30506        let mut f = Fixture::new();
30507        f.run(&[
30508            b"FT.CREATE",
30509            b"e",
30510            b"SCHEMA",
30511            b"a",
30512            b"TEXT",
30513            b"NOSTEM",
30514            b"b",
30515            b"TEXT",
30516            b"NOSTEM",
30517        ]);
30518        f.run(&[b"HSET", b"d1", b"b", b"world"]);
30519        assert_eq!(f.run(&[b"FT.SPELLCHECK", b"e", b"@a:world"]), "*0\r\n");
30520        assert_eq!(
30521            f.run(&[b"FT.SPELLCHECK", b"e", b"@a:worlt"]),
30522            "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nworlt\r\n*0\r\n"
30523        );
30524    }
30525
30526    /// A dictionary named by `INCLUDE` adds words the index never read, scored
30527    /// zero and reported in the spelling the dictionary was given, and one
30528    /// named by `EXCLUDE` says a word is spelled right after all.
30529    #[test]
30530    fn a_spellcheck_reads_the_dictionaries_it_is_pointed_at() {
30531        let mut f = Fixture::new();
30532        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT", b"NOSTEM"]);
30533        f.run(&[b"FT.DICTADD", b"d", b"Hellp", b"hellq"]);
30534        assert_eq!(
30535            f.run(&[b"FT.SPELLCHECK", b"e", b"hellz", b"TERMS", b"INCLUDE", b"d"]),
30536            "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nhellz\r\n\
30537             *2\r\n*2\r\n$1\r\n0\r\n$5\r\nHellp\r\n*2\r\n$1\r\n0\r\n$5\r\nhellq\r\n"
30538        );
30539        assert_eq!(
30540            f.run(&[b"FT.SPELLCHECK", b"e", b"hellq", b"TERMS", b"EXCLUDE", b"d"]),
30541            "*0\r\n"
30542        );
30543        assert_eq!(
30544            f.run(&[b"FT.SPELLCHECK", b"e", b"x", b"TERMS", b"INCLUDE", b"nope"]),
30545            "-Dict does not exist: nope\r\n"
30546        );
30547    }
30548
30549    /// The first `DISTANCE` counts and the rest are dropped, an argument
30550    /// nobody recognises is stepped over rather than refused, and a distance
30551    /// outside one to four is the one thing here that does fail.
30552    #[test]
30553    fn a_spellcheck_reads_its_arguments_leniently() {
30554        let mut f = Fixture::new();
30555        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT", b"NOSTEM"]);
30556        f.run(&[b"HSET", b"d1", b"t", b"hello"]);
30557        let one = "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nhellp\r\n\
30558                   *1\r\n*2\r\n$1\r\n1\r\n$5\r\nhello\r\n";
30559        assert_eq!(f.run(&[b"FT.SPELLCHECK", b"e", b"hellp", b"BOGUS"]), one);
30560        let none = "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nhelqp\r\n*0\r\n";
30561        let args: &[&[u8]] = &[
30562            b"FT.SPELLCHECK",
30563            b"e",
30564            b"helqp",
30565            b"DISTANCE",
30566            b"1",
30567            b"DISTANCE",
30568            b"4",
30569        ];
30570        assert_eq!(f.run(args), none);
30571        assert_eq!(
30572            f.run(&[b"FT.SPELLCHECK", b"e", b"hellp", b"DISTANCE", b"5"]),
30573            "-bad distance given, distance must be a natural number between 1 to 4\r\n"
30574        );
30575        assert_eq!(
30576            f.run(&[b"FT.SPELLCHECK", b"nope", b"hellp"]),
30577            "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n"
30578        );
30579    }
30580
30581    // -------------------------------------------------------------- suggest
30582
30583    /// The reply is the size of the dictionary afterwards, which is neither
30584    /// what was added nor whether anything changed.
30585    #[test]
30586    fn an_add_answers_how_many_suggestions_are_in_there_now() {
30587        let mut f = Fixture::new();
30588        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]), ":1\r\n");
30589        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"one", b"9"]), ":1\r\n");
30590        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"only", b"2"]), ":2\r\n");
30591        assert_eq!(f.run(&[b"FT.SUGLEN", b"s"]), ":2\r\n");
30592        assert_eq!(f.run(&[b"FT.SUGLEN", b"nokey"]), ":0\r\n");
30593    }
30594
30595    /// A suggestion dictionary is the one thing the search module puts in the
30596    /// keyspace, so every keyspace command reaches it.
30597    #[test]
30598    fn a_suggestion_dictionary_is_a_key_with_a_type_of_its_own() {
30599        let mut f = Fixture::new();
30600        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30601        assert_eq!(f.run(&[b"TYPE", b"s"]), "+trietype0\r\n");
30602        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$3\r\nraw\r\n");
30603        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
30604        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\ns\r\n");
30605        assert_eq!(f.run(&[b"EXPIRE", b"s", b"100"]), ":1\r\n");
30606        assert_eq!(f.run(&[b"TTL", b"s"]), ":100\r\n");
30607        assert_eq!(f.run(&[b"DEL", b"s"]), ":1\r\n");
30608        assert_eq!(f.run(&[b"FT.SUGLEN", b"s"]), ":0\r\n");
30609    }
30610
30611    /// The last suggestion out takes the key with it, which most module types
30612    /// do not do.
30613    #[test]
30614    fn deleting_the_last_suggestion_deletes_the_key() {
30615        let mut f = Fixture::new();
30616        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30617        assert_eq!(f.run(&[b"FT.SUGDEL", b"s", b"nope"]), ":0\r\n");
30618        assert_eq!(f.run(&[b"FT.SUGDEL", b"s", b"one"]), ":1\r\n");
30619        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
30620        assert_eq!(f.run(&[b"FT.SUGDEL", b"nokey", b"a"]), ":0\r\n");
30621    }
30622
30623    /// A key holding anything else is refused rather than overwritten, on all
30624    /// four of them.
30625    #[test]
30626    fn a_suggestion_command_on_another_kind_of_key_is_wrongtype() {
30627        let mut f = Fixture::new();
30628        f.run(&[b"SET", b"s", b"x"]);
30629        for cmd in [
30630            vec![&b"FT.SUGADD"[..], b"s", b"t", b"1"],
30631            vec![&b"FT.SUGGET"[..], b"s", b"t"],
30632            vec![&b"FT.SUGDEL"[..], b"s", b"t"],
30633            vec![&b"FT.SUGLEN"[..], b"s"],
30634        ] {
30635            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{cmd:?}");
30636        }
30637        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nx\r\n");
30638    }
30639
30640    /// The scores in here were read off a real server, single precision and
30641    /// all. An exact match answers a sentinel so it sorts in front.
30642    #[test]
30643    fn a_lookup_answers_a_score_it_works_out_rather_than_the_one_stored() {
30644        let mut f = Fixture::new();
30645        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30646        f.run(&[b"FT.SUGADD", b"s", b"only", b"2"]);
30647        f.run(&[b"FT.SUGADD", b"s", b"ontario", b"3"]);
30648        assert_eq!(
30649            f.run(&[b"FT.SUGGET", b"s", b"on", b"WITHSCORES"]),
30650            "*6\r\n$7\r\nontario\r\n$18\r\n1.2247449159622192\r\n\
30651             $4\r\nonly\r\n$17\r\n1.154700517654419\r\n\
30652             $3\r\none\r\n$18\r\n0.7071067690849304\r\n"
30653        );
30654        assert_eq!(
30655            f.run(&[b"FT.SUGGET", b"s", b"one", b"WITHSCORES"]),
30656            "*2\r\n$3\r\none\r\n$10\r\n2147483648\r\n"
30657        );
30658        assert_eq!(f.run(&[b"FT.SUGGET", b"nokey", b"a"]), "*0\r\n");
30659    }
30660
30661    /// `FUZZY` is one edit, and the edit is a rune rather than a byte.
30662    #[test]
30663    fn fuzzy_allows_one_edit_and_nothing_allows_two() {
30664        let mut f = Fixture::new();
30665        f.run(&[b"FT.SUGADD", b"s", b"only", b"2"]);
30666        assert_eq!(f.run(&[b"FT.SUGGET", b"s", b"one"]), "*0\r\n");
30667        assert_eq!(
30668            f.run(&[b"FT.SUGGET", b"s", b"one", b"FUZZY", b"WITHSCORES"]),
30669            "*2\r\n$4\r\nonly\r\n$19\r\n0.19139298796653748\r\n"
30670        );
30671        assert_eq!(f.run(&[b"FT.SUGGET", b"s", b"xyz", b"FUZZY"]), "*0\r\n");
30672    }
30673
30674    /// Five without a `MAX`, and the terms come back in score order.
30675    #[test]
30676    fn a_lookup_answers_five_unless_it_is_told_otherwise() {
30677        let mut f = Fixture::new();
30678        for (term, score) in [
30679            (&b"a1"[..], &b"1"[..]),
30680            (b"a2", b"2"),
30681            (b"a3", b"3"),
30682            (b"a4", b"4"),
30683            (b"a5", b"5"),
30684            (b"a6", b"6"),
30685        ] {
30686            f.run(&[b"FT.SUGADD", b"s", term, score]);
30687        }
30688        assert_eq!(
30689            f.run(&[b"FT.SUGGET", b"s", b"a"]),
30690            "*5\r\n$2\r\na6\r\n$2\r\na5\r\n$2\r\na4\r\n$2\r\na3\r\n$2\r\na2\r\n"
30691        );
30692        assert_eq!(
30693            f.run(&[b"FT.SUGGET", b"s", b"a", b"MAX", b"2"]),
30694            "*2\r\n$2\r\na6\r\n$2\r\na5\r\n"
30695        );
30696        // A `MAX` larger than the dictionary answers what there is.
30697        assert!(
30698            f.run(&[b"FT.SUGGET", b"s", b"a", b"MAX", b"100"])
30699                .starts_with("*6\r\n")
30700        );
30701    }
30702
30703    /// A payload is replaced only when one is given, and an empty one is no
30704    /// payload at all.
30705    #[test]
30706    fn a_payload_comes_back_beside_the_term_or_a_null_does() {
30707        let mut f = Fixture::new();
30708        f.run(&[b"FT.SUGADD", b"s", b"one", b"1", b"PAYLOAD", b"p"]);
30709        assert_eq!(
30710            f.run(&[b"FT.SUGGET", b"s", b"o", b"WITHPAYLOADS"]),
30711            "*2\r\n$3\r\none\r\n$1\r\np\r\n"
30712        );
30713        f.run(&[b"FT.SUGADD", b"s", b"one", b"2"]);
30714        assert_eq!(
30715            f.run(&[b"FT.SUGGET", b"s", b"o", b"WITHPAYLOADS"]),
30716            "*2\r\n$3\r\none\r\n$1\r\np\r\n"
30717        );
30718        // An empty payload is the same as not having given one at all, so it
30719        // leaves the payload where it is rather than clearing it.
30720        f.run(&[b"FT.SUGADD", b"s", b"one", b"2", b"PAYLOAD", b""]);
30721        assert_eq!(
30722            f.run(&[b"FT.SUGGET", b"s", b"o", b"WITHPAYLOADS"]),
30723            "*2\r\n$3\r\none\r\n$1\r\np\r\n"
30724        );
30725        // A term that never had one answers a null.
30726        f.run(&[b"FT.SUGADD", b"s", b"other", b"1", b"PAYLOAD", b""]);
30727        assert_eq!(
30728            f.run(&[b"FT.SUGGET", b"s", b"ot", b"WITHPAYLOADS"]),
30729            "*2\r\n$5\r\nother\r\n$-1\r\n"
30730        );
30731    }
30732
30733    /// `INCR` adds to the score that is there rather than replacing it, and
30734    /// three tenths a tenth at a time is the reading that shows the score is
30735    /// held in single precision.
30736    #[test]
30737    fn incr_adds_to_the_score_that_is_already_there() {
30738        let mut f = Fixture::new();
30739        for _ in 0..3 {
30740            f.run(&[b"FT.SUGADD", b"s", b"xxx", b"0.1", b"INCR"]);
30741        }
30742        assert_eq!(
30743            f.run(&[b"FT.SUGGET", b"s", b"xx", b"WITHSCORES"]),
30744            "*2\r\n$3\r\nxxx\r\n$18\r\n0.2121320366859436\r\n"
30745        );
30746    }
30747
30748    /// The five error sentences, none of which are written the same way.
30749    #[test]
30750    fn the_suggestion_errors_are_the_lines_the_module_sends() {
30751        let mut f = Fixture::new();
30752        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30753        assert_eq!(
30754            f.run(&[b"FT.SUGADD", b"s", b"t", b"abc"]),
30755            "-ERR invalid score\r\n"
30756        );
30757        // The unknown word is complained about before the score is converted.
30758        assert_eq!(
30759            f.run(&[b"FT.SUGADD", b"s", b"t", b"abc", b"NOPE"]),
30760            "-Unknown argument `NOPE`\r\n"
30761        );
30762        assert_eq!(
30763            f.run(&[b"FT.SUGADD", b"s", b"t", b"1", b"PAYLOAD"]),
30764            "-Invalid payload: Expected an argument, but none provided\r\n"
30765        );
30766        // Too many words is an arity error and not an unknown argument.
30767        assert!(
30768            f.run(&[
30769                b"FT.SUGADD",
30770                b"s",
30771                b"t",
30772                b"1",
30773                b"PAYLOAD",
30774                b"a",
30775                b"PAYLOAD",
30776                b"b"
30777            ])
30778            .contains("wrong number of arguments")
30779        );
30780        assert_eq!(
30781            f.run(&[b"FT.SUGGET", b"s", b"o", b"NOPE"]),
30782            "-SEARCH_PARSE_ARGS Unrecognized argument: NOPE\r\n"
30783        );
30784        // A count read as a whole number and then found to be out of range,
30785        // against one that had to be read as a double first, where anything
30786        // under one is a conversion that failed rather than a range that did.
30787        for max in [&b"0"[..], b"-1", b"4294967296", b"1e10", b"inf"] {
30788            assert_eq!(
30789                f.run(&[b"FT.SUGGET", b"s", b"o", b"MAX", max]),
30790                "-SEARCH_PARSE_ARGS MAX: Value is outside acceptable bounds\r\n",
30791                "{}",
30792                String::from_utf8_lossy(max)
30793            );
30794        }
30795        for max in [
30796            &b"abc"[..],
30797            b"0.0",
30798            b"00",
30799            b"-0",
30800            b"+0",
30801            b"0.5",
30802            b"-1.5",
30803            b"1e400",
30804        ] {
30805            assert_eq!(
30806                f.run(&[b"FT.SUGGET", b"s", b"o", b"MAX", max]),
30807                "-SEARCH_PARSE_ARGS MAX: Could not convert argument to expected type\r\n",
30808                "{}",
30809                String::from_utf8_lossy(max)
30810            );
30811        }
30812        for max in [&b"01"[..], b"+1", b"1.5", b"0x10", b"1e2"] {
30813            assert_eq!(
30814                f.run(&[b"FT.SUGGET", b"s", b"o", b"MAX", max]),
30815                "*1\r\n$3\r\none\r\n",
30816                "{}",
30817                String::from_utf8_lossy(max)
30818            );
30819        }
30820        assert_eq!(
30821            f.run(&[b"FT.SUGGET", b"s", b"o", b"MAX"]),
30822            "-SEARCH_PARSE_ARGS MAX: Expected an argument, but none provided\r\n"
30823        );
30824        // A score too large for a double is refused where one spelled out is
30825        // taken, which is the module reading errno after the conversion.
30826        assert_eq!(
30827            f.run(&[b"FT.SUGADD", b"s", b"t", b"1e400"]),
30828            "-ERR invalid score\r\n"
30829        );
30830        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"t", b"inf"]), ":2\r\n");
30831    }
30832
30833    /// An empty term is taken and not stored, so the reply is the length that
30834    /// was already there and nothing new comes back. The key is still made,
30835    /// and a delete that finds nothing is what clears it away again.
30836    #[test]
30837    fn an_empty_suggestion_is_taken_and_dropped_but_still_makes_the_key() {
30838        let mut f = Fixture::new();
30839        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30840        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"", b"1"]), ":1\r\n");
30841        assert_eq!(f.run(&[b"FT.SUGGET", b"s", b""]), "*1\r\n$3\r\none\r\n");
30842        assert_eq!(f.run(&[b"FT.SUGADD", b"e", b"", b"1"]), ":0\r\n");
30843        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":1\r\n");
30844        assert_eq!(f.run(&[b"TYPE", b"e"]), "+trietype0\r\n");
30845        assert_eq!(f.run(&[b"FT.SUGDEL", b"e", b"nothing"]), ":0\r\n");
30846        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
30847    }
30848
30849    /// A key that will not read is counted against the index and against the
30850    /// field, and `FT.INFO` says so.
30851    #[test]
30852    fn a_hash_that_will_not_read_is_counted_where_ft_info_reports_it() {
30853        let mut f = Fixture::new();
30854        f.run(&[
30855            b"FT.CREATE",
30856            b"ix",
30857            b"PREFIX",
30858            b"1",
30859            b"p:",
30860            b"SCHEMA",
30861            b"n",
30862            b"NUMERIC",
30863        ]);
30864        f.run(&[b"HSET", b"p:1", b"n", b"notanumber"]);
30865        assert_eq!(held(&f, b"ix"), (0, 0));
30866
30867        let reply = f.run(&[b"FT.INFO", b"ix"]);
30868        assert!(
30869            reply.contains("SEARCH_NUMERIC_VALUE_INVALID Invalid numeric value: 'notanumber'"),
30870            "{reply}"
30871        );
30872        assert!(reply.contains("hash_indexing_failures"), "{reply}");
30873    }
30874
30875    /// An index can only be made on database zero, and the check comes after
30876    /// the `IFNX` shortcut and before everything else.
30877    #[test]
30878    fn an_index_can_only_be_made_on_database_zero() {
30879        let mut f = Fixture::new();
30880        f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]);
30881        f.run(&[b"SELECT", b"1"]);
30882        let refused = "-Cannot create index on db != 0\r\n";
30883        assert_eq!(
30884            f.run(&[b"FT.CREATE", b"jx", b"SCHEMA", b"t", b"TEXT"]),
30885            refused
30886        );
30887        // The name is taken, and it still answers about the database.
30888        assert_eq!(
30889            f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]),
30890            refused
30891        );
30892        // And so does one whose arguments are nonsense.
30893        assert_eq!(
30894            f.run(&[b"FT.CREATE", b"zz", b"BOGUS", b"SCHEMA", b"t", b"TEXT"]),
30895            refused
30896        );
30897        // `IFNX` over a name that is taken is the one that gets through.
30898        assert_eq!(
30899            f.run(&[b"FT._CREATEIFNX", b"ix", b"SCHEMA", b"t", b"TEXT"]),
30900            "+OK\r\n"
30901        );
30902        assert_eq!(f.server.search.lock().len(), 1);
30903    }
30904
30905    /// The scan reads the database the create was run on, and after that the
30906    /// index follows its keys in every database.
30907    ///
30908    /// The asymmetry is a real server's, measured, and it is the sort of thing
30909    /// nobody would arrive at by choosing.
30910    #[test]
30911    fn the_scan_is_one_database_and_the_following_is_all_of_them() {
30912        let mut f = Fixture::new();
30913        f.run(&[b"SELECT", b"1"]);
30914        f.run(&[b"HSET", b"p:9", b"t", b"on one"]);
30915        f.run(&[b"SELECT", b"0"]);
30916        f.run(&[b"HSET", b"p:0", b"t", b"on zero"]);
30917        f.run(&[
30918            b"FT.CREATE",
30919            b"ix",
30920            b"PREFIX",
30921            b"1",
30922            b"p:",
30923            b"SCHEMA",
30924            b"t",
30925            b"TEXT",
30926        ]);
30927        assert_eq!(held(&f, b"ix"), (1, 1), "the scan read database zero only");
30928
30929        f.run(&[b"SELECT", b"1"]);
30930        f.run(&[b"HSET", b"p:8", b"t", b"later"]);
30931        assert_eq!(
30932            held(&f, b"ix"),
30933            (2, 2),
30934            "and then it follows every database"
30935        );
30936    }
30937
30938    /// Four documents over the two kinds of field a query can ask about, which
30939    /// is the corpus the searches below read.
30940    fn corpus(f: &mut Fixture) {
30941        f.run(&[
30942            b"FT.CREATE",
30943            b"sx",
30944            b"PREFIX",
30945            b"1",
30946            b"d:",
30947            b"SCHEMA",
30948            b"t",
30949            b"TEXT",
30950            b"g",
30951            b"TAG",
30952            b"n",
30953            b"NUMERIC",
30954        ]);
30955        for (key, text, tag, number) in [
30956            (b"d:1".as_slice(), "alpha beta", "aa,bb", "1"),
30957            (b"d:2", "alpha gamma", "bb", "2"),
30958            (b"d:3", "delta", "cc", "3"),
30959            (b"d:4", "alpha beta gamma", "aa,cc", "4"),
30960        ] {
30961            f.run(&[
30962                b"HSET",
30963                key,
30964                b"t",
30965                text.as_bytes(),
30966                b"g",
30967                tag.as_bytes(),
30968                b"n",
30969                number.as_bytes(),
30970            ]);
30971        }
30972    }
30973
30974    /// A corpus with something to sort by: a text field the index keeps a copy
30975    /// of, a number, the same text field under another name, and a text field
30976    /// the index keeps nothing of.
30977    fn sortable(f: &mut Fixture) {
30978        f.run(&[
30979            b"FT.CREATE",
30980            b"sy",
30981            b"PREFIX",
30982            b"1",
30983            b"s:",
30984            b"SCHEMA",
30985            b"t",
30986            b"TEXT",
30987            b"SORTABLE",
30988            b"n",
30989            b"NUMERIC",
30990            b"SORTABLE",
30991            b"body",
30992            b"AS",
30993            b"b",
30994            b"TEXT",
30995            b"SORTABLE",
30996            b"p",
30997            b"TEXT",
30998        ]);
30999        for (key, text, number) in [
31000            (b"s:1".as_slice(), "Banana Split", "2"),
31001            (b"s:2", "apple", "10"),
31002        ] {
31003            f.run(&[
31004                b"HSET",
31005                key,
31006                b"t",
31007                text.as_bytes(),
31008                b"n",
31009                number.as_bytes(),
31010                b"body",
31011                text.as_bytes(),
31012                b"p",
31013                b"alpha",
31014            ]);
31015        }
31016        // A key with nothing under either sortable field, which is what sorts
31017        // last whichever way round the sort runs.
31018        f.run(&[b"HSET", b"s:3", b"p", b"alpha"]);
31019    }
31020
31021    /// A sort runs off the copy of the value the index keeps, and a row with no
31022    /// value at all is last both ways round.
31023    #[test]
31024    fn a_search_sorts_by_a_field_the_index_keeps_a_copy_of() {
31025        let mut f = Fixture::new();
31026        sortable(&mut f);
31027        assert_eq!(
31028            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"n", b"NOCONTENT"]),
31029            "*4\r\n:3\r\n$3\r\ns:1\r\n$3\r\ns:2\r\n$3\r\ns:3\r\n"
31030        );
31031        assert_eq!(
31032            f.run(&[
31033                b"FT.SEARCH",
31034                b"sy",
31035                b"alpha",
31036                b"SORTBY",
31037                b"n",
31038                b"DESC",
31039                b"NOCONTENT"
31040            ]),
31041            "*4\r\n:3\r\n$3\r\ns:2\r\n$3\r\ns:1\r\n$3\r\ns:3\r\n"
31042        );
31043        // The copy of a text field is folded, so `apple` sorts before
31044        // `Banana Split` where a comparison of the bytes would not.
31045        assert_eq!(
31046            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"t", b"NOCONTENT"]),
31047            "*4\r\n:3\r\n$3\r\ns:2\r\n$3\r\ns:1\r\n$3\r\ns:3\r\n"
31048        );
31049    }
31050
31051    /// A field the index keeps no copy of is sorted by the value read off the
31052    /// key, which happens after the walk rather than during it.
31053    #[test]
31054    fn a_search_sorts_by_a_field_it_has_to_read_the_key_for() {
31055        let mut f = Fixture::new();
31056        sortable(&mut f);
31057        f.run(&[b"HSET", b"s:1", b"p", b"alpha zulu"]);
31058        assert_eq!(
31059            f.run(&[
31060                b"FT.SEARCH",
31061                b"sy",
31062                b"alpha",
31063                b"SORTBY",
31064                b"p",
31065                b"NOCONTENT",
31066                b"LIMIT",
31067                b"0",
31068                b"2"
31069            ]),
31070            "*3\r\n:3\r\n$3\r\ns:2\r\n$3\r\ns:3\r\n"
31071        );
31072        // Nothing is folded on this side, because the schema never asked for a
31073        // copy to fold, so the value goes into the sort as it was written.
31074        assert_eq!(
31075            f.run(&[
31076                b"FT.SEARCH",
31077                b"sy",
31078                b"alpha",
31079                b"SORTBY",
31080                b"p",
31081                b"WITHSORTKEYS",
31082                b"NOCONTENT",
31083                b"LIMIT",
31084                b"2",
31085                b"1"
31086            ]),
31087            "*3\r\n:3\r\n$3\r\ns:1\r\n$11\r\n$alpha zulu\r\n"
31088        );
31089    }
31090
31091    /// The value the sort compared goes beside every row, as a number after a
31092    /// hash, as text after a dollar, and as a null on a row that had none.
31093    #[test]
31094    fn a_search_can_send_the_value_it_sorted_by_back() {
31095        let mut f = Fixture::new();
31096        sortable(&mut f);
31097        assert_eq!(
31098            f.run(&[
31099                b"FT.SEARCH",
31100                b"sy",
31101                b"alpha",
31102                b"SORTBY",
31103                b"n",
31104                b"WITHSORTKEYS",
31105                b"NOCONTENT"
31106            ]),
31107            concat!(
31108                "*7\r\n:3\r\n",
31109                "$3\r\ns:1\r\n$2\r\n#2\r\n",
31110                "$3\r\ns:2\r\n$3\r\n#10\r\n",
31111                "$3\r\ns:3\r\n$-1\r\n"
31112            )
31113        );
31114        assert_eq!(
31115            f.run(&[
31116                b"FT.SEARCH",
31117                b"sy",
31118                b"alpha",
31119                b"SORTBY",
31120                b"t",
31121                b"WITHSORTKEYS",
31122                b"NOCONTENT"
31123            ]),
31124            concat!(
31125                "*7\r\n:3\r\n",
31126                "$3\r\ns:2\r\n$6\r\n$apple\r\n",
31127                "$3\r\ns:1\r\n$13\r\n$banana split\r\n",
31128                "$3\r\ns:3\r\n$-1\r\n"
31129            )
31130        );
31131        // Asking for a sort key without sorting is taken and answers a null on
31132        // every row, which is what a real server does.
31133        assert_eq!(
31134            f.run(&[
31135                b"FT.SEARCH",
31136                b"sy",
31137                b"banana",
31138                b"WITHSORTKEYS",
31139                b"NOCONTENT"
31140            ]),
31141            "*3\r\n:1\r\n$3\r\ns:1\r\n$-1\r\n"
31142        );
31143    }
31144
31145    /// The field a search sorted by is written in front of the fields of the
31146    /// key, and the key's own value for it wins when the two share a name.
31147    #[test]
31148    fn a_sort_puts_the_field_it_sorted_by_in_front_of_the_row() {
31149        let mut f = Fixture::new();
31150        sortable(&mut f);
31151        // `b` is what the schema calls the field the key calls `body`, so the
31152        // folded copy comes back under one name and the value as it was written
31153        // comes back under the other.
31154        assert_eq!(
31155            f.run(&[
31156                b"FT.SEARCH",
31157                b"sy",
31158                b"alpha",
31159                b"SORTBY",
31160                b"b",
31161                b"LIMIT",
31162                b"0",
31163                b"1"
31164            ]),
31165            concat!(
31166                "*3\r\n:3\r\n$3\r\ns:2\r\n*10\r\n",
31167                "$1\r\nb\r\n$5\r\napple\r\n",
31168                "$1\r\nt\r\n$5\r\napple\r\n",
31169                "$1\r\nn\r\n$2\r\n10\r\n",
31170                "$4\r\nbody\r\n$5\r\napple\r\n",
31171                "$1\r\np\r\n$5\r\nalpha\r\n"
31172            )
31173        );
31174        // With a `RETURN` list there is nothing to put in, so the field is moved
31175        // to the front of the names that were asked for instead.
31176        assert_eq!(
31177            f.run(&[
31178                b"FT.SEARCH",
31179                b"sy",
31180                b"alpha",
31181                b"SORTBY",
31182                b"b",
31183                b"RETURN",
31184                b"2",
31185                b"p",
31186                b"b",
31187                b"LIMIT",
31188                b"0",
31189                b"1"
31190            ]),
31191            concat!(
31192                "*3\r\n:3\r\n$3\r\ns:2\r\n*4\r\n",
31193                "$1\r\nb\r\n$5\r\napple\r\n",
31194                "$1\r\np\r\n$5\r\nalpha\r\n"
31195            )
31196        );
31197    }
31198
31199    /// The four ways a `SORTBY` on a search is refused.
31200    #[test]
31201    fn a_search_refuses_the_sorts_it_cannot_run() {
31202        let mut f = Fixture::new();
31203        sortable(&mut f);
31204        assert_eq!(
31205            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY"]),
31206            "-SEARCH_PARSE_ARGS Bad SORTBY arguments\r\n"
31207        );
31208        assert_eq!(
31209            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"n", b"SORTBY"]),
31210            "-SEARCH_PARSE_ARGS Multiple SORTBY steps are not allowed\r\n"
31211        );
31212        assert_eq!(
31213            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"n", b"MAX", b"2"]),
31214            "-SEARCH_PARSE_ARGS SORTBY MAX is not supported by FT.SEARCH\r\n"
31215        );
31216        assert_eq!(
31217            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"zz"]),
31218            "-SEARCH_PROP_NOT_FOUND Property `zz` not loaded nor in schema\r\n"
31219        );
31220        // The property is looked up once the whole list has read cleanly, so a
31221        // word after it that nobody knows is the error that comes back.
31222        assert_eq!(
31223            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"zz", b"NOPE"]),
31224            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `NOPE` at position 3 for <main>\r\n"
31225        );
31226    }
31227
31228    /// An index over two text fields, a number and a tag, holding one key whose
31229    /// `a` runs long enough to be worth cutting down and whose `b` and `g` hold
31230    /// nothing the query matches.
31231    fn marking(f: &mut Fixture) {
31232        f.run(&[
31233            b"FT.CREATE",
31234            b"mk",
31235            b"ON",
31236            b"HASH",
31237            b"PREFIX",
31238            b"1",
31239            b"m:",
31240            b"SCHEMA",
31241            b"a",
31242            b"TEXT",
31243            b"b",
31244            b"TEXT",
31245            b"n",
31246            b"NUMERIC",
31247            b"g",
31248            b"TAG",
31249        ]);
31250        f.run(&[
31251            b"HSET",
31252            b"m:1",
31253            b"a",
31254            b"c1 c2 c3 fox d1 d2 d3 d4 d5 d6 d7 d8 d9 fox e1 e2 e3",
31255            b"b",
31256            b"t1 t2 t3 t4 t5 t6 t7 t8",
31257            b"n",
31258            b"1",
31259            b"g",
31260            b"red",
31261        ]);
31262    }
31263
31264    /// A field the query matched comes back as fragments and a field it did not
31265    /// comes back as its own front.
31266    #[test]
31267    fn a_summarize_cuts_a_field_down_to_what_matched() {
31268        let mut f = Fixture::new();
31269        marking(&mut f);
31270        let got = f.run(&[b"FT.SEARCH", b"mk", b"fox", b"SUMMARIZE", b"LEN", b"2"]);
31271        assert!(got.contains("c3 fox d1 d2... d9 fox e1 e2... "), "{got}");
31272        // `b` holds no match, so it keeps its front and loses its last word.
31273        assert!(got.contains("t1 t2 t3 t4 t5 t6 t7\r\n"), "{got}");
31274        // And so does the tag, which is a value like any other to this clause.
31275        assert!(got.contains("$1\r\nr\r\n"), "{got}");
31276    }
31277
31278    /// `FRAGS` is applied before the context either side of a fragment is worked
31279    /// out, so the fragment that is left runs over the match of the one that was
31280    /// dropped rather than stopping on it.
31281    #[test]
31282    fn a_dropped_fragment_stops_bounding_the_one_that_was_kept() {
31283        let mut f = Fixture::new();
31284        marking(&mut f);
31285        let got = f.run(&[
31286            b"FT.SEARCH",
31287            b"mk",
31288            b"fox",
31289            b"SUMMARIZE",
31290            b"FRAGS",
31291            b"1",
31292            b"LEN",
31293            b"20",
31294        ]);
31295        assert!(
31296            got.contains("c2 c3 fox d1 d2 d3 d4 d5 d6 d7 d8 d9 fox e1 e2... "),
31297            "{got}"
31298        );
31299        // Keep both and the first stops on the second rather than running over
31300        // it, on the same query and the same budget.
31301        let two = f.run(&[
31302            b"FT.SEARCH",
31303            b"mk",
31304            b"fox",
31305            b"SUMMARIZE",
31306            b"FRAGS",
31307            b"2",
31308            b"LEN",
31309            b"20",
31310        ]);
31311        assert!(
31312            two.contains("c2 c3 fox d1 d2 d3 d4 d5 d6 d7 d8 d9... d1"),
31313            "{two}"
31314        );
31315    }
31316
31317    /// A `HIGHLIGHT` wraps every match, and on a field with no match in it the
31318    /// clause also calls off the cutting down a `SUMMARIZE` would have done.
31319    #[test]
31320    fn a_highlight_marks_the_matches_and_leaves_the_rest_of_the_field_alone() {
31321        let mut f = Fixture::new();
31322        marking(&mut f);
31323        let got = f.run(&[b"FT.SEARCH", b"mk", b"fox", b"HIGHLIGHT"]);
31324        assert!(got.contains("<b>fox</b> d1 d2"), "{got}");
31325        let both = f.run(&[
31326            b"FT.SEARCH",
31327            b"mk",
31328            b"fox",
31329            b"SUMMARIZE",
31330            b"LEN",
31331            b"2",
31332            b"HIGHLIGHT",
31333        ]);
31334        assert!(both.contains("c3 <b>fox</b> d1 d2... "), "{both}");
31335        // `b` still holds no match, and this time it comes back whole.
31336        assert!(both.contains("t1 t2 t3 t4 t5 t6 t7 t8\r\n"), "{both}");
31337        assert!(both.contains("$3\r\nred\r\n"), "{both}");
31338        // Naming a field one clause does not cover leaves it cut down again.
31339        let split = f.run(&[
31340            b"FT.SEARCH",
31341            b"mk",
31342            b"fox",
31343            b"SUMMARIZE",
31344            b"FIELDS",
31345            b"1",
31346            b"b",
31347            b"LEN",
31348            b"2",
31349            b"HIGHLIGHT",
31350            b"FIELDS",
31351            b"1",
31352            b"a",
31353        ]);
31354        assert!(split.contains("t1 t2 t3 t4 t5 t6 t7\r\n"), "{split}");
31355    }
31356
31357    /// A tag is never marked, in its own field or in a text field beside it.
31358    #[test]
31359    fn a_highlight_does_not_mark_a_tag() {
31360        let mut f = Fixture::new();
31361        marking(&mut f);
31362        f.run(&[b"HSET", b"m:1", b"b", b"red and blue"]);
31363        let got = f.run(&[b"FT.SEARCH", b"mk", b"@g:{red}", b"HIGHLIGHT"]);
31364        assert!(!got.contains("<b>"), "{got}");
31365        assert!(got.contains("red and blue"), "{got}");
31366    }
31367
31368    /// A search answers a total and then a row for every key in the window,
31369    /// with the fields of that key after it.
31370    #[test]
31371    fn a_search_answers_a_total_and_then_the_rows() {
31372        let mut f = Fixture::new();
31373        corpus(&mut f);
31374        assert_eq!(
31375            f.run(&[b"FT.SEARCH", b"sx", b"delta"]),
31376            "*3\r\n:1\r\n$3\r\nd:3\r\n*6\r\n$1\r\nt\r\n$5\r\ndelta\r\n$1\r\ng\r\n$2\r\ncc\r\n$1\r\nn\r\n$1\r\n3\r\n"
31377        );
31378        // The fields are what the key holds and not what the schema names, so
31379        // a field nobody indexed comes back too.
31380        f.run(&[b"HSET", b"d:3", b"extra", b"more"]);
31381        assert!(f.run(&[b"FT.SEARCH", b"sx", b"delta"]).contains("extra"));
31382        // `NOCONTENT` leaves the keys on their own, and `LIMIT 0 0` leaves
31383        // the total on its own.
31384        assert_eq!(
31385            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
31386            "*2\r\n:1\r\n$3\r\nd:3\r\n"
31387        );
31388        assert_eq!(
31389            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
31390            "*1\r\n:3\r\n"
31391        );
31392    }
31393
31394    /// The window is ten rows when nobody said, and the cap is on how wide it
31395    /// is rather than on where it starts.
31396    #[test]
31397    fn the_window_is_ten_rows_and_a_million_wide_at_most() {
31398        let mut f = Fixture::new();
31399        corpus(&mut f);
31400        assert_eq!(
31401            f.run(&[
31402                b"FT.SEARCH",
31403                b"sx",
31404                b"alpha",
31405                b"NOCONTENT",
31406                b"LIMIT",
31407                b"1",
31408                b"1"
31409            ]),
31410            "*2\r\n:3\r\n$3\r\nd:2\r\n"
31411        );
31412        assert_eq!(
31413            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0"]),
31414            "-SEARCH_PARSE_ARGS LIMIT requires two arguments\r\n"
31415        );
31416        assert_eq!(
31417            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"-1"]),
31418            "-SEARCH_PARSE_ARGS LIMIT needs two numeric arguments\r\n"
31419        );
31420        assert_eq!(
31421            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"1000001"]),
31422            "-SEARCH_LIMIT_OVER LIMIT exceeds maximum of 1000000\r\n"
31423        );
31424        assert_eq!(
31425            f.run(&[
31426                b"FT.SEARCH",
31427                b"sx",
31428                b"alpha",
31429                b"NOCONTENT",
31430                b"LIMIT",
31431                b"999999",
31432                b"1000000"
31433            ]),
31434            "*1\r\n:3\r\n"
31435        );
31436    }
31437
31438    /// `RETURN 0` reads on the wire like `NOCONTENT` and is not the same
31439    /// thing, because a later `RETURN` puts the fields back and a later
31440    /// `RETURN` after a `NOCONTENT` does not.
31441    #[test]
31442    fn a_return_of_nothing_is_not_the_same_as_nocontent() {
31443        let mut f = Fixture::new();
31444        corpus(&mut f);
31445        let bare = "*2\r\n:1\r\n$3\r\nd:3\r\n";
31446        assert_eq!(
31447            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"0"]),
31448            bare
31449        );
31450        assert_eq!(
31451            f.run(&[
31452                b"FT.SEARCH",
31453                b"sx",
31454                b"delta",
31455                b"NOCONTENT",
31456                b"RETURN",
31457                b"1",
31458                b"t"
31459            ]),
31460            bare
31461        );
31462        assert_eq!(
31463            f.run(&[
31464                b"FT.SEARCH",
31465                b"sx",
31466                b"delta",
31467                b"RETURN",
31468                b"0",
31469                b"RETURN",
31470                b"1",
31471                b"t"
31472            ]),
31473            "*3\r\n:1\r\n$3\r\nd:3\r\n*2\r\n$1\r\nt\r\n$5\r\ndelta\r\n"
31474        );
31475    }
31476
31477    /// The count after `RETURN` counts words and not fields, so the `AS` and
31478    /// the name after it are two of them.
31479    #[test]
31480    fn the_count_after_return_counts_words() {
31481        let mut f = Fixture::new();
31482        corpus(&mut f);
31483        // Two words is one renamed field, and the name is the one it comes
31484        // back under.
31485        assert_eq!(
31486            f.run(&[
31487                b"FT.SEARCH",
31488                b"sx",
31489                b"delta",
31490                b"RETURN",
31491                b"3",
31492                b"t",
31493                b"AS",
31494                b"x"
31495            ]),
31496            "*3\r\n:1\r\n$3\r\nd:3\r\n*2\r\n$1\r\nx\r\n$5\r\ndelta\r\n"
31497        );
31498        // A count that stops on the `AS` has nothing to rename to, and one
31499        // that reaches past the last word is short an argument.
31500        assert_eq!(
31501            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"2", b"t", b"AS"]),
31502            "-SEARCH_PARSE_ARGS RETURN path AS name - must be accompanied with NAME\r\n"
31503        );
31504        assert_eq!(
31505            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"3", b"t", b"AS"]),
31506            "-SEARCH_PARSE_ARGS Bad arguments for RETURN: Expected an argument, but none provided\r\n"
31507        );
31508        // A count that stops before the `AS` asks for a field called `AS`,
31509        // which no key holds, and a field the key does not hold is left out
31510        // rather than sent empty.
31511        assert_eq!(
31512            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"AS"]),
31513            "*3\r\n:1\r\n$3\r\nd:3\r\n*0\r\n"
31514        );
31515    }
31516
31517    /// A `FILTER` is a numeric range written outside the query, and it is only
31518    /// the wrong way round on a field the schema holds as a number.
31519    #[test]
31520    fn a_filter_is_a_range_written_outside_the_query() {
31521        let mut f = Fixture::new();
31522        corpus(&mut f);
31523        assert_eq!(
31524            f.run(&[
31525                b"FT.SEARCH",
31526                b"sx",
31527                b"alpha",
31528                b"NOCONTENT",
31529                b"FILTER",
31530                b"n",
31531                b"2",
31532                b"4"
31533            ]),
31534            "*3\r\n:2\r\n$3\r\nd:2\r\n$3\r\nd:4\r\n"
31535        );
31536        assert_eq!(
31537            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2"]),
31538            "-SEARCH_PARSE_ARGS FILTER requires 3 arguments\r\n"
31539        );
31540        assert_eq!(
31541            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"x", b"1"]),
31542            "-SEARCH_PARSE_ARGS Bad lower range: x\r\n"
31543        );
31544        assert_eq!(
31545            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2", b"1"]),
31546            "-SEARCH_SYNTAX Invalid numeric range (min > max): @n:[2.000000 1.000000]\r\n"
31547        );
31548        // The same range on a field that is not a number at all, and on a
31549        // field that is not there, answers nothing rather than refusing.
31550        for field in [b"g".as_slice(), b"nope"] {
31551            assert_eq!(
31552                f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", field, b"2", b"1"]),
31553                "*1\r\n:0\r\n"
31554            );
31555        }
31556    }
31557
31558    /// The index is resolved before the arguments after it are read, so a name
31559    /// that is not there answers about the name whatever else is wrong.
31560    #[test]
31561    fn the_index_is_found_before_the_arguments_are_read() {
31562        let mut f = Fixture::new();
31563        corpus(&mut f);
31564        let missing = "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n";
31565        assert_eq!(f.run(&[b"FT.SEARCH", b"nope", b"alpha", b"BOGUS"]), missing);
31566        assert_eq!(
31567            f.run(&[b"FT.EXPLAIN", b"nope", b"alpha", b"BOGUS"]),
31568            missing
31569        );
31570        // And the arguments are read before the query is, so a query that
31571        // will not parse still answers about the argument.
31572        assert_eq!(
31573            f.run(&[b"FT.SEARCH", b"sx", b"@@@", b"BOGUS"]),
31574            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `BOGUS` at position 1 for <main>\r\n"
31575        );
31576    }
31577
31578    /// `INKEYS` filters the answer before the total is taken, which is not
31579    /// where a client would guess it happens.
31580    #[test]
31581    fn inkeys_comes_off_the_total() {
31582        let mut f = Fixture::new();
31583        corpus(&mut f);
31584        assert_eq!(
31585            f.run(&[
31586                b"FT.SEARCH",
31587                b"sx",
31588                b"alpha",
31589                b"NOCONTENT",
31590                b"INKEYS",
31591                b"1",
31592                b"d:1"
31593            ]),
31594            "*2\r\n:1\r\n$3\r\nd:1\r\n"
31595        );
31596        assert_eq!(
31597            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"NOCONTENT", b"INKEYS", b"0"]),
31598            "*1\r\n:0\r\n"
31599        );
31600    }
31601
31602    /// The fields come from the database the session is on, and a row whose
31603    /// key will not load there is dropped from the reply and taken off the
31604    /// total.
31605    ///
31606    /// Measured against a real server, which follows a key on every database
31607    /// and then loads it from one.
31608    #[test]
31609    fn the_fields_are_read_from_the_session_database() {
31610        let mut f = Fixture::new();
31611        corpus(&mut f);
31612        f.run(&[b"SELECT", b"1"]);
31613        f.run(&[b"HSET", b"d:9", b"t", b"delta", b"n", b"9"]);
31614        // Both documents are in the index, and only one of them is in this
31615        // database.
31616        assert_eq!(
31617            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
31618            "*3\r\n:2\r\n$3\r\nd:3\r\n$3\r\nd:9\r\n"
31619        );
31620        assert_eq!(
31621            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
31622            "*3\r\n:1\r\n$3\r\nd:9\r\n*2\r\n$1\r\nn\r\n$1\r\n9\r\n"
31623        );
31624    }
31625
31626    /// The deeper protocol answers a map of five rather than an array, with
31627    /// every row a map of its own.
31628    #[test]
31629    fn the_third_protocol_answers_a_map_of_five() {
31630        let mut f = Fixture::new();
31631        corpus(&mut f);
31632        f.out = Out::new(Proto::Resp3);
31633        assert_eq!(
31634            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
31635            concat!(
31636                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
31637                "%3\r\n+id\r\n$3\r\nd:3\r\n+extra_attributes\r\n%1\r\n$1\r\nn\r\n$1\r\n3\r\n",
31638                "+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
31639            )
31640        );
31641    }
31642
31643    /// A window of nothing is a client asking for the count on its own, and a
31644    /// window of nothing that starts somewhere else is a contradiction all
31645    /// three commands refuse in the same words.
31646    #[test]
31647    fn a_window_of_nothing_has_to_start_at_the_top() {
31648        let mut f = Fixture::new();
31649        corpus(&mut f);
31650        let refused = "-SEARCH_LIMIT_OVER The `offset` of the LIMIT must be 0 when `num` is 0\r\n";
31651        assert_eq!(
31652            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
31653            refused
31654        );
31655        assert_eq!(
31656            f.run(&[b"FT.EXPLAIN", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
31657            refused
31658        );
31659        assert_eq!(
31660            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
31661            refused
31662        );
31663        assert_eq!(
31664            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
31665            "*1\r\n:3\r\n"
31666        );
31667    }
31668
31669    /// An aggregation answers a count and then a list of properties for every
31670    /// row, which is empty until something asks for a field.
31671    #[test]
31672    fn an_aggregation_answers_a_count_and_then_the_properties() {
31673        let mut f = Fixture::new();
31674        corpus(&mut f);
31675        assert_eq!(
31676            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha"]),
31677            "*4\r\n:1\r\n*0\r\n*0\r\n*0\r\n"
31678        );
31679        // Every row, and not the ten a search would have cut it down to. The
31680        // count in front of them is one because that is how far the reply had
31681        // got when it was written, which is measured against a real server.
31682        assert_eq!(
31683            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"1", b"@t"]),
31684            concat!(
31685                "*4\r\n:1\r\n*2\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
31686                "*2\r\n$1\r\nt\r\n$11\r\nalpha gamma\r\n",
31687                "*2\r\n$1\r\nt\r\n$16\r\nalpha beta gamma\r\n"
31688            )
31689        );
31690        // Ascending document number, because nothing sorts the answer. The
31691        // second and fourth documents are the ones the window lands on and the
31692        // best scoring one is not among them.
31693        assert_eq!(
31694            f.run(&[
31695                b"FT.AGGREGATE",
31696                b"sx",
31697                b"alpha",
31698                b"LOAD",
31699                b"1",
31700                b"@n",
31701                b"LIMIT",
31702                b"1",
31703                b"2"
31704            ]),
31705            "*3\r\n:2\r\n*2\r\n$1\r\nn\r\n$1\r\n2\r\n*2\r\n$1\r\nn\r\n$1\r\n4\r\n"
31706        );
31707        // A query nothing answers is a count of nothing and no rows at all.
31708        assert_eq!(
31709            f.run(&[b"FT.AGGREGATE", b"sx", b"nope", b"LOAD", b"1", b"@t"]),
31710            "*1\r\n:0\r\n"
31711        );
31712    }
31713
31714    /// `LOAD` counts words rather than fields, names the property after the
31715    /// path unless an `AS` renames it, and reads everything the key holds when
31716    /// it is given a star.
31717    #[test]
31718    fn a_load_counts_words_and_can_rename_what_it_reads() {
31719        let mut f = Fixture::new();
31720        corpus(&mut f);
31721        // Three words, which are the path, the `AS` and the name.
31722        assert_eq!(
31723            f.run(&[
31724                b"FT.AGGREGATE",
31725                b"sx",
31726                b"alpha",
31727                b"LOAD",
31728                b"3",
31729                b"@t",
31730                b"AS",
31731                b"text"
31732            ]),
31733            concat!(
31734                "*4\r\n:1\r\n*2\r\n$4\r\ntext\r\n$10\r\nalpha beta\r\n",
31735                "*2\r\n$4\r\ntext\r\n$11\r\nalpha gamma\r\n",
31736                "*2\r\n$4\r\ntext\r\n$16\r\nalpha beta gamma\r\n"
31737            )
31738        );
31739        assert_eq!(
31740            f.run(&[
31741                b"FT.AGGREGATE",
31742                b"sx",
31743                b"alpha",
31744                b"LOAD",
31745                b"*",
31746                b"LIMIT",
31747                b"0",
31748                b"1"
31749            ]),
31750            concat!(
31751                "*2\r\n:1\r\n*6\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
31752                "$1\r\ng\r\n$5\r\naa,bb\r\n$1\r\nn\r\n$1\r\n1\r\n"
31753            )
31754        );
31755        // A field the key does not hold is left out rather than sent empty.
31756        assert_eq!(
31757            f.run(&[
31758                b"FT.AGGREGATE",
31759                b"sx",
31760                b"alpha",
31761                b"LOAD",
31762                b"2",
31763                b"@n",
31764                b"@nope",
31765                b"LIMIT",
31766                b"0",
31767                b"2"
31768            ]),
31769            "*3\r\n:1\r\n*2\r\n$1\r\nn\r\n$1\r\n1\r\n*2\r\n$1\r\nn\r\n$1\r\n2\r\n"
31770        );
31771    }
31772
31773    /// The `LOAD` grammar, which has four ways to go wrong and one of them is
31774    /// only reported once the rest of the argument list has read cleanly.
31775    #[test]
31776    fn a_load_refuses_a_count_it_cannot_use() {
31777        let mut f = Fixture::new();
31778        corpus(&mut f);
31779        let head = "-SEARCH_PARSE_ARGS Bad arguments for LOAD: ";
31780        assert_eq!(
31781            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"x"]),
31782            format!("{head}Expected number of fields or `*`\r\n")
31783        );
31784        assert_eq!(
31785            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"-1", b"@t"]),
31786            format!("{head}Value is outside acceptable bounds\r\n")
31787        );
31788        assert_eq!(
31789            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"5", b"@t"]),
31790            format!("{head}Expected an argument, but none provided\r\n")
31791        );
31792        assert_eq!(
31793            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD"]),
31794            format!("{head}Expected an argument, but none provided\r\n")
31795        );
31796        // A count that runs out on the `AS` is held back, because the word
31797        // after it is read as an argument of its own and may be worth an error
31798        // of its own. Nothing follows here, so the held back line is the one.
31799        assert_eq!(
31800            f.run(&[
31801                b"FT.AGGREGATE",
31802                b"sx",
31803                b"alpha",
31804                b"LOAD",
31805                b"2",
31806                b"@t",
31807                b"AS"
31808            ]),
31809            "-SEARCH_PARSE_ARGS LOAD path AS name - must be accompanied with NAME\r\n"
31810        );
31811        // And here the word after it is one an aggregation stops taking once a
31812        // step has been read, so that is what the client hears about.
31813        assert_eq!(
31814            f.run(&[
31815                b"FT.AGGREGATE",
31816                b"sx",
31817                b"alpha",
31818                b"LOAD",
31819                b"2",
31820                b"@t",
31821                b"AS",
31822                b"VERBATIM"
31823            ]),
31824            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 5 for <main>\r\n"
31825        );
31826        // A `LOAD 0` is a step that names nothing. It shuts the same door
31827        // without becoming a loader, so the count stays the one a query with no
31828        // `LOAD` gets.
31829        assert_eq!(
31830            f.run(&[
31831                b"FT.AGGREGATE",
31832                b"sx",
31833                b"alpha",
31834                b"LOAD",
31835                b"0",
31836                b"LIMIT",
31837                b"0",
31838                b"1"
31839            ]),
31840            "*2\r\n:1\r\n*0\r\n"
31841        );
31842    }
31843
31844    /// Reading a step of the pipeline stops the words about the search itself
31845    /// being taken, and `LIMIT` and `TIMEOUT` are not steps.
31846    #[test]
31847    fn a_pipeline_step_closes_the_door_on_the_search_words() {
31848        let mut f = Fixture::new();
31849        corpus(&mut f);
31850        assert_eq!(
31851            f.run(&[
31852                b"FT.AGGREGATE",
31853                b"sx",
31854                b"alpha",
31855                b"LOAD",
31856                b"1",
31857                b"@t",
31858                b"VERBATIM"
31859            ]),
31860            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 4 for <main>\r\n"
31861        );
31862        assert_eq!(
31863            f.run(&[
31864                b"FT.AGGREGATE",
31865                b"sx",
31866                b"alpha",
31867                b"LIMIT",
31868                b"0",
31869                b"1",
31870                b"VERBATIM"
31871            ]),
31872            "*2\r\n:1\r\n*0\r\n"
31873        );
31874        // Three words a search takes that this command names in its refusal
31875        // rather than calling them unknown.
31876        for word in [b"RETURN".as_slice(), b"SUMMARIZE", b"HIGHLIGHT"] {
31877            let name = core::str::from_utf8(word).expect("the three words are text");
31878            assert_eq!(
31879                f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", word]),
31880                format!("-SEARCH_PARSE_ARGS {name} is not supported on FT.AGGREGATE\r\n")
31881            );
31882        }
31883    }
31884
31885    /// `ADDSCORES` writes the score as a property to twelve significant digits
31886    /// where `WITHSCORES` writes it beside the row in full.
31887    #[test]
31888    fn addscores_writes_a_shorter_score_than_withscores() {
31889        let mut f = Fixture::new();
31890        corpus(&mut f);
31891        assert_eq!(
31892            f.run(&[
31893                b"FT.AGGREGATE",
31894                b"sx",
31895                b"alpha",
31896                b"ADDSCORES",
31897                b"LOAD",
31898                b"1",
31899                b"@n",
31900                b"LIMIT",
31901                b"0",
31902                b"2"
31903            ]),
31904            concat!(
31905                "*3\r\n:1\r\n",
31906                "*4\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n$1\r\nn\r\n$1\r\n1\r\n",
31907                "*4\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n$1\r\nn\r\n$1\r\n2\r\n"
31908            )
31909        );
31910        // `NOCONTENT` takes the properties away and leaves whatever was asked
31911        // for beside them, and a sort key is always null because nothing sorts
31912        // by one yet.
31913        assert_eq!(
31914            f.run(&[
31915                b"FT.AGGREGATE",
31916                b"sx",
31917                b"alpha",
31918                b"NOCONTENT",
31919                b"WITHSCORES",
31920                b"LIMIT",
31921                b"0",
31922                b"2"
31923            ]),
31924            "*3\r\n:1\r\n$18\r\n0.3566749439387324\r\n$18\r\n0.3566749439387324\r\n"
31925        );
31926        assert_eq!(
31927            f.run(&[
31928                b"FT.AGGREGATE",
31929                b"sx",
31930                b"alpha",
31931                b"WITHSORTKEYS",
31932                b"LOAD",
31933                b"1",
31934                b"@n",
31935                b"LIMIT",
31936                b"0",
31937                b"1"
31938            ]),
31939            "*3\r\n:1\r\n$-1\r\n*2\r\n$1\r\nn\r\n$1\r\n1\r\n"
31940        );
31941    }
31942
31943    /// The one scorer that has to see the whole answer first turns the count
31944    /// into the real total and hands the rows back backwards.
31945    #[test]
31946    fn a_normalising_scorer_answers_the_rows_backwards() {
31947        let mut f = Fixture::new();
31948        corpus(&mut f);
31949        assert_eq!(
31950            f.run(&[
31951                b"FT.AGGREGATE",
31952                b"sx",
31953                b"alpha",
31954                b"SCORER",
31955                b"BM25STD.NORM",
31956                b"ADDSCORES",
31957                b"LOAD",
31958                b"1",
31959                b"@n",
31960                b"LIMIT",
31961                b"1",
31962                b"2"
31963            ]),
31964            concat!(
31965                "*3\r\n:3\r\n",
31966                "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n2\r\n",
31967                "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n1\r\n"
31968            )
31969        );
31970        // Without `ADDSCORES` nothing on the row needs the score, so the rows
31971        // come back the way every other query answers them.
31972        assert_eq!(
31973            f.run(&[
31974                b"FT.AGGREGATE",
31975                b"sx",
31976                b"alpha",
31977                b"SCORER",
31978                b"BM25STD.NORM",
31979                b"LOAD",
31980                b"1",
31981                b"@n",
31982                b"LIMIT",
31983                b"1",
31984                b"2"
31985            ]),
31986            "*3\r\n:2\r\n*2\r\n$1\r\nn\r\n$1\r\n2\r\n*2\r\n$1\r\nn\r\n$1\r\n4\r\n"
31987        );
31988    }
31989
31990    /// The deeper protocol answers the same map of five a search answers, with
31991    /// the `id` gone because an aggregation is about the properties.
31992    #[test]
31993    fn an_aggregation_answers_a_map_of_five_as_well() {
31994        let mut f = Fixture::new();
31995        corpus(&mut f);
31996        f.out = Out::new(Proto::Resp3);
31997        assert_eq!(
31998            f.run(&[
31999                b"FT.AGGREGATE",
32000                b"sx",
32001                b"alpha",
32002                b"ADDSCORES",
32003                b"WITHSCORES",
32004                b"WITHSORTKEYS",
32005                b"LOAD",
32006                b"1",
32007                b"@n",
32008                b"LIMIT",
32009                b"0",
32010                b"1"
32011            ]),
32012            concat!(
32013                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
32014                "%4\r\n+score\r\n,0.3566749439387324\r\n+sortkey\r\n_\r\n",
32015                "+extra_attributes\r\n%2\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n",
32016                "$1\r\nn\r\n$1\r\n1\r\n+values\r\n*0\r\n",
32017                "+total_results\r\n:1\r\n+warning\r\n*0\r\n"
32018            )
32019        );
32020        // The count is worked out from the rows the reply reached under this
32021        // protocol, where under RESP2 it is worked out from the first of them.
32022        assert_eq!(
32023            f.run(&[
32024                b"FT.AGGREGATE",
32025                b"sx",
32026                b"alpha",
32027                b"NOCONTENT",
32028                b"LIMIT",
32029                b"0",
32030                b"1"
32031            ]),
32032            concat!(
32033                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
32034                "%1\r\n+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
32035            )
32036        );
32037    }
32038    // ------------------------------------------------------------- CLIENT
32039
32040    /// The field names `CLIENT INFO` reports, in the order 8.10.1 reports them.
32041    ///
32042    /// Written out rather than derived, because the whole point of the command
32043    /// is that a parser somewhere else knows this list, so a change to it is a
32044    /// change a test should have to be edited for.
32045    const INFO_FIELDS: &[&str] = &[
32046        "id",
32047        "addr",
32048        "laddr",
32049        "fd",
32050        "name",
32051        "age",
32052        "idle",
32053        "flags",
32054        "db",
32055        "sub",
32056        "psub",
32057        "ssub",
32058        "multi",
32059        "watch",
32060        "qbuf",
32061        "qbuf-free",
32062        "argv-mem",
32063        "multi-mem",
32064        "rbs",
32065        "rbp",
32066        "obl",
32067        "oll",
32068        "omem",
32069        "omem-shared",
32070        "omem-unshared",
32071        "tot-mem",
32072        "events",
32073        "cmd",
32074        "user",
32075        "redir",
32076        "resp",
32077        "lib-name",
32078        "lib-ver",
32079        "io-thread",
32080        "tot-net-in",
32081        "tot-net-out",
32082        "tot-cmds",
32083        "read-events",
32084        "avg-pipeline-len-sum",
32085        "avg-pipeline-len-cnt",
32086    ];
32087
32088    /// The report as a list of name and value pairs, taken out of the bulk
32089    /// string the reply is on RESP2.
32090    fn client_info(f: &mut Fixture) -> Vec<(String, String)> {
32091        let reply = f.run(&[b"CLIENT", b"INFO"]);
32092        let body = reply.split_once("\r\n").expect("a bulk header").1;
32093        // A verbatim string on RESP3 carries its format in front of the text,
32094        // and the same reply is a plain bulk string on RESP2.
32095        let line = body.trim_end_matches("\r\n").trim_start_matches("txt:");
32096        assert!(
32097            line.ends_with('\n'),
32098            "the report ends in a newline: {line:?}"
32099        );
32100        line.trim_end()
32101            .split(' ')
32102            .map(|pair| {
32103                let (name, value) = pair.split_once('=').expect("name=value");
32104                (name.to_string(), value.to_string())
32105            })
32106            .collect()
32107    }
32108
32109    /// One field of the report.
32110    fn client_field(f: &mut Fixture, name: &str) -> String {
32111        client_info(f)
32112            .into_iter()
32113            .find(|(n, _)| n == name)
32114            .map(|(_, v)| v)
32115            .unwrap_or_else(|| panic!("no {name} field"))
32116    }
32117
32118    #[test]
32119    fn client_info_names_every_field_a_real_server_names() {
32120        let mut f = Fixture::new();
32121        let got: Vec<String> = client_info(&mut f).into_iter().map(|(n, _)| n).collect();
32122        assert_eq!(got, INFO_FIELDS);
32123    }
32124
32125    /// A session nobody told about a socket is what an embedded caller gets, and
32126    /// it has to answer rather than pretend to have an address.
32127    #[test]
32128    fn a_connection_with_no_socket_reports_no_address_and_no_descriptor() {
32129        let mut f = Fixture::new();
32130        assert_eq!(client_field(&mut f, "addr"), "");
32131        assert_eq!(client_field(&mut f, "laddr"), "");
32132        assert_eq!(client_field(&mut f, "fd"), "-1");
32133        assert_eq!(client_field(&mut f, "id"), "7");
32134    }
32135
32136    #[test]
32137    fn client_setname_takes_a_name_back_and_refuses_one_with_a_space_in_it() {
32138        let mut f = Fixture::new();
32139        assert_eq!(f.run(&[b"CLIENT", b"GETNAME"]), "$-1\r\n");
32140        assert_eq!(f.run(&[b"CLIENT", b"SETNAME", b"worker"]), "+OK\r\n");
32141        assert_eq!(f.run(&[b"CLIENT", b"GETNAME"]), "$6\r\nworker\r\n");
32142        assert_eq!(client_field(&mut f, "name"), "worker");
32143        assert_eq!(
32144            f.run(&[b"CLIENT", b"SETNAME", b"two words"]),
32145            "-ERR Client names cannot contain spaces, newlines or special characters.\r\n"
32146        );
32147        // And the name it had is still the name it has.
32148        assert_eq!(f.run(&[b"CLIENT", b"GETNAME"]), "$6\r\nworker\r\n");
32149    }
32150
32151    /// `RESET` is `clearClientConnectionState`, and the surprising half of it is
32152    /// what it keeps: the library behind the socket is the same library it was.
32153    #[test]
32154    fn reset_clears_the_name_and_the_switches_and_keeps_the_library() {
32155        let mut f = Fixture::new();
32156        f.run(&[b"CLIENT", b"SETNAME", b"worker"]);
32157        f.run(&[b"CLIENT", b"SETINFO", b"LIB-NAME", b"yo-py"]);
32158        f.run(&[b"CLIENT", b"SETINFO", b"LIB-VER", b"1.2.3"]);
32159        f.run(&[b"CLIENT", b"NO-EVICT", b"on"]);
32160        f.run(&[b"CLIENT", b"NO-TOUCH", b"on"]);
32161        assert_eq!(client_field(&mut f, "flags"), "eT");
32162
32163        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
32164        assert_eq!(client_field(&mut f, "name"), "");
32165        assert_eq!(client_field(&mut f, "flags"), "N");
32166        assert_eq!(client_field(&mut f, "lib-name"), "yo-py");
32167        assert_eq!(client_field(&mut f, "lib-ver"), "1.2.3");
32168    }
32169
32170    #[test]
32171    fn client_setinfo_complains_the_way_a_real_server_does() {
32172        let mut f = Fixture::new();
32173        assert_eq!(
32174            f.run(&[b"CLIENT", b"SETINFO", b"LIB-NAME"]),
32175            "-ERR wrong number of arguments for 'client|setinfo' command\r\n"
32176        );
32177        assert_eq!(
32178            f.run(&[b"CLIENT", b"SETINFO", b"NOPE", b"x"]),
32179            "-ERR Unrecognized option 'NOPE'\r\n"
32180        );
32181        assert_eq!(
32182            f.run(&[b"CLIENT", b"SETINFO", b"lib-name", b"ok x"]),
32183            "-ERR lib-name cannot contain spaces, newlines or special characters.\r\n"
32184        );
32185        assert_eq!(
32186            f.run(&[b"CLIENT", b"SETINFO", b"LIB-VER", b"has space"]),
32187            "-ERR lib-ver cannot contain spaces, newlines or special characters.\r\n"
32188        );
32189    }
32190
32191    #[test]
32192    fn client_refuses_a_subcommand_it_does_not_have_and_arguments_it_did_not_ask_for() {
32193        let mut f = Fixture::new();
32194        assert_eq!(
32195            f.run(&[b"CLIENT", b"NOPE"]),
32196            "-ERR unknown subcommand 'NOPE'. Try CLIENT HELP.\r\n"
32197        );
32198        assert_eq!(
32199            f.run(&[b"CLIENT", b"GETNAME", b"extra"]),
32200            "-ERR wrong number of arguments for 'client|getname' command\r\n"
32201        );
32202        assert_eq!(
32203            f.run(&[b"CLIENT", b"NO-EVICT", b"maybe"]),
32204            "-ERR syntax error\r\n"
32205        );
32206        assert_eq!(
32207            f.run(&[b"CLIENT", b"REPLY", b"BAD"]),
32208            "-ERR syntax error\r\n"
32209        );
32210    }
32211
32212    /// The three subscribe namespaces are counted apart, which is not the same
32213    /// count a subscribe reply carries: that one puts channels and patterns
32214    /// together.
32215    #[test]
32216    fn client_info_counts_the_three_subscribe_namespaces_apart() {
32217        let mut f = Fixture::new();
32218        // On RESP3, because a subscribed RESP2 connection may only send nine
32219        // commands and `CLIENT` is not one of them.
32220        f.run(&[b"HELLO", b"3"]);
32221        f.run(&[b"SUBSCRIBE", b"a", b"b"]);
32222        f.run(&[b"PSUBSCRIBE", b"p*"]);
32223        f.run(&[b"SSUBSCRIBE", b"s"]);
32224        let info = client_info(&mut f);
32225        let at = |name: &str| {
32226            info.iter()
32227                .find(|(n, _)| n == name)
32228                .map(|(_, v)| v.clone())
32229                .unwrap()
32230        };
32231        assert_eq!(at("sub"), "2");
32232        assert_eq!(at("psub"), "1");
32233        assert_eq!(at("ssub"), "1");
32234        assert_eq!(at("flags"), "P");
32235        forget_session(&f.server, &mut f.session);
32236    }
32237
32238    /// The `cmd` field names the subcommand, which for this command is always
32239    /// `client|info` and is the one field that reports the command asking.
32240    #[test]
32241    fn client_info_reports_itself_as_the_command_running() {
32242        let mut f = Fixture::new();
32243        assert_eq!(client_field(&mut f, "cmd"), "client|info");
32244        f.run(&[b"GET", b"nothing"]);
32245        // Still `client|info`, because the field is about the command asking
32246        // and the command asking is this one.
32247        assert_eq!(client_field(&mut f, "cmd"), "client|info");
32248    }
32249
32250    /// A container called in mixed case is still the same command underneath.
32251    #[test]
32252    fn the_command_field_is_lower_case_however_the_client_spelled_it() {
32253        let mut f = Fixture::new();
32254        let reply = f.run(&[b"CLIENT", b"Info"]);
32255        assert!(reply.contains("cmd=client|info"), "{reply}");
32256    }
32257
32258    #[test]
32259    fn client_help_lists_the_subcommands_that_are_here() {
32260        let mut f = Fixture::new();
32261        let reply = f.run(&[b"CLIENT", b"HELP"]);
32262        for sub in ["ID", "GETNAME", "SETNAME", "SETINFO", "INFO", "REPLY"] {
32263            assert!(reply.contains(sub), "no {sub} in {reply}");
32264        }
32265        // And not the ones that are not, since a client reads this to find out
32266        // what it can send.
32267        assert!(!reply.contains("TRACKING"), "{reply}");
32268    }
32269
32270    // ------------------------------------------------- what crosses to a replica
32271
32272    /// The stream, split back into the commands it is made of.
32273    ///
32274    /// A replica reads this with the same parser it reads a client with, so a
32275    /// test can read it the same way, and a list of words is what the rewrite
32276    /// table in the spec is written in.
32277    fn commands(stream: &str) -> Vec<Vec<String>> {
32278        let mut out = Vec::new();
32279        let mut rest = stream;
32280        while let Some(tail) = rest.strip_prefix('*') {
32281            let (n, tail) = tail.split_once("\r\n").expect("a header ends");
32282            let mut one = Vec::new();
32283            let mut tail = tail;
32284            for _ in 0..n.parse::<usize>().expect("a count") {
32285                let body = tail.strip_prefix('$').expect("a bulk string");
32286                let (len, body) = body.split_once("\r\n").expect("a length ends");
32287                let len: usize = len.parse().expect("a length");
32288                one.push(body[..len].to_string());
32289                tail = &body[len + 2..];
32290            }
32291            out.push(one);
32292            rest = tail;
32293        }
32294        assert!(rest.is_empty(), "left over: {rest:?}");
32295        out
32296    }
32297
32298    /// The words of the one command a test expects to have crossed.
32299    fn only(stream: &str) -> Vec<String> {
32300        let mut each = commands(stream);
32301        assert_eq!(each.len(), 1, "expected one command: {stream:?}");
32302        each.pop().expect("one command")
32303    }
32304
32305    #[test]
32306    fn the_stream_opens_with_a_select_and_says_it_once() {
32307        let mut f = Fixture::replicated();
32308        assert_eq!(
32309            commands(&f.crossed(&[b"SET", b"k", b"v"])),
32310            vec![
32311                vec!["SELECT".to_string(), "0".to_string()],
32312                vec!["SET".to_string(), "k".to_string(), "v".to_string()],
32313            ]
32314        );
32315        // The second write is on the same database, so it goes on its own.
32316        assert_eq!(only(&f.crossed(&[b"SET", b"k2", b"v"])), ["SET", "k2", "v"]);
32317        // A different one says so first, and the SELECT is not the client's,
32318        // which crossed nothing on its own.
32319        f.run(&[b"SELECT", b"3"]);
32320        assert_eq!(
32321            commands(&f.crossed(&[b"SET", b"k3", b"v"])),
32322            vec![
32323                vec!["SELECT".to_string(), "3".to_string()],
32324                vec!["SET".to_string(), "k3".to_string(), "v".to_string()],
32325            ]
32326        );
32327    }
32328
32329    /// The deadline is read back off the key rather than worked out twice.
32330    ///
32331    /// So what crosses is the instant this server picked, and a replica that
32332    /// applies it an hour later still expires the key at the same moment.
32333    #[test]
32334    fn a_relative_deadline_crosses_as_the_instant_it_resolved_to() {
32335        let mut f = Fixture::replicated();
32336        f.crossed(&[b"SET", b"seed", b"1"]);
32337        for parts in [
32338            &[b"SET".as_slice(), b"k", b"v", b"EX", b"100"][..],
32339            &[b"SETEX".as_slice(), b"k", b"100", b"v"][..],
32340        ] {
32341            let words = only(&f.crossed(parts));
32342            assert_eq!(&words[..3], ["SET", "k", "v"], "{words:?}");
32343            assert_eq!(words[3], "PXAT", "{words:?}");
32344            let at: i64 = words[4].parse().expect("an instant");
32345            assert!(at > f.server.clock.now_ms() as i64, "{words:?}");
32346        }
32347        for parts in [
32348            &[b"EXPIRE".as_slice(), b"k", b"50"][..],
32349            &[b"PEXPIRE".as_slice(), b"k", b"50000"][..],
32350            &[b"EXPIREAT".as_slice(), b"k", b"99999999999"][..],
32351            &[b"GETEX".as_slice(), b"k", b"EX", b"100"][..],
32352        ] {
32353            let words = only(&f.crossed(parts));
32354            assert_eq!(&words[..2], ["PEXPIREAT", "k"], "{words:?}");
32355        }
32356        assert_eq!(
32357            only(&f.crossed(&[b"GETEX", b"k", b"PERSIST"])),
32358            ["PERSIST", "k"]
32359        );
32360    }
32361
32362    /// A read sends nothing, and neither does a write that was refused.
32363    #[test]
32364    fn nothing_crosses_for_a_read_or_for_a_failure() {
32365        let mut f = Fixture::replicated();
32366        f.crossed(&[b"SET", b"k", b"v"]);
32367        for parts in [
32368            &[b"GET".as_slice(), b"k"][..],
32369            &[b"TYPE".as_slice(), b"k"][..],
32370            &[b"STRLEN".as_slice(), b"k"][..],
32371            &[b"EXISTS".as_slice(), b"k"][..],
32372            &[b"PING".as_slice()][..],
32373            // Refused, and a refusal leaves the stream alone whatever the body
32374            // pushed before it found out.
32375            &[b"LPUSH".as_slice(), b"k", b"a"][..],
32376            &[b"INCR".as_slice(), b"k"][..],
32377        ] {
32378            assert_eq!(f.crossed(parts), "", "{parts:?}");
32379        }
32380    }
32381
32382    /// A write that changed nothing still crosses, which is D-140.
32383    ///
32384    /// Redis decides with a counter of real changes and sends nothing when it
32385    /// did not move. There is no such counter here yet, so what is sent is what
32386    /// can be said without one: an accepted write goes down the link. The ones
32387    /// whose verbatim form would be wrong rather than merely wasteful already
32388    /// say so for themselves, which is the second half of this.
32389    #[test]
32390    fn a_write_that_changed_nothing_still_crosses() {
32391        let mut f = Fixture::replicated();
32392        f.crossed(&[b"SET", b"k", b"v"]);
32393        assert_eq!(
32394            only(&f.crossed(&[b"DEL", b"nosuchkey"])),
32395            ["DEL", "nosuchkey"]
32396        );
32397        assert_eq!(only(&f.crossed(&[b"SET", b"k", b"v"])), ["SET", "k", "v"]);
32398        // And the ones that would be wrong say nothing, whatever the rule above.
32399        for parts in [
32400            &[b"SPOP".as_slice(), b"nosuchset"][..],
32401            &[b"EXPIRE".as_slice(), b"nosuchkey", b"100"][..],
32402            &[
32403                b"XADD".as_slice(),
32404                b"nosuchstream",
32405                b"NOMKSTREAM",
32406                b"*",
32407                b"f",
32408                b"v",
32409            ][..],
32410        ] {
32411            assert_eq!(f.crossed(parts), "", "{parts:?}");
32412        }
32413    }
32414
32415    /// A conditional write crosses as the plain one, since the condition was
32416    /// decided here and a replica has no business deciding it again.
32417    #[test]
32418    fn a_condition_that_held_crosses_without_it() {
32419        let mut f = Fixture::replicated();
32420        f.crossed(&[b"SET", b"seed", b"1"]);
32421        assert_eq!(only(&f.crossed(&[b"SETNX", b"k", b"v"])), ["SET", "k", "v"]);
32422        assert_eq!(
32423            only(&f.crossed(&[b"SET", b"k", b"w", b"XX"])),
32424            ["SET", "k", "w"]
32425        );
32426    }
32427
32428    /// A write whose result depends on where it ran crosses as the result.
32429    #[test]
32430    fn a_random_or_derived_write_crosses_as_what_it_did() {
32431        let mut f = Fixture::replicated();
32432        f.crossed(&[b"SET", b"seed", b"1"]);
32433        f.crossed(&[b"SADD", b"s", b"one", b"two"]);
32434        let words = only(&f.crossed(&[b"SPOP", b"s"]));
32435        assert_eq!(&words[..2], ["SREM", "s"], "{words:?}");
32436        assert!(words[2] == "one" || words[2] == "two", "{words:?}");
32437        // The one that took the last member still crosses as the removal and
32438        // not as the key going, which is a real server's rule and not an
32439        // oversight: the far side takes the member out and finds it is holding
32440        // an empty set, which it drops on its own.
32441        let words = only(&f.crossed(&[b"SPOP", b"s"]));
32442        assert_eq!(&words[..2], ["SREM", "s"], "{words:?}");
32443        // The form with a count is where taking the lot is sent as the delete,
32444        // because there it can be one line instead of a whole set of them.
32445        f.crossed(&[b"SADD", b"s", b"one", b"two"]);
32446        assert_eq!(only(&f.crossed(&[b"SPOP", b"s", b"2"])), ["DEL", "s"]);
32447        f.crossed(&[b"SET", b"n", b"1"]);
32448        assert_eq!(
32449            only(&f.crossed(&[b"INCRBYFLOAT", b"n", b"1.5"])),
32450            ["SET", "n", "2.5", "KEEPTTL"]
32451        );
32452        assert_eq!(only(&f.crossed(&[b"GETDEL", b"n"])), ["DEL", "n"]);
32453        let words = only(&f.crossed(&[b"XADD", b"st", b"*", b"f", b"v"]));
32454        assert_eq!(&words[..2], ["XADD", "st"], "{words:?}");
32455        assert_ne!(words[2], "*", "an auto id has to be resolved: {words:?}");
32456        assert_eq!(&words[3..], ["f", "v"], "{words:?}");
32457    }
32458
32459    /// A key that went on its own crosses as the deletion, ahead of whatever the
32460    /// command that noticed was doing.
32461    ///
32462    /// A replica never expires anything itself, so this is the only way it hears
32463    /// about it, and the order matters: the write that follows would be refused
32464    /// by a replica still holding the old key at the old type.
32465    #[test]
32466    fn an_expiry_a_read_noticed_crosses_as_a_deletion() {
32467        let mut f = Fixture::replicated();
32468        f.crossed(&[b"SET", b"k", b"v", b"PX", b"50"]);
32469        f.advance(100);
32470        assert_eq!(only(&f.crossed(&[b"GET", b"k"])), ["DEL", "k"]);
32471        // And the deletion goes first when the command had something of its own.
32472        f.run(&[b"SET", b"k2", b"v", b"PX", b"50"]);
32473        f.crossed(&[b"PING"]);
32474        f.advance(100);
32475        assert_eq!(
32476            commands(&f.crossed(&[b"LPUSH", b"k2", b"a"])),
32477            vec![
32478                vec!["DEL".to_string(), "k2".to_string()],
32479                vec!["LPUSH".to_string(), "k2".to_string(), "a".to_string()],
32480            ]
32481        );
32482    }
32483
32484    /// A command that parked has done nothing, so nothing crosses.
32485    ///
32486    /// What must never cross is the command as it arrived, since a replica told
32487    /// to `BLPOP` would stop and wait on the one connection that cannot stop.
32488    #[test]
32489    fn a_blocking_command_that_parked_crosses_nothing() {
32490        let mut f = Fixture::replicated();
32491        f.crossed(&[b"SET", b"seed", b"1"]);
32492        assert_eq!(f.flow(&[b"BLPOP", b"gone", b"0"]).0, Flow::Block);
32493        assert_eq!(f.server.stream_since(f.mark).0, "");
32494        f.run(&[b"XADD", b"st", b"1-1", b"f", b"v"]);
32495        f.run(&[b"XGROUP", b"CREATE", b"st", b"g", b"$"]);
32496        f.crossed(&[b"PING"]);
32497        // A group read that read nothing is in the same position, and this one
32498        // does not even park.
32499        f.run(&[
32500            b"XREADGROUP",
32501            b"GROUP",
32502            b"g",
32503            b"c",
32504            b"COUNT",
32505            b"1",
32506            b"STREAMS",
32507            b"st",
32508            b">",
32509        ]);
32510        assert_eq!(
32511            only(&f.crossed(&[b"PING"])),
32512            ["XGROUP", "CREATECONSUMER", "st", "g", "c"]
32513        );
32514    }
32515
32516    /// `XGROUP` carries its write flag on its subcommands, which are not in the
32517    /// table yet, so each arm says for itself what it did.
32518    #[test]
32519    fn every_xgroup_subcommand_that_changed_something_crosses() {
32520        let mut f = Fixture::replicated();
32521        f.crossed(&[b"XADD", b"st", b"1-1", b"f", b"v"]);
32522        // The dollar is resolved here, because by the time a replica reads it
32523        // the stream it means is a different length.
32524        assert_eq!(
32525            only(&f.crossed(&[b"XGROUP", b"CREATE", b"st", b"g", b"$"])),
32526            ["XGROUP", "CREATE", "st", "g", "1-1"]
32527        );
32528        assert_eq!(
32529            only(&f.crossed(&[b"XGROUP", b"CREATECONSUMER", b"st", b"g", b"c"])),
32530            ["XGROUP", "CREATECONSUMER", "st", "g", "c"]
32531        );
32532        assert_eq!(
32533            only(&f.crossed(&[b"XGROUP", b"SETID", b"st", b"g", b"0"])),
32534            ["XGROUP", "SETID", "st", "g", "0-0"]
32535        );
32536        assert_eq!(
32537            only(&f.crossed(&[b"XGROUP", b"DELCONSUMER", b"st", b"g", b"c"])),
32538            ["XGROUP", "DELCONSUMER", "st", "g", "c"]
32539        );
32540        assert_eq!(
32541            only(&f.crossed(&[b"XGROUP", b"DESTROY", b"st", b"g"])),
32542            ["XGROUP", "DESTROY", "st", "g"]
32543        );
32544        // And one that changed nothing crosses nothing.
32545        assert_eq!(f.crossed(&[b"XGROUP", b"DESTROY", b"st", b"g"]), "");
32546    }
32547
32548    /// A publish crosses even though it is not a write and touches no key.
32549    ///
32550    /// A client subscribed to a replica is subscribed to the whole server, so
32551    /// it has to hear what was published on the master.
32552    #[test]
32553    fn a_publish_crosses_with_nobody_listening() {
32554        let mut f = Fixture::replicated();
32555        f.crossed(&[b"SET", b"seed", b"1"]);
32556        assert_eq!(
32557            only(&f.crossed(&[b"PUBLISH", b"news", b"hello"])),
32558            ["PUBLISH", "news", "hello"]
32559        );
32560        assert_eq!(
32561            only(&f.crossed(&[b"SPUBLISH", b"news", b"hello"])),
32562            ["SPUBLISH", "news", "hello"]
32563        );
32564    }
32565
32566    /// A replica that lost the link for a moment is given the bytes it missed.
32567    ///
32568    /// The number it sends is the position of the first byte it wants counted
32569    /// from one, so a replica that has everything asks for one past the end.
32570    /// Reading that as a count of bytes written instead is an off by one that
32571    /// turns every reconnect into a full resync, which is exactly what a real
32572    /// replica did until this was fixed.
32573    #[test]
32574    fn a_replica_asking_to_carry_on_is_caught_up_from_the_backlog() {
32575        let mut f = Fixture::replicated();
32576        f.crossed(&[b"SET", b"k", b"v"]);
32577        let id = f.server.repl_id();
32578        let had = f.mark;
32579        f.crossed(&[b"SET", b"k2", b"later"]);
32580        let asked = (had + 1).to_string();
32581        let reply = f.run(&[b"PSYNC", &id, asked.as_bytes()]);
32582        assert!(reply.starts_with("+CONTINUE "), "{reply}");
32583        assert!(reply.contains("later"), "{reply}");
32584        // And what it already had is not sent twice.
32585        assert_eq!(reply.matches("k2").count(), 1, "{reply}");
32586    }
32587
32588    /// A replica with nothing to carry on from is sent the whole dataset.
32589    #[test]
32590    fn a_replica_with_no_history_is_sent_a_snapshot() {
32591        let mut f = Fixture::replicated();
32592        f.crossed(&[b"SET", b"k", b"v"]);
32593        let reply = f.run(&[b"PSYNC", b"?", b"-1"]);
32594        assert!(reply.starts_with("+FULLRESYNC "), "{reply}");
32595        // The header, then the image as a bulk string with no newline after it.
32596        let body = reply.split_once("\r\n").expect("a header ends").1;
32597        assert!(body.starts_with('$'), "{body:?}");
32598        assert!(!body.ends_with("\r\n"), "{body:?}");
32599    }
32600
32601    // ------------------------------------------------------ being a replica
32602
32603    /// The whole point of the read only refusal, and the read that goes through.
32604    #[test]
32605    fn a_read_only_replica_refuses_a_write_and_answers_a_read() {
32606        let mut f = Fixture::new();
32607        f.run(&[b"SET", b"k", b"v"]);
32608        f.server.pretend_following("127.0.0.1", 6379, true);
32609        assert_eq!(
32610            f.run(&[b"SET", b"k", b"other"]),
32611            "-READONLY You can't write against a read only replica.\r\n"
32612        );
32613        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
32614        // And a command that is not a write at all is not touched by any of it.
32615        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
32616    }
32617
32618    /// The refusal is off on a server that is nobody's replica, whatever the
32619    /// setting says, because the setting is about being a replica.
32620    #[test]
32621    fn a_master_takes_writes_however_the_read_only_setting_is_left() {
32622        let mut f = Fixture::new();
32623        f.server.set_replica_read_only(true);
32624        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
32625        f.server.pretend_following("127.0.0.1", 6379, true);
32626        assert!(f.run(&[b"SET", b"k", b"v"]).starts_with("-READONLY"));
32627        // And a replica that was told it is writable takes the write.
32628        f.server.set_replica_read_only(false);
32629        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
32630        f.server.set_replica_read_only(true);
32631        // Stopping being a replica is enough on its own, with the setting left
32632        // exactly where it was.
32633        f.server.pretend_master();
32634        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
32635    }
32636
32637    /// The master's own connection is what the refusal is not for.
32638    #[test]
32639    fn the_link_to_the_master_writes_through_the_read_only_refusal() {
32640        let mut f = Fixture::new();
32641        f.server.pretend_following("127.0.0.1", 6379, true);
32642        assert!(f.run(&[b"SET", b"k", b"v"]).starts_with("-READONLY"));
32643        f.session.serve_master(true);
32644        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
32645        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
32646    }
32647
32648    /// A refused `EXEC` fails the whole transaction rather than one command in
32649    /// it, which is the same rule every other gate in `resolved` follows.
32650    #[test]
32651    fn a_transaction_on_a_read_only_replica_is_refused_whole() {
32652        let mut f = Fixture::new();
32653        f.server.pretend_following("127.0.0.1", 6379, true);
32654        f.run(&[b"MULTI"]);
32655        assert!(f.run(&[b"SET", b"k", b"v"]).starts_with("-READONLY"));
32656        assert!(f.run(&[b"EXEC"]).starts_with("-EXECABORT"));
32657    }
32658
32659    /// What an operator reads to find out who this server is following.
32660    #[test]
32661    fn a_replica_says_who_it_follows_in_info_and_in_role() {
32662        let mut f = Fixture::new();
32663        assert!(f.run(&[b"INFO", b"replication"]).contains("role:master"));
32664        f.server.pretend_following("10.0.0.4", 7000, true);
32665        let info = f.run(&[b"INFO", b"replication"]);
32666        assert!(info.contains("role:slave"), "{info}");
32667        assert!(info.contains("master_host:10.0.0.4"), "{info}");
32668        assert!(info.contains("master_port:7000"), "{info}");
32669        assert!(info.contains("master_link_status:up"), "{info}");
32670        assert!(info.contains("slave_read_only:1"), "{info}");
32671        // The five element replica form, and the state word is the one that
32672        // tells an operator whether anything is arriving.
32673        let role = f.run(&[b"ROLE"]);
32674        assert!(role.starts_with("*5\r\n$5\r\nslave\r\n"), "{role}");
32675        assert!(role.contains("10.0.0.4"), "{role}");
32676        assert!(role.contains("connected"), "{role}");
32677        // A link that is down says so in both places rather than in one.
32678        f.server.pretend_following("10.0.0.4", 7000, false);
32679        assert!(
32680            f.run(&[b"INFO", b"replication"])
32681                .contains("master_link_status:down"),
32682            "a link that is not up is down"
32683        );
32684        assert!(f.run(&[b"ROLE"]).contains("connect"));
32685    }
32686
32687    /// A server nobody wrapped in a handle cannot start a link, and says so
32688    /// rather than answering `OK` and doing nothing.
32689    #[test]
32690    fn replicaof_on_an_embedded_server_says_it_is_not_available() {
32691        let mut f = Fixture::new();
32692        let said = f.run(&[b"REPLICAOF", b"127.0.0.1", b"6379"]);
32693        assert!(
32694            said.contains("not available on an embedded server"),
32695            "{said}"
32696        );
32697        // The arity and the port are checked first, so a caller that got the
32698        // command wrong hears about that and not about the handle.
32699        assert!(
32700            f.run(&[b"REPLICAOF", b"127.0.0.1"])
32701                .starts_with("-ERR wrong number")
32702        );
32703        assert_eq!(
32704            f.run(&[b"SLAVEOF", b"127.0.0.1", b"abc"]),
32705            "-ERR Invalid master port\r\n"
32706        );
32707        assert_eq!(
32708            f.run(&[b"REPLICAOF", b"127.0.0.1", b"99999"]),
32709            "-ERR Invalid master port\r\n"
32710        );
32711    }
32712
32713    /// Promotion keeps the history it was part of, which is what lets the
32714    /// replicas that shared it carry on rather than start again.
32715    #[test]
32716    fn a_promotion_keeps_the_old_history_as_the_second_id() {
32717        let f = Fixture::new();
32718        let was = f.server.repl_id();
32719        f.server.promote();
32720        assert_ne!(f.server.repl_id(), was);
32721        let info = {
32722            let mut f = f;
32723            f.run(&[b"INFO", b"replication"])
32724        };
32725        let was = String::from_utf8_lossy(&was).into_owned();
32726        assert!(info.contains(&format!("master_replid2:{was}")), "{info}");
32727    }
32728
32729    /// `DEBUG CHANGE-REPL-ID` is the opposite: a new history and no claim on the
32730    /// old one, so the next `PSYNC` between two servers that shared it is full.
32731    #[test]
32732    fn change_repl_id_takes_a_new_id_and_forgets_the_old_one() {
32733        let mut f = Fixture::new();
32734        let was = f.server.repl_id();
32735        f.server.promote();
32736        assert_eq!(f.run(&[b"DEBUG", b"CHANGE-REPL-ID"]), "+OK\r\n");
32737        assert_ne!(f.server.repl_id(), was);
32738        let info = f.run(&[b"INFO", b"replication"]);
32739        assert!(
32740            info.contains(&format!("master_replid2:{}", "0".repeat(40))),
32741            "{info}"
32742        );
32743    }
32744
32745    /// Every way of getting `FAILOVER` wrong, in the order a real server checks
32746    /// them, because the order is what a script sees when it gets two things
32747    /// wrong at once.
32748    #[test]
32749    fn failover_refuses_in_the_order_the_reference_refuses() {
32750        let mut f = Fixture::new();
32751        // Nothing going on, so ABORT has nothing to abort.
32752        assert_eq!(
32753            f.run(&[b"FAILOVER", b"ABORT"]),
32754            "-ERR No failover in progress.\r\n"
32755        );
32756        // The parsing comes before any of the state checks, and a timeout of
32757        // nought or less has a sentence of its own rather than being a syntax
32758        // error.
32759        assert_eq!(
32760            f.run(&[b"FAILOVER", b"TIMEOUT", b"0"]),
32761            "-ERR FAILOVER timeout must be greater than 0\r\n"
32762        );
32763        assert_eq!(
32764            f.run(&[b"FAILOVER", b"TIMEOUT", b"-1"]),
32765            "-ERR FAILOVER timeout must be greater than 0\r\n"
32766        );
32767        assert!(
32768            f.run(&[b"FAILOVER", b"TIMEOUT", b"abc"])
32769                .starts_with("-ERR value is not an integer")
32770        );
32771        // Each word is taken at most once, so a second one is a syntax error and
32772        // not an overwrite, and anything unrecognised is one too.
32773        assert_eq!(f.run(&[b"FAILOVER", b"bogus"]), "-ERR syntax error\r\n");
32774        assert_eq!(
32775            f.run(&[b"FAILOVER", b"TIMEOUT", b"1", b"TIMEOUT", b"2"]),
32776            "-ERR syntax error\r\n"
32777        );
32778        assert_eq!(
32779            f.run(&[b"FAILOVER", b"FORCE", b"FORCE"]),
32780            "-ERR syntax error\r\n"
32781        );
32782        // TO wants both of its words, so one word short of it is a syntax error
32783        // rather than a target with a missing port.
32784        assert_eq!(f.run(&[b"FAILOVER", b"TO", b"h"]), "-ERR syntax error\r\n");
32785        // ABORT is only ABORT when it is the whole command.
32786        assert_eq!(
32787            f.run(&[b"FAILOVER", b"ABORT", b"TIMEOUT", b"1"]),
32788            "-ERR syntax error\r\n"
32789        );
32790        // Then the state checks. Nobody is following this server, so there is
32791        // nobody to hand the job to, and that is asked before FORCE is.
32792        assert_eq!(
32793            f.run(&[b"FAILOVER"]),
32794            "-ERR FAILOVER requires connected replicas.\r\n"
32795        );
32796        assert_eq!(
32797            f.run(&[b"FAILOVER", b"FORCE"]),
32798            "-ERR FAILOVER requires connected replicas.\r\n"
32799        );
32800        // A replica has nothing of its own to give away.
32801        f.server.pretend_following("10.0.0.4", 7000, true);
32802        assert_eq!(
32803            f.run(&[b"FAILOVER"]),
32804            "-ERR FAILOVER is not valid when server is a replica.\r\n"
32805        );
32806    }
32807
32808    /// The state word `INFO` reports, which is what an operator watching a
32809    /// handover reads, and which is `no-failover` on a server that is not in one.
32810    #[test]
32811    fn a_server_that_is_not_failing_over_says_no_failover() {
32812        let mut f = Fixture::new();
32813        assert!(
32814            f.run(&[b"INFO", b"replication"])
32815                .contains("master_failover_state:no-failover"),
32816            "the field is there and says nothing is going on"
32817        );
32818    }
32819
32820    /// A transaction crosses as the commands it ran, which is D-141: a real
32821    /// server wraps them in `MULTI` and `EXEC`.
32822    #[test]
32823    fn a_transaction_crosses_as_its_commands() {
32824        let mut f = Fixture::replicated();
32825        f.crossed(&[b"SET", b"seed", b"1"]);
32826        f.run(&[b"MULTI"]);
32827        f.run(&[b"SET", b"a", b"1"]);
32828        f.run(&[b"INCR", b"a"]);
32829        assert_eq!(
32830            commands(&f.crossed(&[b"EXEC"])),
32831            vec![
32832                vec!["SET".to_string(), "a".to_string(), "1".to_string()],
32833                vec!["INCR".to_string(), "a".to_string()],
32834            ]
32835        );
32836    }
32837
32838    /// A cluster node owning every slot, with a second node in the table that
32839    /// nobody has met, which is the only way a redirection can fire before the
32840    /// bus is in.
32841    ///
32842    /// The slot `foo` lands in, read off a real server.
32843    const FOO: u16 = 12182;
32844
32845    /// The slot `bar` lands in, which is a different one and is the whole point.
32846    const BAR: u16 = 5061;
32847
32848    fn clustered() -> Fixture {
32849        let mut server = Server::new();
32850        server.enable_cluster("", 7000);
32851        server.cluster_own_everything();
32852        let other = server.cluster_pretend_node(
32853            "5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f",
32854            "10.0.0.9",
32855            7002,
32856        );
32857        assert_eq!(other, 1, "the made up node is the second one in the table");
32858        Fixture::on(server)
32859    }
32860
32861    /// The runs come out in slot order and not in node order, which is the
32862    /// order a real server walks and the order a client that caches the reply
32863    /// by position is counting on.
32864    #[test]
32865    fn cluster_slots_comes_out_in_slot_order() {
32866        let mut f = clustered();
32867        for slot in 0..100u16 {
32868            f.server.cluster_hand_over(slot, 1);
32869        }
32870        let reply = f.run(&[b"CLUSTER", b"SLOTS"]);
32871        assert!(
32872            reply.starts_with("*2\r\n*3\r\n:0\r\n:99\r\n*4\r\n$8\r\n10.0.0.9\r\n:7002\r\n"),
32873            "the other node's run is first because it starts at slot 0: {reply}"
32874        );
32875        assert!(
32876            reply.contains("*3\r\n:100\r\n:16383\r\n"),
32877            "and this node's run is the rest of them: {reply}"
32878        );
32879    }
32880
32881    /// A key in a slot somebody else owns is a redirection and not an answer.
32882    #[test]
32883    fn a_key_on_another_node_is_moved_there() {
32884        let mut f = clustered();
32885        f.server.cluster_hand_over(BAR, 1);
32886        assert_eq!(
32887            f.run(&[b"GET", b"bar"]),
32888            format!("-MOVED {BAR} 10.0.0.9:7002\r\n")
32889        );
32890        // Every other slot is still this node's, so nothing about them moves.
32891        assert_eq!(f.run(&[b"GET", b"foo"]), "$-1\r\n");
32892    }
32893
32894    /// A command that names no key never redirects, whatever the table says,
32895    /// which is what lets a client talk to any node at all.
32896    #[test]
32897    fn a_command_with_no_keys_never_redirects() {
32898        let mut f = clustered();
32899        for slot in 0..16384u16 {
32900            f.server.cluster_hand_over(slot, 1);
32901        }
32902        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
32903        assert_eq!(f.run(&[b"ECHO", b"hi"]), "$2\r\nhi\r\n");
32904    }
32905
32906    /// Two keys in two slots cannot be served by anybody, so the client is told
32907    /// that rather than being sent somewhere that would only fail again.
32908    #[test]
32909    fn two_slots_in_one_command_is_a_cross_slot() {
32910        let mut f = clustered();
32911        assert_eq!(
32912            f.run(&[b"MGET", b"foo", b"bar"]),
32913            "-CROSSSLOT Keys in request don't hash to the same slot\r\n"
32914        );
32915        // The same two keys with a tag that puts them together are fine.
32916        assert_eq!(
32917            f.run(&[b"MGET", b"{t}foo", b"{t}bar"]),
32918            "*2\r\n$-1\r\n$-1\r\n"
32919        );
32920    }
32921
32922    /// A hole in the table beats everything, including the two slots, because a
32923    /// real server works out the first key's node before it looks at the rest.
32924    #[test]
32925    fn a_hole_is_reported_before_the_cross_slot() {
32926        let mut server = Server::new();
32927        server.enable_cluster("", 7000);
32928        let mut f = Fixture::on(server);
32929        assert_eq!(
32930            f.run(&[b"MGET", b"foo", b"bar"]),
32931            "-CLUSTERDOWN Hash slot not served\r\n"
32932        );
32933        // And with the slots back it is the two slots again.
32934        f.server.cluster_own_everything();
32935        assert_eq!(
32936            f.run(&[b"MGET", b"foo", b"bar"]),
32937            "-CROSSSLOT Keys in request don't hash to the same slot\r\n"
32938        );
32939    }
32940
32941    /// A slot on its way out sends a client on for the keys that have gone and
32942    /// answers for the ones that are still here, which is what makes a slot move
32943    /// without a window where a key is on neither node.
32944    #[test]
32945    fn a_migrating_slot_asks_for_the_keys_that_have_gone() {
32946        let mut f = clustered();
32947        f.run(&[b"SET", b"foo", b"1"]);
32948        f.server.cluster_moving(FOO, Some(1), None);
32949        // Still here, so this node answers.
32950        assert_eq!(f.run(&[b"GET", b"foo"]), "$1\r\n1\r\n");
32951        // Gone, so the client is sent on for this one command only.
32952        assert_eq!(
32953            f.run(&[b"GET", b"{foo}gone"]),
32954            format!("-ASK {FOO} 10.0.0.9:7002\r\n")
32955        );
32956    }
32957
32958    /// Some here and some gone is nobody's command to run, and the client is
32959    /// told to come back rather than being given half an answer.
32960    #[test]
32961    fn a_half_moved_slot_is_a_try_again() {
32962        let mut f = clustered();
32963        f.run(&[b"SET", b"{t}here", b"1"]);
32964        let slot = cluster::key_slot(b"{t}here");
32965        f.server.cluster_moving(slot, Some(1), None);
32966        assert_eq!(
32967            f.run(&[b"MGET", b"{t}here", b"{t}gone"]),
32968            "-TRYAGAIN Multiple keys request during rehashing of slot\r\n"
32969        );
32970    }
32971
32972    /// A slot coming in is refused until the connection says `ASKING`, and the
32973    /// permission lasts exactly one command.
32974    #[test]
32975    fn asking_lets_one_command_into_an_importing_slot() {
32976        let mut f = clustered();
32977        f.server.cluster_hand_over(FOO, 1);
32978        f.server.cluster_moving(FOO, None, Some(1));
32979        let moved = format!("-MOVED {FOO} 10.0.0.9:7002\r\n");
32980        assert_eq!(f.run(&[b"GET", b"foo"]), moved);
32981        assert_eq!(f.run(&[b"ASKING"]), "+OK\r\n");
32982        assert_eq!(f.run(&[b"SET", b"foo", b"1"]), "+OK\r\n");
32983        // And it is spent, so the next one is a redirection again.
32984        assert_eq!(f.run(&[b"GET", b"foo"]), moved);
32985    }
32986
32987    /// `RESTORE-ASKING` carries its own `ASKING`, which is the whole reason it
32988    /// exists: the node being sent a slot's keys does not own the slot yet, so a
32989    /// plain `RESTORE` would come back as a redirection to the node sending
32990    /// them and the migration would never get off the ground.
32991    #[test]
32992    fn restore_asking_gets_into_an_importing_slot_on_its_own() {
32993        let mut f = clustered();
32994        f.server.cluster_hand_over(FOO, 1);
32995        f.server.cluster_moving(FOO, None, Some(1));
32996        // The payload is whatever `DUMP` makes of a one byte string, taken from
32997        // this server so the footer is this server's.
32998        f.run(&[b"SET", b"scratch", b"1"]);
32999        let dumped = f.raw(&[b"DUMP", b"scratch"]);
33000        let payload =
33001            &dumped[dumped.iter().position(|b| *b == b'\n').unwrap() + 1..dumped.len() - 2];
33002        let payload = payload.to_vec();
33003        // The ordinary spelling is turned away.
33004        assert_eq!(
33005            f.run(&[b"RESTORE", b"foo", b"0", &payload]),
33006            format!("-MOVED {FOO} 10.0.0.9:7002\r\n")
33007        );
33008        // And the one migration uses is not.
33009        assert_eq!(
33010            f.run(&[b"RESTORE-ASKING", b"foo", b"0", &payload]),
33011            "+OK\r\n"
33012        );
33013        // It is not a connection wide permission either, so the next ordinary
33014        // command is redirected the same as before.
33015        assert_eq!(
33016            f.run(&[b"GET", b"foo"]),
33017            format!("-MOVED {FOO} 10.0.0.9:7002\r\n")
33018        );
33019    }
33020
33021    /// The end of a slot import takes an epoch above everybody else's, which is
33022    /// the only thing that makes the rest of the cluster stop pointing clients
33023    /// at the node the slot came from.
33024    #[test]
33025    fn closing_an_import_takes_a_higher_epoch() {
33026        let mut f = clustered();
33027        f.server.cluster_hand_over(FOO, 1);
33028        f.server.cluster_moving(FOO, None, Some(1));
33029        let me = f.run(&[b"CLUSTER", b"MYID"]);
33030        let me = me[me.find("\r\n").unwrap() + 2..me.len() - 2].to_owned();
33031        assert_eq!(
33032            f.run(&[
33033                b"CLUSTER",
33034                b"SETSLOT",
33035                FOO.to_string().as_bytes(),
33036                b"NODE",
33037                me.as_bytes()
33038            ]),
33039            "+OK\r\n"
33040        );
33041        // The epoch moved on its own, so a bump asked for now has nothing left
33042        // to outrank and says so.
33043        assert_eq!(f.run(&[b"CLUSTER", b"BUMPEPOCH"]), "+STILL 1\r\n");
33044        // And the slot is this node's with nothing left marked.
33045        assert_eq!(f.run(&[b"GET", b"foo"]), "$-1\r\n");
33046    }
33047
33048    /// And a slot handed over without an import behind it does not, because
33049    /// nothing has been taken off anybody and there is nothing to outrank.
33050    #[test]
33051    fn a_plain_hand_over_does_not_touch_the_epoch() {
33052        let mut f = clustered();
33053        let me = f.run(&[b"CLUSTER", b"MYID"]);
33054        let me = me[me.find("\r\n").unwrap() + 2..me.len() - 2].to_owned();
33055        assert_eq!(
33056            f.run(&[
33057                b"CLUSTER",
33058                b"SETSLOT",
33059                FOO.to_string().as_bytes(),
33060                b"NODE",
33061                me.as_bytes()
33062            ]),
33063            "+OK\r\n"
33064        );
33065        assert_eq!(
33066            f.run(&[b"CLUSTER", b"BUMPEPOCH"]),
33067            "+BUMPED 1\r\n",
33068            "the epoch was still zero, so this is the first thing to move it"
33069        );
33070    }
33071
33072    /// A slot is not handed to somebody else while this node still holds keys
33073    /// for it, because that would leave two nodes answering for the same data.
33074    #[test]
33075    fn a_slot_with_keys_in_it_is_not_handed_over() {
33076        let mut f = clustered();
33077        f.run(&[b"SET", b"foo", b"1"]);
33078        let them = b"5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f";
33079        assert_eq!(
33080            f.run(&[
33081                b"CLUSTER",
33082                b"SETSLOT",
33083                FOO.to_string().as_bytes(),
33084                b"NODE",
33085                them
33086            ]),
33087            format!(
33088                "-ERR Can't assign hashslot {FOO} to a different node while I still hold keys for this hash slot.\r\n"
33089            )
33090        );
33091        // With the key gone it goes through.
33092        f.run(&[b"DEL", b"foo"]);
33093        assert_eq!(
33094            f.run(&[
33095                b"CLUSTER",
33096                b"SETSLOT",
33097                FOO.to_string().as_bytes(),
33098                b"NODE",
33099                them
33100            ]),
33101            "+OK\r\n"
33102        );
33103        assert_eq!(
33104            f.run(&[b"GET", b"foo"]),
33105            format!("-MOVED {FOO} 10.0.0.9:7002\r\n")
33106        );
33107    }
33108
33109    /// The slot migration protocol is shut to anybody who is not a node, and the
33110    /// connection goes with the refusal.
33111    ///
33112    /// The hang up is the reference's and it is the part worth having. Nothing
33113    /// behind this command checks that it is being driven in order, because the
33114    /// only thing that ever drives it is another node following the same state
33115    /// machine, so the whole defence is getting in at all and making a guess cost
33116    /// a fresh connection is most of that defence.
33117    #[test]
33118    fn the_slot_migration_protocol_is_shut_to_a_client() {
33119        let mut f = clustered();
33120        // The arity is read first, so a client that sends the container on its own
33121        // is told that much and keeps its connection.
33122        let (flow, reply) = f.flow(&[b"CLUSTER", b"SYNCSLOTS"]);
33123        assert_eq!(
33124            reply,
33125            "-ERR wrong number of arguments for 'cluster|syncslots' command\r\n"
33126        );
33127        assert_eq!(flow, Flow::Continue);
33128        let (flow, reply) = f.flow(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]);
33129        assert_eq!(
33130            reply,
33131            "-ERR CLUSTER SYNCSLOTS subcommands are only allowed for internal clients\r\n"
33132        );
33133        assert_eq!(flow, Flow::Close, "and the socket goes with it");
33134    }
33135
33136    /// The one way in is the secret the whole cluster has agreed on, and there is
33137    /// no secret at all on a server that is not in a cluster.
33138    #[test]
33139    fn the_internal_login_wants_the_cluster_secret() {
33140        let mut f = Fixture::new();
33141        assert_eq!(
33142            f.run(&[b"AUTH", b"internal connection", b"x"]),
33143            "-ERR Cannot authenticate as an internal connection on non-cluster instances\r\n"
33144        );
33145        assert_eq!(
33146            f.run(&[b"DEBUG", b"INTERNAL_SECRET"]),
33147            "-ERR Internal secret is missing\r\n"
33148        );
33149        let mut f = clustered();
33150        assert_eq!(
33151            f.run(&[b"AUTH", b"internal connection", b"x"]),
33152            "-WRONGPASS invalid internal password\r\n"
33153        );
33154        // The name is matched exactly and not the way a keyword is, so this is a
33155        // failed login as a user of that name rather than a failed internal one.
33156        assert_eq!(
33157            f.run(&[b"AUTH", b"INTERNAL CONNECTION", b"x"]),
33158            "-WRONGPASS invalid username-password pair or user is disabled.\r\n"
33159        );
33160        let secret = f.server.cluster_secret();
33161        assert_eq!(secret.len(), 40, "forty characters, like a node id");
33162        assert_eq!(
33163            f.run(&[b"DEBUG", b"INTERNAL_SECRET"]),
33164            format!(":{}\r\n", yo_common::crc::crc16(secret.as_bytes())),
33165            "what comes back is a checksum, so a test can see two nodes agree \
33166             and nobody can log in with what they read"
33167        );
33168        assert_eq!(
33169            f.run(&[b"AUTH", b"internal connection", secret.as_bytes()]),
33170            "+OK\r\n"
33171        );
33172        assert_eq!(
33173            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]),
33174            "+OK\r\n"
33175        );
33176    }
33177
33178    /// `CONF` carries on past an option it did not understand and still says
33179    /// `OK`, so one command can answer with two replies.
33180    #[test]
33181    fn conf_says_ok_after_an_option_it_did_not_know() {
33182        let mut f = clustered();
33183        assert_eq!(f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]), "+OK\r\n");
33184        assert_eq!(
33185            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"zzz", b"1"]),
33186            "-ERR Unknown option zzz\r\n+OK\r\n"
33187        );
33188        // A capability nobody here has heard of is not an unknown option, which
33189        // is what lets a newer node say something to an older one.
33190        assert_eq!(
33191            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"quantum"]),
33192            "+OK\r\n"
33193        );
33194        // The node saying who it is has to be a node this one knows.
33195        assert_eq!(
33196            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"node-id", b"abc"]),
33197            "-ERR Invalid node id length 3\r\n"
33198        );
33199        let unknown = b"1111111111111111111111111111111111111111";
33200        assert_eq!(
33201            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"node-id", unknown]),
33202            "-ERR Node 1111111111111111111111111111111111111111 not found in cluster\r\n"
33203        );
33204        assert_eq!(
33205            f.run(&[
33206                b"CLUSTER",
33207                b"SYNCSLOTS",
33208                b"CONF",
33209                b"node-id",
33210                b"5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f"
33211            ]),
33212            "+OK\r\n"
33213        );
33214        // The size hint is three numbers and the first of them is a slot.
33215        assert_eq!(
33216            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"slot-info", b"5:10:2"]),
33217            "+OK\r\n"
33218        );
33219        for bad in [b"zz".as_slice(), b"16384:0:0", b"5:10:2:3", b"5:-1:0"] {
33220            assert_eq!(
33221                f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"slot-info", bad]),
33222                format!(
33223                    "-ERR Invalid slot info: {}\r\n",
33224                    String::from_utf8_lossy(bad)
33225                )
33226            );
33227        }
33228        // And a master has no business being told what its own migration looks
33229        // like, since it is the one running it.
33230        assert_eq!(
33231            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"asm-task", b"x"]),
33232            "-ERR CLUSTER SYNCSLOTS CONF ASM-TASK only allowed on replica\r\n"
33233        );
33234        assert_eq!(
33235            f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT", b"UNMARK"]),
33236            "+OK\r\n"
33237        );
33238        let (flow, _) = f.flow(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]);
33239        assert_eq!(flow, Flow::Close, "and the door shuts again");
33240    }
33241
33242    /// The slot ranges are checked in full before anything is asked to move, and
33243    /// the answers name what is wrong with them.
33244    #[test]
33245    fn the_slot_ranges_of_a_sync_are_checked_in_full() {
33246        let mut f = clustered();
33247        f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]);
33248        let id = b"5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f";
33249        fn sync<'a>(id: &'a [u8], slots: &[&'a [u8]]) -> Vec<&'a [u8]> {
33250            let mut parts: Vec<&[u8]> = vec![b"CLUSTER", b"SYNCSLOTS", b"SYNC", id];
33251            parts.extend_from_slice(slots);
33252            parts
33253        }
33254        let bar = BAR.to_string();
33255        let bar = bar.as_bytes();
33256        assert_eq!(
33257            f.run(&sync(id, &[b"5", b"4"])),
33258            "-ERR start slot number 5 is greater than end slot number 4\r\n"
33259        );
33260        assert_eq!(
33261            f.run(&sync(id, &[b"99999", b"2"])),
33262            "-ERR Invalid or out of range slot\r\n"
33263        );
33264        // Ranges that touch are joined up and ranges that overlap are not, so
33265        // this is one slot asked for twice and the one below is a run of four.
33266        assert_eq!(
33267            f.run(&sync(id, &[b"1", b"2", b"2", b"3"])),
33268            "-ERR Slot 2 specified multiple times\r\n"
33269        );
33270        // Ranges it can serve get the task and the invitation to open the second
33271        // connection, which is the whole of what the far side is waiting on.
33272        assert_eq!(
33273            f.run(&sync(id, &[b"1", b"2", b"3", b"4"])),
33274            "+RDBCHANNELSYNCSLOTS\r\n"
33275        );
33276        assert_eq!(
33277            f.run(&[b"CLUSTER", b"MIGRATION", b"CANCEL", b"ALL"]),
33278            ":1\r\n"
33279        );
33280        // A slot somebody else owns is not this node's to send.
33281        f.server.cluster_hand_over(BAR, 1);
33282        assert_eq!(
33283            f.run(&sync(id, &[bar])),
33284            "-ERR syntax error\r\n",
33285            "one slot number is not a range, and a shape it does not know is a \
33286             syntax error rather than a count it can complain about"
33287        );
33288        assert_eq!(
33289            f.run(&sync(id, &[bar, bar])),
33290            "-ERR This node is not the owner of the slots\r\n"
33291        );
33292        // And neither way of moving a slot runs while the other one is half done.
33293        f.server.cluster_moving(FOO, Some(1), None);
33294        assert_eq!(
33295            f.run(&sync(id, &[b"1", b"2"])),
33296            "-ERR all slot states must be STABLE to start a slot migration task.\r\n"
33297        );
33298    }
33299
33300    /// The whole of the giving up side, over the wire, in the order the node
33301    /// taking the slots does it.
33302    ///
33303    /// The two connections are one here, which the real protocol never does and
33304    /// nothing in the dispatch layer cares about: what is being read is that the
33305    /// task is created, that the second request is what releases the snapshot,
33306    /// and that the snapshot holds the slots asked for and nothing else.
33307    #[test]
33308    fn a_sync_and_an_rdbchannel_hand_over_the_slots_asked_for() {
33309        let mut f = clustered();
33310        f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]);
33311        f.run(&[b"SET", b"foo", b"in the range"]);
33312        f.run(&[b"SET", b"bar", b"outside it"]);
33313        let id = b"5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f";
33314        let foo = FOO.to_string();
33315        let foo = foo.as_bytes();
33316
33317        // Nothing running, so nothing to report and nothing to cancel.
33318        assert_eq!(
33319            f.run(&[b"CLUSTER", b"MIGRATION", b"STATUS", b"ALL"]),
33320            "*0\r\n"
33321        );
33322        assert_eq!(
33323            f.run(&[b"CLUSTER", b"MIGRATION", b"CANCEL", b"ALL"]),
33324            ":0\r\n"
33325        );
33326
33327        // The snapshot connection cannot come first, because there is no task
33328        // for it to belong to.
33329        assert_eq!(
33330            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"RDBCHANNEL", id]),
33331            "-ERR No slot migration task in progress\r\n"
33332        );
33333        assert_eq!(
33334            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"SYNC", id, foo, foo]),
33335            "+RDBCHANNELSYNCSLOTS\r\n"
33336        );
33337        // Which is a task, waiting for exactly that connection.
33338        let status = f.run(&[b"CLUSTER", b"MIGRATION", b"STATUS", b"ALL"]);
33339        assert!(status.starts_with("*1\r\n"), "{status:?}");
33340        assert!(status.contains("wait-rdbchannel"), "{status:?}");
33341        assert!(status.contains("migrate"), "{status:?}");
33342        // And one at a time, whoever asks.
33343        assert_eq!(
33344            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"SYNC", id, foo, foo]),
33345            "-ERR Another ASM task is already in progress\r\n"
33346        );
33347
33348        let snapshot = f.raw(&[b"CLUSTER", b"SYNCSLOTS", b"RDBCHANNEL", id]);
33349        let text = String::from_utf8_lossy(&snapshot);
33350        assert!(text.starts_with("+SLOTSSNAPSHOT\r\n"), "{text:?}");
33351        assert!(text.contains("$8\r\nFUNCTION\r\n"), "{text:?}");
33352        assert!(
33353            text.contains("$3\r\nSET\r\n$3\r\nfoo\r\n$12\r\nin the range\r\n"),
33354            "{text:?}"
33355        );
33356        assert!(!text.contains("$3\r\nbar\r\n"), "{text:?}");
33357        assert!(
33358            text.ends_with("$7\r\nCLUSTER\r\n$9\r\nSYNCSLOTS\r\n$12\r\nSNAPSHOT-EOF\r\n"),
33359            "{text:?}"
33360        );
33361        // The snapshot has gone, so what is left is the stream behind it.
33362        let status = f.run(&[b"CLUSTER", b"MIGRATION", b"STATUS", b"ID", id]);
33363        assert!(status.contains("send-stream"), "{status:?}");
33364        assert_eq!(
33365            f.run(&[b"CLUSTER", b"MIGRATION", b"CANCEL", b"ID", id]),
33366            ":1\r\n"
33367        );
33368        // Cancelled and kept, so an operator can still ask what happened.
33369        let status = f.run(&[b"CLUSTER", b"MIGRATION", b"STATUS", b"ID", id]);
33370        assert!(status.contains("canceled"), "{status:?}");
33371        assert!(
33372            status.contains("Cancelled due to user request"),
33373            "{status:?}"
33374        );
33375    }
33376
33377    /// A replica takes one thing off its master and nothing at all off anybody
33378    /// else, because there is nothing it could be being asked to hand over.
33379    #[test]
33380    fn a_replica_only_hears_the_settings_and_only_from_its_master() {
33381        let mut server = Server::new();
33382        server.enable_cluster("", 7000);
33383        let of = server.cluster_pretend_node(
33384            "5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f",
33385            "10.0.0.9",
33386            7002,
33387        );
33388        server.cluster_pretend_follower(of);
33389        let mut f = Fixture::on(server);
33390        f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]);
33391        let (flow, reply) = f.flow(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]);
33392        assert_eq!(
33393            reply,
33394            "-ERR CLUSTER SYNCSLOTS subcommands are only allowed for master\r\n"
33395        );
33396        assert_eq!(flow, Flow::Close);
33397        // Off the master's own stream the settings go through, and anything else
33398        // is dropped without a word rather than refused, because an error written
33399        // into the replication stream is an error nobody reads.
33400        f.session.serve_master(true);
33401        assert_eq!(
33402            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]),
33403            "+OK\r\n"
33404        );
33405        assert_eq!(f.run(&[b"CLUSTER", b"SYNCSLOTS", b"SNAPSHOT-EOF"]), "");
33406        assert_eq!(
33407            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"asm-task", b"x"]),
33408            "-ERR Failed to handle master task: x\r\n+OK\r\n",
33409            "there is no migration for a replica to follow along with yet, and \
33410             this option is one the reference keeps going past as well"
33411        );
33412    }
33413
33414    /// The arms that answer nothing at all, which is how the far side of a
33415    /// migration says something it does not expect a reply to.
33416    #[test]
33417    fn the_one_way_arms_of_the_protocol_say_nothing_back() {
33418        let mut f = clustered();
33419        f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]);
33420        assert_eq!(f.run(&[b"CLUSTER", b"SYNCSLOTS", b"ACK", b"x", b"1"]), "");
33421        assert_eq!(f.run(&[b"CLUSTER", b"SYNCSLOTS", b"FAIL", b"boom"]), "");
33422        // The two that say a transfer has ended do the same and drop the
33423        // connection, since there is no transfer here for them to be about.
33424        let (flow, reply) = f.flow(&[b"CLUSTER", b"SYNCSLOTS", b"STREAM-EOF"]);
33425        assert_eq!(reply, "");
33426        assert_eq!(flow, Flow::Close);
33427        // And the one arm that has a real answer on a node with nothing running.
33428        let mut f = clustered();
33429        f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]);
33430        assert_eq!(
33431            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"RDBCHANNEL", b"abc"]),
33432            "-ERR Invalid task id\r\n"
33433        );
33434        assert_eq!(
33435            f.run(&[
33436                b"CLUSTER",
33437                b"SYNCSLOTS",
33438                b"RDBCHANNEL",
33439                b"0000000000000000000000000000000000000000"
33440            ]),
33441            "-ERR No slot migration task in progress\r\n"
33442        );
33443        assert_eq!(
33444            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"NONSENSE"]),
33445            "-ERR syntax error\r\n"
33446        );
33447    }
33448
33449    /// A transaction is refused at queue time rather than at `EXEC`, so a client
33450    /// finds out about the redirection while it can still do something about it.
33451    #[test]
33452    fn a_transaction_is_refused_when_it_is_queued() {
33453        let mut f = clustered();
33454        f.server.cluster_hand_over(BAR, 1);
33455        assert_eq!(f.run(&[b"MULTI"]), "+OK\r\n");
33456        assert_eq!(
33457            f.run(&[b"GET", b"bar"]),
33458            format!("-MOVED {BAR} 10.0.0.9:7002\r\n")
33459        );
33460        assert_eq!(
33461            f.run(&[b"EXEC"]),
33462            "-EXECABORT Transaction discarded because of previous errors.\r\n"
33463        );
33464    }
33465
33466    /// The two commands a cluster refuses outright, because there is only one
33467    /// database in a cluster and nothing to swap it with.
33468    #[test]
33469    fn select_and_swapdb_are_not_cluster_commands() {
33470        let mut f = clustered();
33471        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
33472        assert_eq!(
33473            f.run(&[b"SELECT", b"1"]),
33474            "-ERR SELECT is not allowed in cluster mode\r\n"
33475        );
33476        assert_eq!(
33477            f.run(&[b"SWAPDB", b"0", b"1"]),
33478            "-ERR SWAPDB is not allowed in cluster mode\r\n"
33479        );
33480    }
33481
33482    /// Everything in the container is refused on a server that was not started
33483    /// as a cluster node, and so are the three connection commands.
33484    #[test]
33485    fn a_plain_server_has_no_cluster_in_it() {
33486        let mut f = Fixture::new();
33487        for argv in [
33488            &[b"CLUSTER".as_slice(), b"INFO".as_slice()][..],
33489            &[b"CLUSTER", b"MYID"],
33490            &[b"CLUSTER", b"SLOTS"],
33491            &[b"CLUSTER", b"HELP"],
33492            &[b"ASKING"],
33493            &[b"READONLY"],
33494            &[b"READWRITE"],
33495        ] {
33496            assert_eq!(
33497                f.run(argv),
33498                "-ERR This instance has cluster support disabled\r\n",
33499                "{argv:?}"
33500            );
33501        }
33502        // The arity is still checked in front of the refusal.
33503        assert_eq!(
33504            f.run(&[b"CLUSTER", b"KEYSLOT"]),
33505            "-ERR wrong number of arguments for 'cluster|keyslot' command\r\n"
33506        );
33507    }
33508}