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}
407
408impl Local {
409    /// A thread's counters, starting its compaction cursor at `at`.
410    fn at(at: usize) -> Local {
411        Local {
412            stats: Stats::default(),
413            cmdstats: CommandStats::default(),
414            dirty: AtomicU64::new(0),
415            turn: AtomicU64::new(ALL_DATABASES),
416            parked: AtomicUsize::new(0),
417            collect_ms: AtomicU64::new(u64::MAX),
418            compact_db: AtomicUsize::new(at),
419        }
420    }
421}
422
423impl Default for Local {
424    fn default() -> Local {
425        Local::at(0)
426    }
427}
428
429impl Local {
430    /// Note that a command has run against these databases.
431    fn mark(&self, dbs: u64) {
432        self.dirty.store(self.dirty.load(Relaxed) | dbs, Relaxed);
433    }
434
435    /// Add `dbs` to what this thread's turn is going to look at.
436    fn note(&self, dbs: u64) {
437        self.turn.store(self.turn.load(Relaxed) | dbs, Relaxed);
438    }
439
440    /// Take `at` off the list of databases this thread's turn will look at.
441    fn done(&self, at: usize) {
442        self.turn
443            .store(self.turn.load(Relaxed) & !(1u64 << at), Relaxed);
444    }
445
446    /// Whether this thread's turn still has database `at` to look at.
447    fn wanted(&self, at: usize) -> bool {
448        self.turn.load(Relaxed) & (1u64 << at) != 0
449    }
450
451    /// Whether this thread has yet to take the marks on millisecond `now`.
452    ///
453    /// Says yes once a millisecond and remembers that it did, so the caller can
454    /// ask on every batch and pay for it a thousand times a second.
455    fn collecting(&self, now: u64) -> bool {
456        if self.collect_ms.load(Relaxed) == now {
457            return false;
458        }
459        self.collect_ms.store(now, Relaxed);
460        true
461    }
462
463    /// Note that `n` more of this thread's clients are parked.
464    fn blocked(&self, n: usize) {
465        self.parked
466            .store(self.parked.load(Relaxed).saturating_add(n), Relaxed);
467    }
468
469    /// Note that `n` of them are not parked any more.
470    fn woke(&self, n: usize) {
471        self.parked
472            .store(self.parked.load(Relaxed).saturating_sub(n), Relaxed);
473    }
474}
475
476/// Room for one thread, which is what a server starts with.
477fn one_thread() -> Box<[Local]> {
478    slots(1)
479}
480
481/// Room for `threads` of them.
482fn slots(threads: usize) -> Box<[Local]> {
483    // By index, so that the compaction cursors start spread out over the
484    // databases rather than every thread walking in on the same one.
485    (0..threads.max(1)).map(Local::at).collect()
486}
487
488/// Where the process was started, which is what `dir` defaults to.
489///
490/// A dot if the working directory cannot be read, which happens when it has
491/// been deleted out from under a running process. That is not a reason to
492/// refuse to start a server, and it leaves `BACKUP` to fail with the real error
493/// from the filesystem if anybody asks for one.
494fn working_dir() -> PathBuf {
495    std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
496}
497
498/// One command's counters, for `INFO commandstats`.
499///
500/// Three of Redis's five. `usec` and `usec_per_call` are not here because
501/// nothing times a command, and timing one means two clock reads around a call
502/// that takes tens of nanoseconds to begin with. Redis pays that because Redis
503/// has room for it; this does not, and a zero under a name that says microseconds
504/// is worse than an absent field, which is the same rule the rest of `INFO`
505/// follows.
506#[derive(Debug, Clone, Copy, Default)]
507pub struct CommandStat {
508    /// Times the command ran, whatever it answered.
509    pub calls: u64,
510    /// Times it was turned away before it ran, which is the wrong number of
511    /// arguments or no room under `maxmemory`.
512    pub rejected: u64,
513    /// Times it ran and answered with an error.
514    pub failed: u64,
515}
516
517impl CommandStat {
518    /// Whether this command has ever been seen.
519    ///
520    /// A row that has not is left out of the reply, which is what Redis does and
521    /// is why the section is a handful of lines on a working server rather than
522    /// one line per command in the table.
523    const fn seen(&self) -> bool {
524        self.calls != 0 || self.rejected != 0 || self.failed != 0
525    }
526}
527
528/// One command's counters as one thread keeps them.
529///
530/// The same three numbers as [`CommandStat`], which is what they add up to when
531/// `INFO` asks. This is the written form and that is the read one.
532#[derive(Debug, Default)]
533struct Row {
534    /// Times the command ran.
535    calls: Counter,
536    /// Times it was turned away before it ran.
537    rejected: Counter,
538    /// Times it ran and answered with an error.
539    failed: Counter,
540}
541
542/// A counter per command, indexed the way [`table::index_of`] says.
543///
544/// A flat array and not a map, because the dispatcher is already holding the
545/// spec and the spec's position in the table is two addresses subtracted. That
546/// makes the counting a load, an add and a store on a row the previous command
547/// of the same name has already pulled into cache.
548#[derive(Debug)]
549struct CommandStats(Box<[Row]>);
550
551impl Default for CommandStats {
552    fn default() -> CommandStats {
553        CommandStats((0..table::count()).map(|_| Row::default()).collect())
554    }
555}
556
557impl CommandStats {
558    /// The row for one command.
559    fn at(&self, spec: &'static Spec) -> &Row {
560        &self.0[table::index_of(spec)]
561    }
562}
563
564/// Where a database gets its store from, asked by database number.
565///
566/// `None` means that database cannot have one. The caller owns whatever the
567/// stores are cut out of, which for `yodb` is one `.yo` file with a log per
568/// database, and this crate never learns what any of that is.
569pub type StoreSource = dyn FnMut(usize) -> Option<Store> + Send;
570
571/// Every thread that runs commands here shares this server, so it has to be
572/// `Send` and `Sync`, and the check is here so that a type added to it that is
573/// neither is a compile error where it was added rather than an error in the
574/// code that starts the threads.
575const _: () = {
576    const fn shareable<T: Send + Sync>() {}
577    shareable::<Server>();
578};
579
580/// Everything a server holds.
581///
582/// One per process, however many threads are serving out of it. What is inside
583/// is either shared outright, which is the counters and the settings, or behind
584/// a lock, which is the stripes and the few pieces of state a command can
585/// change. What makes this a server rather than a shard is that it is the whole
586/// of what a connection can address.
587pub struct Server {
588    dbs: Vec<Db>,
589    /// How many stripes each database is cut into, the same for all of them.
590    ///
591    /// Kept here as well as in each database so that the flat slot arithmetic
592    /// below is a multiply and a divide against a field on the server rather
593    /// than a walk asking each database how wide it is.
594    width: usize,
595    clock: Clock,
596    started_ms: u64,
597    /// Where the next hard compaction starts looking, so that a database under
598    /// constant write load cannot hold the other fifteen's space.
599    ///
600    /// Shared, because the thing that asks for one is a command that went over
601    /// the memory limit and is trying to get back under it, and that is any
602    /// thread. Two threads that read the same cursor start on the same
603    /// database, and what that costs is one of them finding the other has
604    /// already moved what was there. It is only written when a segment did
605    /// move, so a server that is not over its limit never touches it at all.
606    ///
607    /// The maintenance turn has its own cursor per thread rather than sharing
608    /// this one. See [`Local::compact_db`] for why.
609    next_db: AtomicUsize,
610    /// One bit per database, set when a command ran against it.
611    ///
612    /// The maintenance turn after every batch used to ask all sixteen
613    /// databases whether they had anything to collect, and asking costs a load
614    /// and a store in each one. Fifteen of those are cold lines on a server
615    /// where every client is on database zero, which is every server, and the
616    /// answer is no every time. This is the cheap half of the question: a
617    /// database nobody has touched since it last said no cannot have started
618    /// saying yes.
619    ///
620    /// What the connections are holding, kept by the engine.
621    ///
622    /// Shared, because every thread has connections and the memory total is one
623    /// total. Each thread adds and subtracts its own change rather than storing
624    /// a figure it worked out, so two threads whose buffers grew in the same
625    /// moment both count.
626    conn_bytes: AtomicUsize,
627    /// The `maxmemory` limit in bytes, zero when there is not one.
628    ///
629    /// Zero is the default and it is the whole reason the check in front of
630    /// every write is one comparison against a field that is already warm. It
631    /// is read by every command on every thread and written by a client that
632    /// sends `CONFIG SET`, so it is a number the threads can share rather than
633    /// a field one of them owns.
634    maxmemory: AtomicU64,
635    /// Where a database gets a store from the first time it needs one.
636    ///
637    /// A closure and not a store, because there are sixteen databases and a
638    /// server that fills memory on database zero should not have opened
639    /// anything for the other fifteen. Nothing is asked of this until a memory
640    /// limit is actually reached, so a server that never fills memory never
641    /// opens a file, and a server that has no file never has one of these.
642    ///
643    /// `None` from the closure means that database cannot have one, which is
644    /// how the caller says the file it opened has no more room for logs.
645    ///
646    /// Behind a lock because it is a closure the caller gave us and there is no
647    /// saying it can be run by two threads at once. It is asked once per
648    /// database, the first time that database has to move something, so a
649    /// server that has reached its memory limit takes this lock sixteen times
650    /// in its life.
651    store: Lock<Option<Box<StoreSource>>>,
652    /// The `maxstore` limit in bytes, `None` when there is not one.
653    ///
654    /// The storage limit, and the other half of the inversion `14` section 4.1
655    /// describes. `maxmemory` is a limit on memory and the right answer to a
656    /// memory limit on a system with a file under it is to move data to the
657    /// file, not to delete it. Deleting is the right answer to a limit on the
658    /// file, and this is that limit.
659    ///
660    /// Zero is not "no limit" here, which is the one place this reads
661    /// differently from `maxmemory` and is the difference that makes a drop in
662    /// cache possible. A storage budget of zero bytes means nothing may live on
663    /// the file, so migration cannot make room and eviction is the only thing
664    /// left, which is Redis exactly. `None` is no limit and is the default,
665    /// which with `noeviction` means the database grows until the disk is full
666    /// and then writes fail, which is what a database does.
667    ///
668    /// Shared between the threads the same way `maxmemory` is, and no limit is
669    /// [`NO_MAXSTORE`] rather than a second field saying whether the first one
670    /// counts. Two fields cannot be read as one, and a limit that was on when
671    /// the bytes were read and off by the time the number was is a limit that
672    /// answers from a server that never existed.
673    maxstore: AtomicU64,
674    /// What [`Server::memory_bytes`] said at the last maintenance turn.
675    ///
676    /// The reading is a walk over every collection in every database and cannot
677    /// go on a command path, so the command path reads this instead and is at
678    /// most one batch behind. What that costs is overshoot: a server can end a
679    /// batch holding one batch's worth of allocation more than its limit before
680    /// anything notices. A batch is 64 commands, so that is bounded by what 64
681    /// commands can allocate and not by how long the server runs.
682    ///
683    /// Only kept up to date when there is a limit to judge it against. A server
684    /// with no `maxmemory` never reads it and never pays for it.
685    ///
686    /// Shared, because it is read in front of every write on every thread and
687    /// written by whichever thread last took a reading. A reader that catches it
688    /// mid write gets one of the two readings and both of them were true a
689    /// moment ago, which is all this number ever claims to be.
690    used: AtomicUsize,
691    /// What the server was holding before a client had written anything.
692    ///
693    /// `MEMORY STATS` reports it as `startup.allocated` and subtracts it from
694    /// the total to work out what a key costs on average, which only means
695    /// something if the baseline is a real reading rather than a guess. So it is
696    /// taken once, at the end of building the server, and never again.
697    startup: AtomicUsize,
698    /// The largest total anything has ever seen here.
699    ///
700    /// See [`Server::peak_bytes`] for what "ever seen" means, which is not the
701    /// same as the largest total there ever was.
702    peak: AtomicUsize,
703    /// Which database the next eviction draws from.
704    ///
705    /// Its own cursor and not [`Server::next_db`], because eviction and
706    /// compaction move at different rates and sharing one would make the
707    /// database that gets compacted depend on how many keys were evicted.
708    ///
709    /// Shared for the same reason [`Server::next_db`] is, and with the same
710    /// answer: two threads evicting at once may pick the same database, and one
711    /// of them finds the other got there first and moves on.
712    evict_db: AtomicUsize,
713    /// Which database the next active expiry sweep starts at.
714    ///
715    /// A third cursor for the same reason there is a second one. A sweep runs on
716    /// every turn of the loop and compaction runs when there is dead space, so
717    /// sharing a cursor would make which database gets swept depend on which one
718    /// was last collected.
719    expire_db: AtomicUsize,
720    /// The millisecond the last active expiry sweep ran on, so the next one on
721    /// the same millisecond does not bother.
722    ///
723    /// One for the server and not one per thread, so the sweeping a server does
724    /// is a function of how long it has been running and not of how many threads
725    /// it was started with. Two threads that read the same millisecond can both
726    /// decide to sweep, which costs one extra sweep of a budget that is already
727    /// small and cannot happen twice for the same millisecond more than once per
728    /// thread.
729    expire_ms: AtomicU64,
730    /// Clients parked on a blocking command.
731    ///
732    /// Behind a lock because a client parks on the thread that ran its command
733    /// and is woken by whichever thread later puts something under a key it
734    /// named, and those are not the same thread. The lock is only ever taken to
735    /// park somebody, to serve somebody or to forget a connection that has gone,
736    /// so a command that does not block never touches it.
737    waiters: Lock<Waiters>,
738    /// How many clients are parked.
739    ///
740    /// Beside the list rather than read out of it, because every command asks
741    /// whether anybody is waiting and nearly every answer is no. Taking a lock
742    /// to be told no would be a cache line every thread has to own to ask, which
743    /// is the cost the list was put behind a lock to avoid.
744    ///
745    /// Written under the lock, by whoever changed the list, so the number and
746    /// the list agree except while a change is in progress. A reader that asks
747    /// during one is told about the moment before it, and the worst that costs
748    /// is a walk of the list that serves nobody or one that has not started yet
749    /// and happens on the next command instead.
750    parked: AtomicUsize,
751    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
752    ///
753    /// Empty on a server nobody has migrated a key out of, which is nearly all
754    /// of them, and it costs a vector's three words to be empty.
755    ///
756    /// Behind a lock because a socket cannot be written by two threads at once
757    /// and a cache of them cannot be searched by one while another is taking an
758    /// entry out. It is held for the whole of a migration, which is a round trip
759    /// to another server, so two threads migrating at the same time take turns.
760    /// That is the right way round: the alternative is a socket per thread per
761    /// peer, and a `MIGRATE` is not what a server spends its time on.
762    peers: Lock<migrate::Peers>,
763    /// What each thread that runs commands here keeps to itself.
764    ///
765    /// A fixed list, because a thread reading its own entry must not have the
766    /// list move under it, and how many threads there will be is known before
767    /// any of them starts. A server nobody told otherwise has one.
768    locals: Box<[Local]>,
769    /// How many entries have been handed out.
770    claimed: AtomicUsize,
771    /// The next client id, which is what `CLIENT ID` answers.
772    ///
773    /// On the server and not on a front, because CLIENT LIST and CLIENT KILL
774    /// name a client by this number across the whole server, and two threads
775    /// counting on their own would hand the same number to two clients. Starts
776    /// at one so that zero is never a client, which is what makes it usable as
777    /// the id of a command that came from nowhere.
778    next_client: AtomicU64,
779    /// Where `BACKUP` puts its files, and where `CONFIG GET dir` points.
780    ///
781    /// Absolute, and resolved once when the server is built rather than every
782    /// time somebody asks. `BACKUP LIST` answers absolute paths and a client is
783    /// entitled to hand one of them to a copy tool, so a relative path that
784    /// meant something different after a `chdir` would be a path that stops
785    /// working for reasons nobody could see.
786    dir: PathBuf,
787    /// What backup is running, if one is.
788    ///
789    /// On the server and not on a session, because a backup outlives the
790    /// connection that asked for it and any other connection can seal it.
791    ///
792    /// Behind a lock because there is one backup at a time and any thread can be
793    /// the one that starts, seals or abandons it. It is held while the base file
794    /// is written, which is what keeps two `BACKUP START` commands from writing
795    /// over each other's files.
796    backup: Lock<backup::State>,
797    /// Whether a sealed backup is sitting on disk.
798    ///
799    /// Beside the state rather than read out of it, because every batch of
800    /// commands asks whether there is a backup old enough to sweep away and on
801    /// nearly every server the answer is that there is no backup at all. A load
802    /// answers that. Written under the lock by whoever moved the phase, so a
803    /// reader that asks mid-change sees the moment before and sweeps one batch
804    /// later, which is a file staying on disk for a few microseconds longer than
805    /// it had to.
806    sealed: AtomicBool,
807    /// The search indexes and the names pointing at them.
808    ///
809    /// On the server and not on a database, which is the one collection in this
810    /// build that is. A real server keeps its indexes in the search module, the
811    /// module has one table, and `SELECT 1` followed by `FT._LIST` lists the
812    /// indexes made on database zero. `search.rs` has the rest of why.
813    ///
814    /// A server nobody has made an index on holds two empty vectors here, which
815    /// is six words and no allocation.
816    ///
817    /// Behind a lock because an index is made and dropped by whichever thread
818    /// ran the command, and the table it goes in is one table. Only the `FT`
819    /// commands take it, so nothing a working server spends its time on comes
820    /// through here.
821    search: Lock<Registry>,
822    /// The replies that came back in pieces and have pieces left.
823    ///
824    /// Beside the indexes rather than inside one, because a cursor is read
825    /// under its own number and a real server resolves the index name on a read
826    /// and then pays no attention to it, so a cursor made on one index reads
827    /// through the name of another. Behind a lock for the reason the registry is
828    /// behind one, and a server nobody has opened a cursor on holds an empty map
829    /// here.
830    cursors: Lock<Cursors>,
831    /// The script bodies `EVALSHA` runs, by their digests.
832    ///
833    /// On the server rather than on a connection, because that is the whole
834    /// point of the cache. A client loads its scripts once when it starts up,
835    /// on whichever connection it happened to open first, and then sends nothing
836    /// but digests forever after, from every connection in its pool.
837    ///
838    /// Behind a lock because loading is a write and every thread can be the one
839    /// doing it. Held only long enough to add a body or copy one out, never
840    /// across a run: a running script calls commands, and those take locks of
841    /// their own.
842    scripts: Lock<lua::Scripts>,
843    /// Every library `FUNCTION LOAD` has taken, and what each one registered.
844    ///
845    /// Data only. A callback is a Lua value and there is an interpreter per
846    /// thread, so what is here is the name, the code, the digest of the code and
847    /// one row per function, and every thread compiles the code for itself the
848    /// first time one of its clients calls into the library.
849    libraries: Lock<lua::library::Libraries>,
850    /// Set by `SHUTDOWN`, and read by whatever is turning the loop.
851    ///
852    /// A flag rather than an exit, because the command layer is not what owns
853    /// the process. It runs inside a batch that has other commands behind it
854    /// and inside a driver that has a socket file to take away and a file to
855    /// close, and a server that calls `exit` from a command handler skips all
856    /// of that. So the command says stop and the driver stops, on the same turn
857    /// and through the same door a signal uses.
858    stopping: AtomicBool,
859    /// Every key any connection is watching, with a stamp on each.
860    ///
861    /// Here and not on the connection, and that is the whole design of `WATCH`
862    /// rather than an implementation detail. A connection cannot see a write
863    /// another thread made, so what records the write has to sit beside the key.
864    /// See the `multi` module for the rest of it.
865    watches: Lock<Watches>,
866    /// How many watched keys there are, so the write path can ask without
867    /// taking the lock.
868    ///
869    /// Zero on every server nobody has sent `WATCH` to, which is very nearly all
870    /// of them, and that is what keeps the cost of watches on a server that has
871    /// none down to one relaxed load per write.
872    watched: AtomicUsize,
873    /// Who is listening on what, for pub/sub.
874    ///
875    /// Here and not on the connection for the reason the watches are: a publish
876    /// arrives on a connection that knows nothing about the subscribers, so what
877    /// finds them has to sit beside the name rather than beside the client. See
878    /// the `pubsub` module for the rest of it.
879    pubsub: Lock<pubsub::Registry>,
880    /// How many subscriptions there are, so a publish can ask without taking
881    /// the lock.
882    ///
883    /// Zero on every server nobody has subscribed on, which is what keeps
884    /// `PUBLISH` on a server with no listeners down to one relaxed load.
885    subs: AtomicUsize,
886    /// One inbox per thread, for messages published on another one.
887    ///
888    /// Its own array and not a field on [`Local`], which is a cache line per
889    /// thread precisely so that no other thread writes to it. A mailbox is a
890    /// line another thread is meant to write to, so it gets one of its own.
891    mail: Box<[pubsub::Mailbox]>,
892    /// Which classes of keyspace notification are turned on.
893    ///
894    /// Zero is off and is the default, so the read every write does costs one
895    /// relaxed load and a test. It is `notify-keyspace-events` and the bits are
896    /// Redis's own, kept in the `notify` module beside the two parsers that
897    /// turn them into the setting text and back.
898    notify: AtomicU32,
899    /// One row per open connection, which is what `CLIENT LIST` reads and what
900    /// `CLIENT KILL` writes to.
901    ///
902    /// Here and not on the front for the reason the watches and the
903    /// subscriptions are here: both commands are about connections the thread
904    /// running them does not own and cannot borrow. See the `clients` module.
905    clients: Lock<clients::Clients>,
906    /// How many connections have been asked to close and not closed yet.
907    ///
908    /// Zero on every server nobody has run `CLIENT KILL` on, which is what keeps
909    /// the check on the flush path down to one load.
910    kills: AtomicUsize,
911    /// When the pause `CLIENT PAUSE` armed runs out, and what it covers.
912    ///
913    /// One word rather than a deadline and a mode beside it, because every
914    /// command on every thread reads this and a server that has never been
915    /// paused should pay one load and one test for it. The low bit says whether
916    /// everything is held or only the writes, and the rest is the deadline in
917    /// milliseconds. Zero is no pause at all, which is why the deadline is
918    /// shifted up rather than packed into the top bits: the whole word is zero
919    /// exactly when nothing is armed.
920    pause: AtomicU64,
921    /// The connections `MONITOR` is feeding, and a count of them.
922    ///
923    /// Here for the third time and for the third version of the same reason:
924    /// the command being reported is running on a thread that cannot reach the
925    /// connection being told about it. See the `monitor` module.
926    monitors: monitor::Monitors,
927    /// Being a master: the identity, the stream and whoever is being fed it.
928    ///
929    /// Here for the fourth time and for the fourth version of the same reason:
930    /// the write being copied is running on a thread that cannot reach the
931    /// connection it has to be copied to. See the `repl` module.
932    repl: repl::Replication,
933    /// Being a replica: who this server follows and the link out to them.
934    ///
935    /// Beside [`Server::repl`] rather than inside it because the two are
936    /// opposite halves of the same idea and a server is nearly always neither.
937    /// See the `follow` module.
938    follow: follow::Follower,
939    /// Handing the master's job over on purpose, which is `FAILOVER`.
940    ///
941    /// Beside the other two because it is the one thing that reaches into both:
942    /// it starts on a master, waits on a replica, and ends with this server
943    /// being one. See the `failover` module.
944    failover: failover::Failover,
945    /// The sixteen thousand slots and who owns each of them, which is all of
946    /// cluster mode and is idle on a server that was not started as a node.
947    ///
948    /// Beside the replication fields because it is the other half of the same
949    /// subject: replication is how one server's keys reach a second, and this is
950    /// how a keyspace too big for one server is cut up in the first place. See
951    /// the `cluster` module.
952    cluster: cluster::Cluster,
953    /// A handle on this server, for the one thing that outlives the command
954    /// that started it.
955    ///
956    /// The replica link is a thread, and a thread cannot borrow the server it
957    /// runs against, so it has to hold a counted handle. Nothing inside a
958    /// `Server` can make one of those out of a borrow, so the handle is put here
959    /// by whoever wrapped the server up, which is `Wire::over` and is the one
960    /// place that has both. Weak rather than strong, because a strong one would
961    /// be a server holding itself alive forever.
962    ///
963    /// Empty on an embedded caller that never built an engine, and `REPLICAOF`
964    /// says so rather than pretending to have started a link.
965    myself: Lock<Weak<Server>>,
966    /// What the saves have done, which is all `INFO persistence` has to report.
967    persist: persist::Persistence,
968    /// Who is allowed to run what, which is also where `requirepass` lives.
969    acl: acl::Users,
970    /// Every refusal the ACL has made, which is what `ACL LOG` reports.
971    acllog: acl::Log,
972    /// The file `ACL LOAD` reads and `ACL SAVE` writes, empty when there is
973    /// none, which is the default and is every server nobody gave one to.
974    ///
975    /// Taken at startup and never changed, the same as on a real server, where
976    /// `aclfile` is an immutable config: a server that could be pointed at a
977    /// different ACL file while it was running would be a server an operator
978    /// could not reason about.
979    aclfile: PathBuf,
980    /// The plain `requirepass`, kept only so `CONFIG GET` can report it.
981    plain: acl::Plain,
982    /// The knobs `DEBUG` turns, which is what a test suite reaches for.
983    debug: debug::Knobs,
984}
985
986impl Server {
987    /// A server with [`DATABASES`] empty databases on the system clock.
988    #[must_use]
989    pub fn new() -> Server {
990        let clock = Clock::system();
991        let server = Server {
992            dbs: (0..DATABASES)
993                .map(|_| Db::with_clock(clock.clone(), 1))
994                .collect(),
995            width: 1,
996            started_ms: clock.now_ms(),
997            clock,
998            next_db: AtomicUsize::new(0),
999            conn_bytes: AtomicUsize::new(0),
1000            maxmemory: AtomicU64::new(0),
1001            store: Lock::new(None),
1002            maxstore: AtomicU64::new(NO_MAXSTORE),
1003            used: AtomicUsize::new(0),
1004            startup: AtomicUsize::new(0),
1005            peak: AtomicUsize::new(0),
1006            evict_db: AtomicUsize::new(0),
1007            expire_db: AtomicUsize::new(0),
1008            expire_ms: AtomicU64::new(0),
1009            waiters: Lock::default(),
1010            parked: AtomicUsize::new(0),
1011            peers: Lock::default(),
1012            locals: one_thread(),
1013            claimed: AtomicUsize::new(0),
1014            next_client: AtomicU64::new(1),
1015            dir: working_dir(),
1016            backup: Lock::default(),
1017            sealed: AtomicBool::new(false),
1018            search: Lock::new(Registry::new()),
1019            cursors: Lock::default(),
1020            scripts: Lock::default(),
1021            libraries: Lock::default(),
1022            stopping: AtomicBool::new(false),
1023            watches: Lock::default(),
1024            watched: AtomicUsize::new(0),
1025            pubsub: Lock::default(),
1026            subs: AtomicUsize::new(0),
1027            notify: AtomicU32::new(0),
1028            clients: Lock::default(),
1029            kills: AtomicUsize::new(0),
1030            pause: AtomicU64::new(0),
1031            monitors: monitor::Monitors::default(),
1032            repl: repl::Replication::default(),
1033            follow: follow::Follower::default(),
1034            failover: failover::Failover::default(),
1035            cluster: cluster::Cluster::default(),
1036            myself: Lock::new(Weak::new()),
1037            persist: persist::Persistence::default(),
1038            acl: acl::Users::default(),
1039            acllog: acl::Log::default(),
1040            aclfile: PathBuf::new(),
1041            plain: acl::Plain::default(),
1042            debug: debug::Knobs::default(),
1043            mail: pubsub::boxes(1),
1044        };
1045        server.note_startup();
1046        server
1047    }
1048
1049    /// A server whose databases are cut into `width` stripes each.
1050    ///
1051    /// Not reachable from the command line yet. Every command group answers on
1052    /// a server of any width now and so does everything that walks a whole
1053    /// database, and the tests run each group at a width of one and a width of
1054    /// eight and check the two agree.
1055    ///
1056    /// What is left before this is what `--threads` sets is the engine. A
1057    /// database being several objects is what makes more than one thread
1058    /// possible, and it is not what makes more than one thread happen.
1059    #[must_use]
1060    pub fn with_width(width: usize) -> Server {
1061        let mut server = Server::new();
1062        // The server's own clock and not a fresh one, because a database
1063        // reading a different clock from the server it is on is a database
1064        // whose keys expire against a time nobody set.
1065        let clock = server.clock.clone();
1066        server.dbs = (0..DATABASES)
1067            .map(|_| Db::with_clock(clock.clone(), width))
1068            .collect();
1069        server.width = server.dbs[0].width();
1070        // Again, because the databases the first reading was taken of have just
1071        // been thrown away and replaced with wider ones, and a wider database
1072        // is a bigger baseline.
1073        server.note_startup();
1074        server
1075    }
1076
1077    /// A server on a clock the caller moves by hand, for tests.
1078    #[must_use]
1079    pub fn with_clock(clock: Clock) -> Server {
1080        let server = Server {
1081            dbs: (0..DATABASES)
1082                .map(|_| Db::with_clock(clock.clone(), 1))
1083                .collect(),
1084            width: 1,
1085            started_ms: clock.now_ms(),
1086            clock,
1087            next_db: AtomicUsize::new(0),
1088            conn_bytes: AtomicUsize::new(0),
1089            maxmemory: AtomicU64::new(0),
1090            store: Lock::new(None),
1091            maxstore: AtomicU64::new(NO_MAXSTORE),
1092            used: AtomicUsize::new(0),
1093            startup: AtomicUsize::new(0),
1094            peak: AtomicUsize::new(0),
1095            evict_db: AtomicUsize::new(0),
1096            expire_db: AtomicUsize::new(0),
1097            expire_ms: AtomicU64::new(0),
1098            waiters: Lock::default(),
1099            parked: AtomicUsize::new(0),
1100            peers: Lock::default(),
1101            locals: one_thread(),
1102            claimed: AtomicUsize::new(0),
1103            next_client: AtomicU64::new(1),
1104            dir: working_dir(),
1105            backup: Lock::default(),
1106            sealed: AtomicBool::new(false),
1107            search: Lock::new(Registry::new()),
1108            cursors: Lock::default(),
1109            scripts: Lock::default(),
1110            libraries: Lock::default(),
1111            stopping: AtomicBool::new(false),
1112            watches: Lock::default(),
1113            watched: AtomicUsize::new(0),
1114            pubsub: Lock::default(),
1115            subs: AtomicUsize::new(0),
1116            notify: AtomicU32::new(0),
1117            clients: Lock::default(),
1118            kills: AtomicUsize::new(0),
1119            pause: AtomicU64::new(0),
1120            monitors: monitor::Monitors::default(),
1121            repl: repl::Replication::default(),
1122            follow: follow::Follower::default(),
1123            failover: failover::Failover::default(),
1124            cluster: cluster::Cluster::default(),
1125            myself: Lock::new(Weak::new()),
1126            persist: persist::Persistence::default(),
1127            acl: acl::Users::default(),
1128            acllog: acl::Log::default(),
1129            aclfile: PathBuf::new(),
1130            plain: acl::Plain::default(),
1131            debug: debug::Knobs::default(),
1132            mail: pubsub::boxes(1),
1133        };
1134        server.note_startup();
1135        server
1136    }
1137
1138    /// One database, by index.
1139    ///
1140    /// A caller that knows which key it wants names the one stripe the key is
1141    /// on rather than working over the whole thing, which is what `at` and its
1142    /// neighbours on [`Db`] are for. A caller that is about a database rather
1143    /// than about a key, which is the snapshot walk and a setting, works over
1144    /// all of them.
1145    ///
1146    /// The database is marked as having had something run against it, which is
1147    /// what this does that [`Server::striped_ref`] does not. Anything that only
1148    /// reads asks for that one and leaves the mark alone.
1149    ///
1150    /// The borrow is shared, and what makes that enough is that a database is
1151    /// several stripes behind a lock each. A caller that wants to change
1152    /// something holds the stripe it is changing, so two threads working on two
1153    /// keys work at once and two working on one key take turns, which is the
1154    /// whole point of cutting a database up.
1155    ///
1156    /// # Panics
1157    ///
1158    /// If `i` is not a database. `SELECT` is the only way a client changes the
1159    /// index and it checks, so an index that is out of range here is a bug in
1160    /// the caller and not something a client can ask for.
1161    pub fn striped(&self, i: usize) -> &Db {
1162        self.mine().mark(1u64 << i);
1163        &self.dbs[i]
1164    }
1165
1166    /// Every keyspace on the server, which is every stripe of every database.
1167    ///
1168    /// What the aggregates walk. A total over the whole server is a total over
1169    /// all of these and the stripe boundaries do not appear in it, which is
1170    /// what makes the numbers `INFO` reports the same numbers whatever the
1171    /// server was cut into.
1172    fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
1173        self.dbs
1174            .iter()
1175            .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
1176    }
1177
1178    /// How many keyspaces there are, counting every stripe of every database.
1179    ///
1180    /// The maintenance turns walk these rather than the databases, because a
1181    /// stripe is the thing that holds an arena and a deadline heap and so it is
1182    /// the thing that has anything to collect.
1183    const fn slots(&self) -> usize {
1184        DATABASES * self.width
1185    }
1186
1187    /// Which database slot `i` belongs to.
1188    const fn slot_db(&self, i: usize) -> usize {
1189        i / self.width
1190    }
1191
1192    /// Keyspace `i` of [`Server::slots`].
1193    fn slot(&self, i: usize) -> Held<'_, Keyspace> {
1194        let (db, stripe) = (i / self.width, i % self.width);
1195        self.dbs[db].hold_stripe(stripe)
1196    }
1197
1198    /// Where `BACKUP` writes and what `CONFIG GET dir` answers.
1199    #[must_use]
1200    pub fn dir(&self) -> &Path {
1201        &self.dir
1202    }
1203
1204    /// Point the server at a different directory, which `yodb serve --dir` does.
1205    ///
1206    /// Only before it is serving. There is no `CONFIG SET dir` here and there
1207    /// is none on a real server either without turning protected configs on,
1208    /// for the good reason that moving it out from under a running backup would
1209    /// leave files nothing can find again.
1210    pub fn set_dir(&mut self, dir: PathBuf) {
1211        self.dir = dir;
1212    }
1213
1214    /// The file `ACL LOAD` reads and `ACL SAVE` writes, or `None` for a server
1215    /// that was not given one.
1216    #[must_use]
1217    pub fn aclfile(&self) -> Option<&Path> {
1218        Some(self.aclfile.as_path()).filter(|p| !p.as_os_str().is_empty())
1219    }
1220
1221    /// Point the server at an ACL file, which `yodb serve --aclfile` does.
1222    ///
1223    /// Only before it is serving, and giving one does not read it: the caller
1224    /// asks for that with [`Server::load_acl`], so that a file that will not
1225    /// parse can stop the process before the port opens rather than after.
1226    pub fn set_aclfile(&mut self, path: PathBuf) {
1227        self.aclfile = path;
1228    }
1229
1230    /// Read the ACL file, if there is one, and make it the server's users.
1231    ///
1232    /// # Errors
1233    ///
1234    /// Everything the file got wrong, in one sentence. A caller starting a
1235    /// server should print it and stop, which is what a real server does: coming
1236    /// up with the users an operator did not ask for is worse than not coming up.
1237    pub fn load_acl(&self) -> std::result::Result<(), String> {
1238        match self.aclfile() {
1239            Some(path) => yo_alloc::allow(|| acl::load_file(self, path)),
1240            None => Ok(()),
1241        }
1242    }
1243
1244    /// Drop a sealed backup that has outlived `backup-sealed-ttl`.
1245    ///
1246    /// Once per batch, from the same maintenance turn that collects the arena.
1247    /// It reads two fields and returns on a server that has never taken a
1248    /// backup, which is nearly all of them.
1249    pub fn backup_expire(&self) {
1250        backup::expire(self);
1251    }
1252
1253    /// Ask for the server to stop, which is what `SHUTDOWN` does.
1254    ///
1255    /// It sets a flag and returns. Nothing here closes a socket, flushes a file
1256    /// or ends the process, because none of those belong to this layer, and a
1257    /// batch that is halfway through still has to finish and be written out.
1258    pub fn stop(&self) {
1259        self.stopping.store(true, Release);
1260    }
1261
1262    /// Whether somebody has asked the server to stop.
1263    ///
1264    /// Read once per turn by the loop, next to the flag a signal sets. The two
1265    /// mean the same thing and are separate only because one arrives from the
1266    /// operating system and the other from a client.
1267    #[must_use]
1268    pub fn stopping(&self) -> bool {
1269        self.stopping.load(Acquire)
1270    }
1271
1272    /// One database, by index, without taking it mutably.
1273    ///
1274    /// What the prefetch stage needs. It runs for all 64 commands in a batch
1275    /// before any of them executes, so it cannot hold the mutable borrow `run`
1276    /// is about to want, and it does not need one: warming a cache line reads
1277    /// nothing and changes nothing.
1278    #[must_use]
1279    pub fn striped_ref(&self, i: usize) -> &Db {
1280        &self.dbs[i]
1281    }
1282
1283    /// The stripe that answers for a database when a setting is read back.
1284    ///
1285    /// A ladder setting and an eviction policy are one number on a real server,
1286    /// and the fact that every stripe of every database carries a copy of it is
1287    /// ours rather than the client's problem. A write puts the same value on
1288    /// every one of them, so any stripe answers for all of them and this is the
1289    /// first one.
1290    fn settings(&self) -> Held<'_, Keyspace> {
1291        self.dbs[0].hold_stripe(0)
1292    }
1293
1294    /// Take a new clock reading, which every database is looking at.
1295    ///
1296    /// Once per turn of the event loop, which is the only place time moves. A
1297    /// command asking what the time is gets the answer the whole batch got, so
1298    /// two keys written by the same batch expire together (`04` section 3).
1299    ///
1300    /// Every thread does this on every turn of its own loop and they do not
1301    /// have to agree about when. The reading is only stored when the
1302    /// millisecond has changed, so what the threads are sharing is a line that
1303    /// is written about a thousand times a second and read millions.
1304    pub fn refresh_clock(&self) {
1305        self.clock.refresh();
1306    }
1307
1308    /// Move every clock here on by `ms`, for tests about expiry.
1309    ///
1310    /// The same thing [`Server::set_clock_ms`] does and by the same argument,
1311    /// except that it moves from wherever the clock is rather than to a stated
1312    /// moment, which is what a test that wants a key to have expired asks for.
1313    pub fn advance_clock_ms(&self, ms: u64) {
1314        let now = self.clock.now_ms() + ms;
1315        self.set_clock_ms(now);
1316    }
1317
1318    /// Move every clock here to `ms` by hand, for tests about expiry.
1319    ///
1320    /// A test cannot wait a hundred seconds and a test that waits a hundred
1321    /// milliseconds is a test that fails on a loaded machine, so time moves on
1322    /// request. The system clock underneath will overwrite this on the next
1323    /// [`Server::refresh_clock`], which is why this is only useful in a test
1324    /// that drives commands directly rather than through the event loop.
1325    pub fn set_clock_ms(&self, ms: u64) {
1326        self.clock.set(ms);
1327    }
1328
1329    /// Seconds since this server was built.
1330    #[must_use]
1331    pub fn uptime_secs(&self) -> u64 {
1332        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
1333    }
1334
1335    /// Bytes held by every database's index and arena, plus the read and reply
1336    /// buffers of every connection.
1337    ///
1338    /// The buffers are in here because they are real and because Redis counts
1339    /// its own, so leaving them out would make the one number people compare
1340    /// flattering rather than true. They are not a database, so nothing in the
1341    /// keyspace can change them and the engine has to say when they move.
1342    #[must_use]
1343    pub fn memory_bytes(&self) -> usize {
1344        self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes()
1345    }
1346
1347    /// What the server was holding before any client had written to it.
1348    ///
1349    /// `MEMORY STATS` reports this as `startup.allocated`.
1350    #[must_use]
1351    pub fn startup_bytes(&self) -> usize {
1352        self.startup.load(Relaxed)
1353    }
1354
1355    /// The largest total anything here has ever seen, this reading included.
1356    ///
1357    /// Peak memory is a sampled number on a real server too: `serverCron` takes
1358    /// a reading every hundred milliseconds and keeps the largest one. This is
1359    /// sampled as well, at the points where the total is already being worked
1360    /// out, which is once a batch on a server with a `maxmemory` and once a call
1361    /// on one without. So on a server with no limit that nobody is watching, the
1362    /// peak is the highest of the readings something asked for, which is the
1363    /// most a server that never takes a reading can honestly claim.
1364    #[must_use]
1365    pub fn peak_bytes(&self) -> usize {
1366        let now = self.memory_bytes();
1367        self.peak.fetch_max(now, Relaxed).max(now)
1368    }
1369
1370    /// Take the reading both of those start from.
1371    fn note_startup(&self) {
1372        let now = self.memory_bytes();
1373        self.startup.store(now, Relaxed);
1374        self.peak.store(now, Relaxed);
1375    }
1376
1377    /// What the keyspace itself is holding, live records only.
1378    ///
1379    /// `used_memory` minus this is what the store costs to run: the index, the
1380    /// space dead records are sitting in until compaction gets to them, and the
1381    /// connections' buffers.
1382    #[must_use]
1383    pub fn dataset_bytes(&self) -> usize {
1384        self.keyspaces()
1385            .map(|db| db.map().arena().live_bytes() as usize)
1386            .sum()
1387    }
1388
1389    /// Bytes the arenas are holding, live and dead together.
1390    #[must_use]
1391    pub fn arena_bytes(&self) -> usize {
1392        self.keyspaces()
1393            .map(|db| db.map().arena().reserved_bytes() as usize)
1394            .sum()
1395    }
1396
1397    /// Bytes the indexes are holding.
1398    #[must_use]
1399    pub fn index_bytes(&self) -> usize {
1400        self.keyspaces()
1401            .map(|db| db.map().index().memory_bytes())
1402            .sum()
1403    }
1404
1405    /// What arena compaction has cost, across every database.
1406    ///
1407    /// The write amplification of value separation, which is invisible from the
1408    /// outside otherwise: a client that writes a megabyte can leave the store
1409    /// copying several more, and the only sign of it without these is that the
1410    /// writes got slower.
1411    #[must_use]
1412    pub fn compaction(&self) -> yo_kv::Compaction {
1413        self.keyspaces().map(|db| db.map().compaction()).fold(
1414            yo_kv::Compaction::default(),
1415            |a, b| yo_kv::Compaction {
1416                walked: a.walked + b.walked,
1417                moved: a.moved + b.moved,
1418                bytes: a.bytes + b.bytes,
1419            },
1420        )
1421    }
1422
1423    /// Freed runs waiting on an arena size class list, across every database.
1424    ///
1425    /// How much of the store's own garbage is already back in circulation. A
1426    /// server whose value lengths repeat keeps a small number here and never
1427    /// compacts, and a server whose lengths wander keeps a large one and does,
1428    /// so the two numbers beside each other say which of the two collectors is
1429    /// doing the work.
1430    #[must_use]
1431    pub fn listed_runs(&self) -> usize {
1432        self.keyspaces()
1433            .map(|db| db.map().arena().listed_runs())
1434            .sum()
1435    }
1436
1437    /// Arena segments whose pages are real, across every database.
1438    #[must_use]
1439    pub fn segment_count(&self) -> usize {
1440        self.keyspaces()
1441            .map(|db| db.map().arena().resident_segments())
1442            .sum()
1443    }
1444
1445    /// What the connections' read and reply buffers are holding.
1446    #[must_use]
1447    pub fn conn_bytes(&self) -> usize {
1448        self.conn_bytes.load(Relaxed)
1449    }
1450
1451    /// Note that the connections are holding `delta` bytes more than they were,
1452    /// or fewer when it is negative.
1453    ///
1454    /// A delta and not a total because the alternative is a walk over every
1455    /// connection, and the walk would have to happen on a turn of the loop
1456    /// rather than when `INFO` asks, which puts the cost of a report on the
1457    /// command path of a server nobody is asking.
1458    pub fn note_conn_bytes(&self, delta: isize) {
1459        // A read and a write and not a fetch and add, because the number is a
1460        // sum of signed changes and the saturating part has to happen in the
1461        // middle. Two threads that change their buffers in the same instant can
1462        // lose one of the two changes, which is a report that is a few kilobytes
1463        // out until the next connection on either thread moves it again.
1464        self.conn_bytes
1465            .store(self.conn_bytes().saturating_add_signed(delta), Relaxed);
1466    }
1467
1468    /// Keys reclaimed by running into them after their deadline.
1469    #[must_use]
1470    pub fn expired_keys(&self) -> u64 {
1471        self.keyspaces().map(|db| db.expired_keys()).sum()
1472    }
1473
1474    /// Hash fields reclaimed after their own deadline passed.
1475    #[must_use]
1476    pub fn expired_fields(&self) -> u64 {
1477        self.keyspaces().map(|db| db.expired_fields()).sum()
1478    }
1479
1480    /// The share of those the cycle found rather than a command tripping over.
1481    #[must_use]
1482    pub fn expired_fields_active(&self) -> u64 {
1483        self.keyspaces().map(|db| db.expired_fields_active()).sum()
1484    }
1485
1486    /// Keys thrown away to make room, which is the other number entirely.
1487    #[must_use]
1488    pub fn evicted_keys(&self) -> u64 {
1489        self.keyspaces().map(|db| db.evicted_keys()).sum()
1490    }
1491
1492    /// Lookups a client's read made that found the key.
1493    #[must_use]
1494    pub fn keyspace_hits(&self) -> u64 {
1495        self.keyspaces().map(|db| db.hits()).sum()
1496    }
1497
1498    /// Lookups a client's read made that did not.
1499    #[must_use]
1500    pub fn keyspace_misses(&self) -> u64 {
1501        self.keyspaces().map(|db| db.misses()).sum()
1502    }
1503
1504    /// Every command that has been seen, with its counters.
1505    ///
1506    /// Only the ones that have. A server reports a handful of lines rather than
1507    /// one per command in the table, which is what Redis does and is the
1508    /// difference between a section a person can read and one they cannot.
1509    pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
1510        (0..table::count())
1511            .map(|at| (table::name_at(at), self.command_stat(at)))
1512            .filter(|(_, row)| row.seen())
1513    }
1514
1515    /// One command's counters, added up over every thread.
1516    fn command_stat(&self, at: usize) -> CommandStat {
1517        let mut sum = CommandStat::default();
1518        for thread in &self.locals {
1519            let row = &thread.cmdstats.0[at];
1520            sum.calls += row.calls.get();
1521            sum.rejected += row.rejected.get();
1522            sum.failed += row.failed.get();
1523        }
1524        sum
1525    }
1526
1527    /// The counters the calling thread writes into.
1528    ///
1529    /// The first call on a thread claims a set and every call after it is a
1530    /// thread local read and an index. A server asked to count from more threads
1531    /// than it was built for wraps round and shares a set, which loses the odd
1532    /// count between two threads and cannot happen to a server `yodb serve`
1533    /// built, because that one is told how many threads it will have before it
1534    /// starts any of them.
1535    pub fn counted(&self) -> &Stats {
1536        &self.mine().stats
1537    }
1538
1539    /// The next client id, taken.
1540    ///
1541    /// Every accept anywhere on this server comes through here, so no two
1542    /// clients share a number however many threads are accepting.
1543    pub fn next_client(&self) -> u64 {
1544        self.next_client.fetch_add(1, Relaxed)
1545    }
1546
1547    /// Say which handle this server is behind, so a background thread can hold
1548    /// one.
1549    ///
1550    /// Called by whoever wrapped it up, as many times as there are threads, and
1551    /// every call after the first says the same thing. It cannot be worked out
1552    /// from the inside, because a `&Server` has no way to reach the handle it
1553    /// is behind, so whoever made the handle has to say.
1554    pub fn is_behind(self: &Arc<Server>) {
1555        let mut myself = self.myself.lock();
1556        if myself.strong_count() == 0 {
1557            *myself = Arc::downgrade(self);
1558        }
1559    }
1560
1561    /// Put that handle down again, so the server can be reached mutably.
1562    ///
1563    /// `Arc::get_mut` counts weak handles as well as strong ones, so a server
1564    /// that knows what it is behind cannot be borrowed mutably while it knows
1565    /// it. Everything that wants a mutable one is startup, which happens before
1566    /// any thread could be holding the handle, so putting it down and picking it
1567    /// up at the next [`Server::is_behind`] costs nothing and keeps the startup
1568    /// path exactly as it was.
1569    pub fn forget_behind(&self) {
1570        let mut myself = self.myself.lock();
1571        *myself = Weak::new();
1572    }
1573
1574    /// A counted handle on this server, for a thread that outlives its caller.
1575    ///
1576    /// `None` on a server nobody wrapped up, and on one that is being dropped,
1577    /// which is the same answer for the same reason: there is no server here to
1578    /// hand a thread.
1579    #[must_use]
1580    pub(crate) fn myself(&self) -> Option<Arc<Server>> {
1581        self.myself.lock().upgrade()
1582    }
1583
1584    /// Which set of per thread state the calling thread is on.
1585    ///
1586    /// The number a blocked client is filed under, so that the thread holding
1587    /// that client's connection is the one that answers it. Claims a set on the
1588    /// first call the same way [`Server::counted`] does, and gives back the same
1589    /// number every time after.
1590    pub fn my_slot(&self) -> usize {
1591        self.mine_at()
1592    }
1593
1594    /// Everything the calling thread keeps to itself.
1595    fn mine(&self) -> &Local {
1596        &self.locals[self.mine_at()]
1597    }
1598
1599    /// The calling thread's place in `locals`, claiming one if it has none.
1600    ///
1601    /// Wraps round when more threads count here than the server was built for,
1602    /// which shares a set between two threads and loses the odd count. That
1603    /// cannot happen to the server `yodb serve` builds, because it is told how
1604    /// many threads it will have before it starts any of them.
1605    fn mine_at(&self) -> usize {
1606        let mut slot = SLOT.get();
1607        if slot == usize::MAX {
1608            slot = self.claimed.fetch_add(1, Relaxed);
1609            SLOT.set(slot);
1610        }
1611        slot % self.locals.len()
1612    }
1613
1614    /// Every thread's numbers added together, which is what `INFO` reports.
1615    #[must_use]
1616    pub fn totals(&self) -> Totals {
1617        let mut sum = Totals::default();
1618        for thread in &self.locals {
1619            sum.clients += thread.stats.clients.get();
1620            sum.connections += thread.stats.connections.get();
1621            sum.commands += thread.stats.commands.get();
1622        }
1623        sum
1624    }
1625
1626    /// The same numbers kept apart, one entry per thread, in slot order.
1627    ///
1628    /// [`Self::totals`] is the sum and it is the sum that answers how busy the
1629    /// server has been. What it cannot answer is whether the threads are
1630    /// carrying the same load as each other, and on a server where every thread
1631    /// keeps the connections it accepted for as long as they are open, that is
1632    /// a question with real consequences: an uneven split is paid by the clients
1633    /// on the crowded thread and is invisible in every number that adds the
1634    /// threads up first.
1635    ///
1636    /// The length is how many threads the server was built for rather than how
1637    /// many have counted anything, so a thread that has not run a command yet
1638    /// shows as zeroes instead of being missing.
1639    #[must_use]
1640    pub fn per_thread(&self) -> Vec<Totals> {
1641        self.locals
1642            .iter()
1643            .map(|thread| Totals {
1644                clients: thread.stats.clients.get(),
1645                connections: thread.stats.connections.get(),
1646                commands: thread.stats.commands.get(),
1647            })
1648            .collect()
1649    }
1650
1651    /// Put the totals back to zero, which is `CONFIG RESETSTAT`.
1652    ///
1653    /// Every thread's set and not only the one asking, since the number the
1654    /// client is resetting is the sum it was just shown. The open connections
1655    /// are left alone because that is a gauge and not a total: the connections
1656    /// are still open.
1657    pub fn reset_stats(&self) {
1658        for thread in &self.locals {
1659            thread.stats.connections.zero();
1660            thread.stats.commands.zero();
1661        }
1662        // These live on the stripes rather than on the threads, so resetting
1663        // them means holding each stripe for as long as it takes to write a
1664        // handful of zeroes. `CONFIG RESETSTAT` is a command a person types, and
1665        // the alternative is a set of numbers a dashboard cannot put back.
1666        for mut db in self.keyspaces() {
1667            db.zero_stats();
1668        }
1669    }
1670
1671    /// Say how many threads will run commands here, before any of them does.
1672    ///
1673    /// What it changes is how many sets of counters there are, and how many
1674    /// pub/sub mailboxes. Called once at startup by whoever is about to start
1675    /// the threads, and calling it on a running server throws away what has been
1676    /// counted so far, which is why it wants the server to itself.
1677    pub fn set_threads(&mut self, threads: usize) {
1678        self.locals = slots(threads);
1679        self.mail = pubsub::boxes(threads);
1680        self.claimed = AtomicUsize::new(0);
1681    }
1682
1683    /// The `maxmemory` limit in bytes, zero when there is not one.
1684    #[must_use]
1685    pub fn maxmemory(&self) -> u64 {
1686        self.maxmemory.load(Relaxed)
1687    }
1688
1689    /// Set the limit, and take a reading straight away.
1690    ///
1691    /// The reading is here rather than left to the next maintenance turn because
1692    /// a client that sets the limit and sends a write in the same batch expects
1693    /// the write to be judged against the limit it just set, and because the
1694    /// cached number is meaningless until the first time there is a limit to
1695    /// compare it with.
1696    ///
1697    /// Turning the limit on also turns on the running total every slab keeps of
1698    /// what its collections hold, and turning it off turns that back off, so a
1699    /// server with no limit is not paying to count something nobody reads. The
1700    /// first reading after switching it on is the walk that the total starts
1701    /// from, and it is the only walk.
1702    pub fn set_maxmemory(&self, bytes: u64) {
1703        self.maxmemory.store(bytes, Relaxed);
1704        for db in &self.dbs {
1705            db.track_memory(bytes != 0);
1706        }
1707        self.used.store(self.settled_memory(), Relaxed);
1708    }
1709
1710    /// Say where a database should get its store from when it needs one.
1711    ///
1712    /// This is what turns the eviction inversion on. Until it is called every
1713    /// database answers a memory limit by evicting, which is Redis, and after it
1714    /// is called a database under memory pressure moves values to whatever the
1715    /// closure hands back instead of throwing keys away.
1716    ///
1717    /// Called at most once per database and only under pressure, so a server
1718    /// that is given a file and never fills memory never touches it.
1719    pub fn set_store_source(
1720        &mut self,
1721        source: impl FnMut(usize) -> Option<Store> + Send + 'static,
1722    ) {
1723        *self.store.lock() = Some(Box::new(source));
1724    }
1725
1726    /// Whether this server has been given somewhere to put cold values.
1727    #[must_use]
1728    pub fn has_store_source(&self) -> bool {
1729        self.store.lock().is_some()
1730    }
1731
1732    /// Open database `at`'s store, if it has not got one and there is one to be
1733    /// had.
1734    ///
1735    /// A store that will not open leaves the database where it was, which is
1736    /// evicting, because a memory limit that cannot be answered by moving data
1737    /// still has to be answered.
1738    fn attach_store(&self, at: usize) {
1739        if self.slot(at).store_bytes().is_some() {
1740            return;
1741        }
1742        // The closure is run with its lock held and the keyspace is taken after
1743        // it has answered, so the file is opened once however many threads asked
1744        // for it and the stripe is not held while a file is being opened.
1745        let mut source = self.store.lock();
1746        let Some(source) = source.as_mut() else {
1747            return;
1748        };
1749        if let Some(blocks) = source(at) {
1750            self.slot(at).attach(blocks);
1751        }
1752    }
1753
1754    /// The `maxstore` limit in bytes, `None` when there is not one.
1755    #[must_use]
1756    pub fn maxstore(&self) -> Option<u64> {
1757        match self.maxstore.load(Relaxed) {
1758            NO_MAXSTORE => None,
1759            bytes => Some(bytes),
1760        }
1761    }
1762
1763    /// Set the storage limit, or clear it with `None`.
1764    ///
1765    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
1766    /// total, because this limit is compared against a number the store keeps
1767    /// and answers on demand, not against a walk.
1768    pub fn set_maxstore(&self, bytes: Option<u64>) {
1769        self.maxstore.store(bytes.unwrap_or(NO_MAXSTORE), Relaxed);
1770    }
1771
1772    /// What every attached store is holding, for `INFO memory`.
1773    ///
1774    /// Zero on a server with nothing attached, which is not the same as a server
1775    /// whose file is empty, and [`Server::regime`] is the field that tells those
1776    /// two apart.
1777    #[must_use]
1778    pub fn store_bytes(&self) -> u64 {
1779        self.keyspaces().filter_map(|db| db.store_bytes()).sum()
1780    }
1781
1782    /// What the file has been asked to do, added up over every database.
1783    ///
1784    /// Counters and not levels, so they only ever go up and a run is the
1785    /// difference between two readings. G9 is a ratio over these: the faults a
1786    /// run took, divided by the point reads it issued, has to come out at 1.05
1787    /// or less with a working set ten times memory. There is no way to work that
1788    /// out from outside the server, so it is reported rather than inferred.
1789    ///
1790    /// A fault is a read that went to the store. Whether it also went to the
1791    /// device depends on the store: a log serves a read out of a resident page
1792    /// without touching anything. At ten times memory almost every fault is a
1793    /// real read, which is why the gate is written against this number, but the
1794    /// two are not the same thing and a run tight against the bar should be
1795    /// checked against what the operating system says.
1796    #[must_use]
1797    pub fn cold_stats(&self) -> yo_kv::tier::Stats {
1798        let mut total = yo_kv::tier::Stats::default();
1799        for db in self.keyspaces() {
1800            let Some(tier) = db.tier() else { continue };
1801            let s = tier.stats();
1802            total.demoted += s.demoted;
1803            total.promoted += s.promoted;
1804            total.faults += s.faults;
1805            total.served += s.served;
1806            total.bytes_out += s.bytes_out;
1807            total.bytes_in += s.bytes_in;
1808        }
1809        total
1810    }
1811
1812    /// Which way this server answers a memory limit, in one word for `INFO`.
1813    ///
1814    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
1815    /// inversion: a memory limit moves values to the file and nothing stored is
1816    /// lost. A server reports one word rather than leaving an operator to work
1817    /// it out from a limit, a setting and whether a file happens to be open.
1818    #[must_use]
1819    pub fn regime(&self) -> &'static str {
1820        if (0..self.slots()).any(|at| self.migrates(at)) {
1821            "migrate"
1822        } else {
1823            "evict"
1824        }
1825    }
1826
1827    /// Whether database `at` answers a memory limit by moving values to the
1828    /// file rather than by throwing keys away.
1829    ///
1830    /// Three things have to hold. There has to be somewhere to move them, which
1831    /// is a store attached to that database or a source that can open one, and
1832    /// on a server that was never given a file this is false everywhere and
1833    /// every database behaves exactly as it did.
1834    /// The storage budget has to be more than nothing, which is what
1835    /// `maxstore 0` says it is not. And the file has to be under that budget,
1836    /// because a full file is a storage limit reached and eviction is the right
1837    /// answer to a storage limit.
1838    fn migrates(&self, at: usize) -> bool {
1839        let cap = self.maxstore();
1840        if cap == Some(0) {
1841            return false;
1842        }
1843        // Out of the stripe first. A match keeps whatever it is looking at
1844        // alive for the whole of itself, and that would be this stripe held
1845        // across the arms for no reason.
1846        let bytes = self.slot(at).store_bytes();
1847        match bytes {
1848            Some(held) => cap.is_none_or(|cap| held < cap),
1849            // Nothing attached, but somewhere to get one from the moment this
1850            // database needs it, which is what makes the answer yes rather than
1851            // no. Opening it here would mean `INFO` opened files.
1852            None => self.store.lock().is_some(),
1853        }
1854    }
1855
1856    /// Take a fresh memory reading, which the maintenance turn does once a batch.
1857    ///
1858    /// Nothing at all when there is no limit, which is the default and is every
1859    /// server that has not asked for one.
1860    pub fn refresh_memory(&self) {
1861        if self.maxmemory() != 0 {
1862            let used = self.settled_memory();
1863            self.used.store(used, Relaxed);
1864            // The peak comes along for free here, because the walk that would
1865            // otherwise cost something has already happened. It is the reason a
1866            // server with a limit has a peak that means what it says and a
1867            // server without one has a peak that is only as good as the last
1868            // time somebody asked.
1869            self.peak.fetch_max(used, Relaxed);
1870        }
1871    }
1872
1873    /// [`Server::memory_bytes`], asked the cheap way.
1874    ///
1875    /// The same number. The difference is that this asks each database only
1876    /// about the collections that could have moved since the last time, which is
1877    /// what a batch touched rather than what the server holds, so it can be
1878    /// asked once a batch and again on every command that is over the limit.
1879    fn settled_memory(&self) -> usize {
1880        self.keyspaces()
1881            .map(|mut db| db.settled_memory_bytes())
1882            .sum::<usize>()
1883            + self.conn_bytes()
1884    }
1885
1886    /// Make room under the `maxmemory` limit, throwing keys away if that is what
1887    /// it takes. Answers whether there is anything left it could throw away.
1888    ///
1889    /// Redis runs the same thing from `processCommand` before every command and
1890    /// so does this: a client that writes has to be judged at the moment it
1891    /// writes, not a batch later, or the limit is a suggestion.
1892    ///
1893    /// Three things happen in the loop and all three are needed. Eviction picks
1894    /// a key and drops it. Compaction gives the pages back, because dropping a
1895    /// key marks its record dead and returns nothing on its own, so a loop that
1896    /// only evicted would throw the whole keyspace away and watch the number
1897    /// stay where it was. The reading is taken again each time round, because
1898    /// the two of them together are the only thing that moves it.
1899    ///
1900    /// # Why running out of budget is not a no
1901    ///
1902    /// `false` means there was nothing left to evict, which is `noeviction`, or
1903    /// a `volatile` policy on a database where nothing has a deadline, or a
1904    /// keyspace that is already empty. It does not mean the server is still over
1905    /// its limit, and that difference is Redis's: `performEvictions` answers
1906    /// `EVICT_FAIL` only when it has run out of things to delete, and
1907    /// `processCommand` refuses the client on that and on nothing else. Running
1908    /// out of time part way through a job it is doing well comes back as
1909    /// `EVICT_RUNNING` and the command goes through, because a server that is
1910    /// evicting steadily and refusing every write while it does it is worse for
1911    /// the client than a little overshoot.
1912    ///
1913    /// # What the limit is worth
1914    ///
1915    /// Space comes back a segment at a time and a segment is two megabytes, so
1916    /// this holds a server to its limit give or take a segment. A `maxmemory` of
1917    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
1918    /// megabytes is asking for a precision this store does not have.
1919    pub fn make_room(&self) -> bool {
1920        let limit = self.maxmemory();
1921        if limit == 0 || self.used.load(Relaxed) as u64 <= limit {
1922            return true;
1923        }
1924        // The cached reading is a batch old and the batch may have compacted
1925        // since, so take a fresh one before throwing anything away. It is the
1926        // settled reading and not the walk, so what this costs is the handful of
1927        // collections the last batch touched and not the whole database.
1928        let mut used = self.settled_memory();
1929        self.used.store(used, Relaxed);
1930        let mut budget = EVICT_BUDGET;
1931        while used as u64 > limit {
1932            let over = used - limit as usize;
1933            if !self.relieve_step(over) {
1934                return false;
1935            }
1936            self.compact_hard_step();
1937            used = self.settled_memory();
1938            self.used.store(used, Relaxed);
1939            budget -= 1;
1940            if budget == 0 {
1941                break;
1942            }
1943        }
1944        true
1945    }
1946
1947    /// Give back `over` bytes from whichever database can, by moving values to
1948    /// the file where there is one and by throwing keys away where there is not.
1949    ///
1950    /// The two answers are the eviction inversion and which one a database gets
1951    /// is [`Server::migrates`]. Answers whether anything was given back at all,
1952    /// and `false` is what refuses the client's write.
1953    ///
1954    /// A store that will not take the bytes counts as nothing given back, so the
1955    /// write is refused rather than turned into a deletion. A disk that is
1956    /// misbehaving is a reason to stop accepting writes and it is not a reason
1957    /// to start losing data that was accepted already.
1958    ///
1959    /// Round robin from a cursor rather than always starting at database zero,
1960    /// so a server using more than one of them does not empty the first before
1961    /// touching the second. Almost every server is on database zero only, where
1962    /// this is one call that answers and fifteen that say the map is empty.
1963    fn relieve_step(&self, over: usize) -> bool {
1964        let from = self.evict_db.load(Relaxed);
1965        for turn in 0..self.slots() {
1966            let i = (from + turn) % self.slots();
1967            // An empty keyspace has nothing to move and opening a log for one
1968            // would cost a resident page window to find that out.
1969            let used = !self.slot(i).is_empty();
1970            let gave = if used && self.migrates(i) {
1971                self.attach_store(i);
1972                // Whether it made room and not whether it moved a key. A round
1973                // that demoted nothing and handed back a segment is a round
1974                // that made room, and reading only the count refuses the write
1975                // that provoked it.
1976                self.slot(i)
1977                    .relieve(over)
1978                    .is_ok_and(yo_kv::tier::Relief::made_room)
1979            } else {
1980                // Against this database rather than whichever one the write
1981                // that provoked the eviction was aimed at, since the key that
1982                // goes is this one's. The funnel is already armed above and
1983                // this is a second one inside it, which is what the answer
1984                // going back into the drain is for.
1985                let armed = notify::arm(self, self.slot_db(i));
1986                let gone = self.slot(i).evict_one();
1987                notify::drain(self, armed);
1988                gone
1989            };
1990            if gave {
1991                self.evict_db.store((i + 1) % self.slots(), Relaxed);
1992                self.mine().mark(1u64 << self.slot_db(i));
1993                return true;
1994            }
1995        }
1996        false
1997    }
1998
1999    /// The sweep the shard loop calls, at most once a millisecond.
2000    ///
2001    /// The gate is the whole difference between this and [`Server::expire_step`].
2002    /// A maintenance slice runs on every turn of the loop and a turn is a
2003    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
2004    /// thousand times per millisecond and spend a real share of the shard on
2005    /// looking for keys that cannot have died since the last look. Nothing in a
2006    /// database changes fast enough to be worth asking about more often than the
2007    /// clock can tell the difference, and the clock here is milliseconds.
2008    ///
2009    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
2010    /// hertz, so this is not the thing that decides how promptly memory comes
2011    /// back. What it decides is that an idle server sweeps a thousand times a
2012    /// second rather than a million.
2013    pub fn expire_slice(&self, budget: usize) -> usize {
2014        // `DEBUG SET-ACTIVE-EXPIRE 0`, which is what a test that wants to see a
2015        // key that is logically gone but still on the shelf turns off. Read
2016        // before the clock because it is the cheaper of the two and because a
2017        // server with the sweep off should not be paying for the clock either.
2018        if !self.expiring() {
2019            return 0;
2020        }
2021        let now = self.clock.now_ms();
2022        if now == self.expire_ms.load(Relaxed) {
2023            return 0;
2024        }
2025        self.expire_ms.store(now, Relaxed);
2026        self.expire_step(budget)
2027    }
2028
2029    /// Sweep dead keys out of the databases, spending at most `budget` looks.
2030    ///
2031    /// Answers what it spent, so the caller can charge its maintenance slice for
2032    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
2033    ///
2034    /// Round robin from its own cursor, and every database gets offered whatever
2035    /// is left of the budget rather than a sixteenth of it each, so a server on
2036    /// database zero only, which is nearly every server, spends the whole slice
2037    /// where the keys are. The fifteen empty ones cost a comparison apiece
2038    /// because a database with no key carrying a deadline says so without
2039    /// drawing anything.
2040    ///
2041    /// The cursor moves to the database after whichever one did the work, so two
2042    /// busy databases take turns instead of the lower numbered one starving the
2043    /// other.
2044    pub fn expire_step(&self, budget: usize) -> usize {
2045        let slots = self.slots();
2046        let mut spent = 0;
2047        let from = self.expire_db.load(Relaxed);
2048        for turn in 0..slots {
2049            if spent >= budget {
2050                break;
2051            }
2052            let i = (from + turn) % slots;
2053            // Nothing armed this thread, because nothing asked for any of this:
2054            // the shard loop is between commands. So the sweep arms and drains
2055            // around itself, and a key it takes is news to a subscriber in the
2056            // same way a key a lookup took on the way past is.
2057            let armed = notify::arm(self, self.slot_db(i));
2058            // Held once for both cycles rather than taken again for the second.
2059            // Two takes of a stripe lock to ask two questions about the same
2060            // stripe is one more line every other thread has to wait for, and
2061            // this asks on every turn of every worker's loop.
2062            let mut slot = self.slot(i);
2063            let c = slot.expire_cycle(budget - spent);
2064            // And the fields, which are the other thing with a deadline nobody
2065            // is waiting on. It draws from its own list and charges the same
2066            // budget, so a database with no hash field deadlines anywhere pays a
2067            // comparison for it and a database full of them cannot starve the
2068            // key sweep.
2069            let left = (budget - spent).saturating_sub(c.examined);
2070            let fields = slot.field_expire_cycle(left);
2071            drop(slot);
2072            notify::drain(self, armed);
2073            // The same deletions a lookup's would be, from the other end of the
2074            // same hook. A replica hears about a key the sweep took exactly as
2075            // it hears about one a `GET` took.
2076            repl::swept(self, self.slot_db(i));
2077            spent += c.examined + fields;
2078            if c.expired > 0 {
2079                self.expire_db.store((i + 1) % slots, Relaxed);
2080                self.mine().note(1u64 << self.slot_db(i));
2081            }
2082        }
2083        spent
2084    }
2085
2086    /// One slice of compaction for a server that is over its limit.
2087    ///
2088    /// Round robin the way [`Server::compact_step`] is, from its own cursor
2089    /// rather than that one's, and it stops at the first database that had
2090    /// something to move and asks with the ratios off. See
2091    /// [`Keyspace::compact_hard`] for what that changes.
2092    fn compact_hard_step(&self) -> Option<usize> {
2093        let from = self.next_db.load(Relaxed);
2094        for turn in 0..self.slots() {
2095            let i = (from + turn) % self.slots();
2096            if let Some(moved) = self.slot(i).compact_hard() {
2097                self.next_db.store((i + 1) % self.slots(), Relaxed);
2098                return Some(moved);
2099            }
2100        }
2101        None
2102    }
2103
2104    /// Take what every thread has marked and add it to the turn's own mask.
2105    ///
2106    /// The mask the turn works from is its own and not a shared one, because a
2107    /// mask it read in place and then cleared a bit of would be a mask that lost
2108    /// whatever another thread marked in between. A swap cannot lose a mark: a
2109    /// thread that ors while the swap happens either gets its bit in before the
2110    /// swap or leaves it there afterwards, and the second one costs one look at
2111    /// a database the turn has already been through.
2112    fn collect_marks(&self) {
2113        let mut marked = 0;
2114        for thread in &self.locals {
2115            marked |= thread.dirty.swap(0, Relaxed);
2116        }
2117        self.mine().note(marked);
2118    }
2119
2120    /// Give one database's dead space back, if any database has enough of it to
2121    /// be worth the move. `None` when no database had a candidate.
2122    ///
2123    /// Once per batch, next to the clock. Overwriting a key writes a new record
2124    /// and counts the old one dead, so without this a server holds everything
2125    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
2126    /// a key against Redis at 144 for the same load, and the whole difference
2127    /// was dead records nothing ever came back for.
2128    ///
2129    /// At most one segment moves per call and the search starts one database
2130    /// further along each time, so the cost of asking is a comparison per
2131    /// database and the cost of acting is bounded by a segment.
2132    pub fn compact_step(&self) -> Option<usize> {
2133        // `DEBUG DICT-RESIZING 0`. On a real server that stops a dictionary
2134        // giving back the room it grew into, and this is where the same thing
2135        // happens here: the arena keeps every segment it has taken until this
2136        // walks over and hands one back.
2137        if !self.resizing() {
2138            return None;
2139        }
2140        let slots = self.slots();
2141        let looks = COMPACT_LOOKS.min(slots);
2142        // Once a millisecond per thread rather than once a batch, because the
2143        // swap is over every thread's counter and a call per batch per worker is
2144        // the thread count squared per batch across the server. A mark a
2145        // millisecond old is still a database somebody wrote to, which is the
2146        // only thing the mask is ever asked.
2147        if self.mine().collecting(self.clock.now_ms()) {
2148            self.collect_marks();
2149        }
2150        let mine = self.mine();
2151        // This thread's cursor and not the server's. The load and the store
2152        // either side of this walk happen after every batch on every thread,
2153        // and on the server's cursor that is one line every thread is writing
2154        // to at batch rate for no reason other than to say where to start.
2155        let from = mine.compact_db.load(Relaxed);
2156        for turn in 0..looks {
2157            let i = (from + turn) % slots;
2158            // Nothing has run against this database since it last said it had
2159            // nothing to collect, so it still has nothing to collect and the
2160            // line it lives on stays where it is.
2161            let at = self.slot_db(i);
2162            if !mine.wanted(at) {
2163                continue;
2164            }
2165            if let Some(moved) = self.slot(i).compact_step() {
2166                mine.compact_db.store((i + 1) % slots, Relaxed);
2167                return Some(moved);
2168            }
2169            // Only once every stripe of the database has said it has nothing,
2170            // since the bit is per database and one stripe answering for all of
2171            // them would stop the others being asked at all.
2172            if i % self.width == self.width - 1 {
2173                mine.done(at);
2174            }
2175        }
2176        mine.compact_db.store((from + looks) % slots, Relaxed);
2177        None
2178    }
2179}
2180
2181impl Server {
2182    /// Whether anybody is watching anything.
2183    ///
2184    /// The one thing every write asks about watches, and it is a relaxed load of
2185    /// a word that is zero and shared on a server where no client has ever sent
2186    /// `WATCH`. Relaxed is enough because the answer only has to be right by the
2187    /// time it matters: a `WATCH` that has not been published yet has not
2188    /// returned to its client either, so no client can have started a
2189    /// transaction that depends on it.
2190    fn watching(&self) -> bool {
2191        self.watched.load(Relaxed) != 0
2192    }
2193
2194    /// Which classes of keyspace notification are turned on.
2195    ///
2196    /// Zero is off, which is the default and is what nearly every server runs
2197    /// with. Relaxed for the same reason the watch count is: a `CONFIG SET` that
2198    /// has not been published to another thread yet has not answered its client
2199    /// either.
2200    pub(crate) fn notify_flags(&self) -> u32 {
2201        self.notify.load(Relaxed)
2202    }
2203
2204    /// Turn a set of notification classes on, or turn them all off with zero.
2205    pub(crate) fn set_notify_flags(&self, flags: u32) {
2206        self.notify.store(flags, Relaxed);
2207    }
2208
2209    /// Note how many watched keys there are, after the table changed.
2210    ///
2211    /// Taken from the table under the same lock the change was made under, so
2212    /// the count can never say nobody is watching while somebody is.
2213    fn recount(&self, watches: &Watches) {
2214        self.watched.store(watches.len(), Relaxed);
2215    }
2216}
2217
2218impl Default for Server {
2219    fn default() -> Server {
2220        Server::new()
2221    }
2222}
2223
2224/// What one connection has chosen.
2225pub struct Session {
2226    db: usize,
2227    id: u64,
2228    /// Which connection slot on the front this session belongs to.
2229    ///
2230    /// Carried here so that a command can say where a reply for this connection
2231    /// goes without the front having to be asked. Pub/sub is what needs it: a
2232    /// subscription is a row on the server naming a slot, and the subscribe
2233    /// command is the only moment the connection and the server are both in
2234    /// hand. [`u32::MAX`] for a session that is not on a front, which is a test.
2235    conn: u32,
2236    name: Vec<u8>,
2237    /// The `HIMPORT` fieldsets this connection has prepared.
2238    ///
2239    /// Connection state and not keyspace state, which is the reference's design
2240    /// and not a shortcut: a fieldset is invisible to every other connection and
2241    /// the keys built from one outlive it.
2242    sets: himport::Fieldsets,
2243    /// Whether the command running right now was called by a script.
2244    ///
2245    /// The one thing it changes is what a blocking command does when it finds
2246    /// nothing to take. A client that sent `BLPOP` waits; a script that called
2247    /// `BLPOP` cannot, because the whole server is waiting on the script, and a
2248    /// script that parked would park everything behind it. So inside a script a
2249    /// blocking command times out at once and answers the null a client that
2250    /// waited its full timeout would have got. That is a real server's rule and
2251    /// it is why `BLPOP` is not on the list a script may not call.
2252    scripted: bool,
2253    /// The commands held since `MULTI`, `None` when no transaction is open.
2254    ///
2255    /// Connection state and nothing else. A transaction is invisible to every
2256    /// other connection until `EXEC` runs it, and a connection that goes away
2257    /// with one open has simply not run it.
2258    multi: Option<multi::Queue>,
2259    /// What this connection asked `WATCH` about, and what those keys looked
2260    /// like at the time.
2261    ///
2262    /// The other half is on the server, beside the keys, because a write by
2263    /// another thread has to reach it. See `multi` for why keeping the value
2264    /// here and comparing it at `EXEC` is not the same thing.
2265    watching: Vec<multi::Watched>,
2266    /// Whether the command running right now was handed over by `EXEC`.
2267    ///
2268    /// The one thing it changes is the RESP2 subscribe mode refusal, which a
2269    /// real server makes in `processCommand` and so does not make for a command
2270    /// that was queued: `MULTI`, `SUBSCRIBE z`, `GET x`, `EXEC` runs the `GET`
2271    /// on 8.10.1 even though sending it on its own would have been refused.
2272    running: bool,
2273    /// The buffer `EXEC` decodes the queued commands through.
2274    ///
2275    /// It lives here rather than in `exec` so that its capacity survives the
2276    /// transaction. A fresh one has no room for spans, so the first command of
2277    /// every transaction would allocate, and a client that runs transactions in
2278    /// a loop would be allocating on a command path forever. Everywhere else
2279    /// the buffer belongs to the connection already and the same reserve is
2280    /// free after the first command.
2281    replay: crate::request::Argv,
2282    /// What this connection has subscribed to, `None` until it subscribes to
2283    /// anything.
2284    ///
2285    /// Boxed so that a connection that never subscribes carries a null pointer
2286    /// rather than three empty vectors. The other half is on the server, keyed
2287    /// by name, because a publish arrives on a connection that cannot see this
2288    /// one. See the `pubsub` module.
2289    subs: Option<Box<pubsub::Subs>>,
2290    /// The library name and version a client library announces with
2291    /// `CLIENT SETINFO`, empty when it has not.
2292    ///
2293    /// Nothing on the server reads them. They are here because an operator
2294    /// looking at `CLIENT LIST` on a server with a hundred connections wants to
2295    /// know which of them is the Python worker and which is the dashboard, and
2296    /// every mainstream client library sends them on connect.
2297    lib_name: Vec<u8>,
2298    lib_ver: Vec<u8>,
2299    /// `CLIENT NO-EVICT`, which asks that this connection's buffers are not the
2300    /// ones given up when the server is short of memory.
2301    ///
2302    /// Nothing gives up a connection's buffers here yet, so this is remembered
2303    /// and reported and does nothing else, which is the honest half of the
2304    /// command: a client that sets it and reads it back sees what it set.
2305    no_evict: bool,
2306    /// `CLIENT NO-TOUCH`, which asks that reads by this connection do not move
2307    /// a key's place in the eviction order.
2308    no_touch: bool,
2309    /// What this connection has asked to be told about, which is `CLIENT REPLY`.
2310    reply: Reply,
2311    /// The row every other thread sees this connection through.
2312    ///
2313    /// Shared rather than owned, because `CLIENT LIST` and `CLIENT KILL` run on
2314    /// whichever thread the client asking is on and that is very often not this
2315    /// one. Everything the report says about the socket lives in there and
2316    /// nowhere else, and the handful of things the session needs for itself are
2317    /// kept here as well and written to both. See the `clients` module for why
2318    /// the row is words and a small lock rather than one lock.
2319    sock: Arc<Client>,
2320    /// Whether this connection has got past the password, if there is one.
2321    ///
2322    /// Decided when the connection is accepted and not when it first sends
2323    /// something, which is what makes `CONFIG SET requirepass` leave the clients
2324    /// that are already connected alone. False here rather than true because a
2325    /// session nobody told is a session on a server nobody gave a password to,
2326    /// and the gate only reads this when there is one. See the `auth` module.
2327    authenticated: bool,
2328    /// Which user this connection is, and the copy of it its commands are
2329    /// checked against.
2330    ///
2331    /// Boxed because it is three allocations and a connection on a server that
2332    /// has no ACL never reads past the first field of it. See the `acl` module
2333    /// for why a copy rather than a lookup.
2334    acl: Box<acl::Identity>,
2335    /// Whether what this session runs arrived from a master this server is
2336    /// following.
2337    ///
2338    /// False on every connection anybody made, which is what keeps this to a
2339    /// field read on the command path. It exempts the master's stream from the
2340    /// three refusals that are about clients and not about it, being the
2341    /// password, the access control list and the read only refusal, and from
2342    /// `CLIENT PAUSE`. See the `follow` module for why each of those.
2343    master: bool,
2344    /// Whether the command running right now was preceded by `ASKING`.
2345    ///
2346    /// It is what lets a client reach a key in a slot this node is receiving and
2347    /// does not own yet, and it lasts exactly one command, which is what makes
2348    /// it safe: a client that has been told to ask over here says so again for
2349    /// every command it sends, and a client that has not cannot stumble into a
2350    /// half moved slot.
2351    ///
2352    /// Two fields for a one command life, the same pair `CLIENT REPLY SKIP`
2353    /// uses, because the flag has to be set by a command that has not finished
2354    /// and read by the next one. `ASKING` sets the second and the end of every
2355    /// command moves the second into the first.
2356    asking: bool,
2357    asking_next: bool,
2358    /// Whether this connection is another node of the cluster rather than a
2359    /// client.
2360    ///
2361    /// Set by `AUTH "internal connection" <secret>`, where the secret is the
2362    /// forty characters the bus has gossiped the whole cluster onto, so a client
2363    /// cannot set it without already knowing something only the nodes know. What
2364    /// it opens is the slot migration protocol, which changes state a client has
2365    /// no business changing and which is deliberately not guarded against being
2366    /// driven out of order, since the only thing that ever drives it is another
2367    /// node following the same state machine.
2368    internal: bool,
2369    /// Whether the socket goes as soon as the reply to the command running right
2370    /// now has been written, which is the reference's `CLIENT_CLOSE_AFTER_REPLY`.
2371    ///
2372    /// One command sets it, which is a `CLUSTER SYNCSLOTS` from a connection
2373    /// that is not a node. The refusal on its own would be enough to be correct
2374    /// and the hang up is what makes it expensive to sit there guessing.
2375    closing: bool,
2376}
2377
2378/// What a connection has asked to hear back, which is `CLIENT REPLY`.
2379///
2380/// The two skipping states are one command apart on purpose. `CLIENT REPLY
2381/// SKIP` says nothing itself and skips the reply of the command after it, so
2382/// the state has to survive one command and no more, and the way Redis does
2383/// that is with a pair of flags that step forward once a command.
2384#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
2385pub enum Reply {
2386    /// Everything, which is where every connection starts.
2387    #[default]
2388    On,
2389    /// Nothing at all until the client says `ON` again.
2390    Off,
2391    /// Nothing for the command after this one.
2392    SkipNext,
2393    /// This is that command.
2394    SkipNow,
2395}
2396
2397impl Session {
2398    /// A new connection, on database zero with no name.
2399    #[must_use]
2400    pub fn new(id: u64) -> Session {
2401        Session {
2402            db: 0,
2403            id,
2404            conn: u32::MAX,
2405            name: Vec::new(),
2406            sets: himport::Fieldsets::default(),
2407            scripted: false,
2408            multi: None,
2409            watching: Vec::new(),
2410            running: false,
2411            replay: crate::request::Argv::new(),
2412            subs: None,
2413            lib_name: Vec::new(),
2414            lib_ver: Vec::new(),
2415            no_evict: false,
2416            no_touch: false,
2417            reply: Reply::On,
2418            sock: Arc::new(Client::new(id)),
2419            authenticated: false,
2420            master: false,
2421            asking: false,
2422            asking_next: false,
2423            internal: false,
2424            closing: false,
2425            acl: Box::default(),
2426        }
2427    }
2428
2429    /// Say whether this connection starts out past the password.
2430    ///
2431    /// Called once by whoever accepted it, which is the one place that can see
2432    /// both the connection and the server. A connection nobody tells is
2433    /// unauthenticated and gets through anyway on a server with no password,
2434    /// which is every embedded caller and every test.
2435    pub fn admit(&mut self, yes: bool) {
2436        self.authenticated = yes;
2437    }
2438
2439    /// Whether this connection has got past the password.
2440    #[must_use]
2441    pub(crate) fn authenticated(&self) -> bool {
2442        self.authenticated
2443    }
2444
2445    /// Say that everything this session runs comes from a master.
2446    ///
2447    /// Called once, by the replica link, which is the only thing that can say
2448    /// it. A session nobody tells is an ordinary client, which is every
2449    /// connection on every server that is nobody's replica.
2450    pub(crate) fn serve_master(&mut self, yes: bool) {
2451        self.master = yes;
2452    }
2453
2454    /// Whether what this session runs came from a master.
2455    #[must_use]
2456    pub(crate) fn serving_master(&self) -> bool {
2457        self.master
2458    }
2459
2460    /// Say whether this connection is another node of the cluster.
2461    ///
2462    /// The one thing that says yes is `AUTH "internal connection"` with the
2463    /// right secret, and `DEBUG MARK-INTERNAL-CLIENT` says it too so that a test
2464    /// can drive the protocol without a second node.
2465    pub(crate) fn serve_internal(&mut self, yes: bool) {
2466        self.internal = yes;
2467    }
2468
2469    /// Whether this connection is another node of the cluster.
2470    ///
2471    /// A master's own stream counts as one, because everything a replica is told
2472    /// by its master is by definition from a node, which is the reference's rule
2473    /// as well.
2474    #[must_use]
2475    pub(crate) fn internal(&self) -> bool {
2476        self.internal || self.master
2477    }
2478
2479    /// Ask that the socket goes once the reply being written has gone out.
2480    pub(crate) fn hang_up(&mut self) {
2481        self.closing = true;
2482    }
2483
2484    /// Whether it has been asked.
2485    #[must_use]
2486    pub(crate) fn hanging_up(&self) -> bool {
2487        self.closing
2488    }
2489
2490    /// Let the command after this one into a slot this node is receiving, which
2491    /// is what `ASKING` does and it lasts exactly that one command.
2492    pub(crate) fn ask_next(&mut self) {
2493        self.asking_next = true;
2494    }
2495
2496    /// The row every other thread sees this connection through.
2497    ///
2498    /// Handed to the server once, when the connection is accepted, so that
2499    /// `CLIENT LIST` can find it. A session nobody hands over is one no other
2500    /// thread can see, which is every embedded caller and every test.
2501    #[must_use]
2502    pub fn row(&self) -> &Arc<Client> {
2503        &self.sock
2504    }
2505
2506    /// Say when this connection was opened, which is what `age` counts from.
2507    ///
2508    /// Called by whoever opened it, which is the only place that knows. A
2509    /// session nobody tells has no age and reports zero, which is every
2510    /// embedded caller and every test.
2511    pub fn opened(&mut self, now_ms: u64) {
2512        self.sock.since_ms.store(now_ms, Relaxed);
2513        self.sock.last_ms.store(now_ms, Relaxed);
2514    }
2515
2516    /// Say what the socket under this connection is.
2517    ///
2518    /// Called once, by whoever accepted it, which is the only place that knows.
2519    /// The two addresses are already in the spelling `CLIENT INFO` reports them
2520    /// in, because turning a socket address into that spelling is the job of the
2521    /// layer that has the socket.
2522    pub fn set_socket(&mut self, peer: &str, local: &str, fd: i32, unix: bool) {
2523        yo_alloc::allow(|| {
2524            let mut text = self.sock.text.lock();
2525            text.peer.clear();
2526            text.peer.extend_from_slice(peer.as_bytes());
2527            text.local.clear();
2528            text.local.extend_from_slice(local.as_bytes());
2529        });
2530        self.sock.fd.store(fd, Relaxed);
2531        self.sock.set_flag(clients::UNIX, unix);
2532    }
2533
2534    /// Note bytes that arrived, and that a read carried them.
2535    pub fn read_bytes(&mut self, n: usize) {
2536        let row = &self.sock;
2537        row.net_in
2538            .store(row.net_in.load(Relaxed) + n as u64, Relaxed);
2539        row.reads.store(row.reads.load(Relaxed) + 1, Relaxed);
2540    }
2541
2542    /// Note bytes that went out.
2543    pub fn wrote_bytes(&mut self, n: usize) {
2544        let row = &self.sock;
2545        row.net_out
2546            .store(row.net_out.load(Relaxed) + n as u64, Relaxed);
2547    }
2548
2549    /// Note what the two buffers are holding, and which protocol they are in.
2550    ///
2551    /// `waiting` is the framed bytes that have not been read yet, `room` is what
2552    /// is left in the read buffer after them, `held` is what the reply buffer
2553    /// still owes and `reply` is its capacity. The high water mark is kept here
2554    /// rather than by the caller so that the caller only has to say what is true
2555    /// now.
2556    pub fn note_buffers(&mut self, waiting: usize, room: usize, held: usize, reply: usize) {
2557        let row = &self.sock;
2558        row.qbuf.store(waiting as u64, Relaxed);
2559        row.qbuf_free.store(room as u64, Relaxed);
2560        row.obl.store(held as u64, Relaxed);
2561        row.rbs.store(reply as u64, Relaxed);
2562        row.rbp
2563            .store(row.rbp.load(Relaxed).max(reply as u64), Relaxed);
2564    }
2565
2566    /// Note which protocol this connection is being answered in.
2567    ///
2568    /// Written after each command rather than with the buffers, because `HELLO`
2569    /// changes it in the reply buffer and a connection that switched to RESP3
2570    /// halfway through a pipeline should be listed as being on it.
2571    pub fn note_proto(&mut self, version: i64) {
2572        self.sock.resp.store(version as u32, Relaxed);
2573    }
2574
2575    /// Note which command is running, before it runs.
2576    ///
2577    /// The clock is passed in because the session has no way to reach one, and
2578    /// the caller is holding the server anyway. `at` is where the command is in
2579    /// the table, since an index is a word another thread can read and a name is
2580    /// not.
2581    pub(crate) fn ran(&mut self, at: usize, sub: Option<&[u8]>, argv: u64, now_ms: u64) {
2582        self.sock.last_ms.store(now_ms, Relaxed);
2583        self.sock.argv_mem.store(argv, Relaxed);
2584        self.sock.note_command(at, sub);
2585    }
2586
2587    /// Note that the command running is over, and put what it changed about this
2588    /// connection where another thread can see it.
2589    ///
2590    /// The count goes up here and not where the command name is noted, so that
2591    /// a connection asking `CLIENT INFO` is told how many commands it had sent
2592    /// before this one. That is what a real server answers: it counts in
2593    /// `commandProcessed` and that runs after the body.
2594    ///
2595    /// The rest is the publishing. Which database a connection is in, what it is
2596    /// subscribed to, whether it is in a transaction and how many keys it is
2597    /// watching are all things a command can have just changed, and they are all
2598    /// things `CLIENT LIST` on another thread reports. Rather than hunting down
2599    /// every command that can move one of them, all six are written out here,
2600    /// which is six ordinary stores to a line this thread already owns.
2601    pub fn finished(&mut self) {
2602        // One command's worth of `ASKING` steps forward here, which is where a
2603        // real server clears its flag: in `resetClient`, after the body, and
2604        // only for a command that was not `ASKING` itself.
2605        self.asking = core::mem::take(&mut self.asking_next);
2606        let (sub, psub, ssub) = self.sub_counts();
2607        let (multi, multi_mem) = self.queued();
2608        let subscribed = self.subscribed();
2609        let in_multi = self.in_multi();
2610        let watching = self.watching.len();
2611        let db = self.db;
2612        let row = &self.sock;
2613        row.cmds.store(row.cmds.load(Relaxed) + 1, Relaxed);
2614        row.db.store(db as u32, Relaxed);
2615        row.sub.store(sub as u32, Relaxed);
2616        row.psub.store(psub as u32, Relaxed);
2617        row.ssub.store(ssub as u32, Relaxed);
2618        row.watch.store(watching as u32, Relaxed);
2619        row.multi.store(multi, Relaxed);
2620        row.multi_mem.store(multi_mem, Relaxed);
2621        row.set_flag(clients::SUBSCRIBED, subscribed);
2622        row.set_flag(clients::IN_MULTI, in_multi);
2623    }
2624
2625    /// What this connection has asked to hear back.
2626    #[must_use]
2627    pub const fn reply_mode(&self) -> Reply {
2628        self.reply
2629    }
2630
2631    /// Step the skipping state on by one command.
2632    ///
2633    /// Called after every command by whoever is deciding whether to keep the
2634    /// reply, so that `SKIP` covers exactly the one command after it.
2635    pub const fn step_reply(&mut self) {
2636        self.reply = match self.reply {
2637            Reply::SkipNext => Reply::SkipNow,
2638            Reply::SkipNow => Reply::On,
2639            other => other,
2640        };
2641    }
2642
2643    /// Whether a script is what is asking, which only a blocking command reads.
2644    pub(crate) const fn scripted(&self) -> bool {
2645        self.scripted
2646    }
2647
2648    /// Whether `EXEC` is what is asking.
2649    pub(crate) const fn running(&self) -> bool {
2650        self.running
2651    }
2652
2653    /// Whether this connection has sent `MONITOR` and stopped being a client.
2654    ///
2655    /// Read off the row rather than kept beside it, so there is one answer to
2656    /// the question and not two that could disagree. The row is a line this
2657    /// session has already touched by the time anything asks, since noting the
2658    /// command it is running writes to it.
2659    pub(crate) fn monitoring(&self) -> bool {
2660        self.sock.flag(clients::MONITOR)
2661    }
2662
2663    /// Whether this connection has sent `PSYNC` and stopped being a client.
2664    ///
2665    /// Off the row for the same reason the question above it is, and read on the
2666    /// way out rather than on the way in: a replica does keep sending commands,
2667    /// `REPLCONF ACK` once a second forever, and what changes is that none of
2668    /// them is answered.
2669    pub(crate) fn replicating(&self) -> bool {
2670        self.sock.flag(clients::REPLICA)
2671    }
2672
2673    /// Say which connection slot this session is in.
2674    ///
2675    /// Called by the front when it opens the connection, which is the only place
2676    /// that knows. A session nobody tells is not on a front, and the one thing
2677    /// that reads this checks the client id before it acts on it.
2678    pub(crate) fn set_conn(&mut self, conn: u32) {
2679        self.conn = conn;
2680        self.sock.conn.store(conn, Relaxed);
2681    }
2682
2683    /// The connection id, which `HELLO` reports and `CLIENT` will.
2684    #[must_use]
2685    pub const fn id(&self) -> u64 {
2686        self.id
2687    }
2688
2689    /// Which database this connection is working in.
2690    #[must_use]
2691    pub const fn db(&self) -> usize {
2692        self.db
2693    }
2694
2695    /// The name the client gave itself, empty if it gave none.
2696    #[must_use]
2697    pub fn name(&self) -> &[u8] {
2698        &self.name
2699    }
2700
2701    /// Put everything back the way it was when the connection was opened.
2702    ///
2703    /// The protocol is not here because it is not here: it lives in the reply
2704    /// buffer, and `RESET` sets it back there.
2705    pub fn reset(&mut self) {
2706        self.db = 0;
2707        self.name.clear();
2708        self.sock.set_text(|text| &mut text.name, b"");
2709        // `SELECT` leaves these alone and `RESET` does not, both checked
2710        // against 8.10.1, which is the one pair of answers you could not guess
2711        // from what the command is for.
2712        self.sets.clear();
2713        // The three `CLIENT` settings that are a choice about this connection go
2714        // back to their defaults, and the library name and version stay, since
2715        // the library behind the socket is the same library it was. Both halves
2716        // are `clearClientConnectionState`'s.
2717        self.reply = Reply::On;
2718        self.set_no_evict(false);
2719        self.set_no_touch(false);
2720        // Back on the default user, whatever it had authenticated as. The
2721        // password half of that is the caller's, because only it can see the
2722        // server and know whether there is one to ask for.
2723        self.forget_user();
2724    }
2725
2726    /// Record the name from `HELLO ... SETNAME` or `CLIENT SETNAME`.
2727    fn set_name(&mut self, name: &[u8]) {
2728        yo_alloc::allow(|| {
2729            self.name.clear();
2730            self.name.extend_from_slice(name);
2731        });
2732        self.sock.set_text(|text| &mut text.name, name);
2733    }
2734
2735    /// Record what `CLIENT SETINFO LIB-NAME` was told.
2736    fn set_lib_name(&mut self, value: &[u8]) {
2737        yo_alloc::allow(|| {
2738            self.lib_name.clear();
2739            self.lib_name.extend_from_slice(value);
2740        });
2741        self.sock.set_text(|text| &mut text.lib_name, value);
2742    }
2743
2744    /// Record what `CLIENT SETINFO LIB-VER` was told.
2745    fn set_lib_ver(&mut self, value: &[u8]) {
2746        yo_alloc::allow(|| {
2747            self.lib_ver.clear();
2748            self.lib_ver.extend_from_slice(value);
2749        });
2750        self.sock.set_text(|text| &mut text.lib_ver, value);
2751    }
2752
2753    /// Record `CLIENT NO-EVICT`.
2754    fn set_no_evict(&mut self, on: bool) {
2755        self.no_evict = on;
2756        self.sock.set_flag(clients::NO_EVICT, on);
2757    }
2758
2759    /// Record `CLIENT NO-TOUCH`.
2760    fn set_no_touch(&mut self, on: bool) {
2761        self.no_touch = on;
2762        self.sock.set_flag(clients::NO_TOUCH, on);
2763    }
2764}
2765
2766/// Give back everything a connection was holding on the server.
2767///
2768/// The transaction, the watches, the subscriptions and the monitor, and it is
2769/// here rather than in [`Session::reset`] because letting go of any of the four
2770/// is a change to the server. A `Session` on its own cannot reach one, and a
2771/// connection that dropped its lists without saying so would leave rows nobody
2772/// is watching, subscriptions nobody is listening to and a monitor nobody is
2773/// reading, which would keep every write, every publish and every command on the
2774/// server paying for clients that are not there.
2775pub fn forget_session(server: &Server, session: &mut Session) {
2776    multi::release(server, session);
2777    pubsub::release(server, session);
2778    if session.monitoring() {
2779        server.watch_no_more(session.row());
2780    }
2781    if session.replicating() {
2782        server.drop_replica(session.row().id);
2783    }
2784}
2785
2786/// Run one command and write its reply.
2787///
2788/// The name is looked up and the arity is checked here, once, so that no body
2789/// has to. Everything after that is the command's own.
2790pub fn execute(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
2791    // The decoder never produces a command with no name. If one ever arrives,
2792    // it is not something to answer.
2793    if args.is_empty() {
2794        return Flow::Continue;
2795    }
2796    let flow = resolved(server, session, lookup(args.name()), args, out);
2797    // The engine does this itself, after the reply has been decided, because it
2798    // is also what settles `CLIENT REPLY`. An embedded caller has no engine, so
2799    // it happens here instead, and the two paths never both run: the engine
2800    // reaches the funnel through `resolved` and not through this.
2801    //
2802    // Not for a command the pause held, because that command has not run and is
2803    // going to be run again. An embedded caller has nowhere to park it, so it
2804    // gets the answer back and decides for itself; a caller that has not paused
2805    // its own server, which is nearly all of them, never sees this.
2806    if flow != Flow::Hold {
2807        session.finished();
2808    }
2809    flow
2810}
2811
2812/// The commands that are a container for a set of subcommands.
2813///
2814/// A hand written list because the table has one row per container and none per
2815/// subcommand, so there is nothing to ask. It goes away with D-114, which gives
2816/// every subcommand a row of its own and makes this a flag on the container.
2817const CONTAINERS: [&str; 13] = [
2818    "acl", "backup", "client", "cluster", "command", "config", "function", "memory", "object",
2819    "pubsub", "script", "xgroup", "xinfo",
2820];
2821
2822/// The subcommand a container command was given, for the `cmd` field of
2823/// `CLIENT INFO`, which reads `client|info` and not `client`.
2824///
2825/// `None` for everything else, and for a container called with nothing after
2826/// it, which is a wrong arity and has no subcommand to name.
2827fn container_sub<'a>(spec: &Spec, args: &Args<'a>) -> Option<&'a [u8]> {
2828    (args.len() > 1 && CONTAINERS.contains(&spec.name)).then(|| args.get(1))
2829}
2830
2831/// The six commands Redis marks `may-replicate` and does not mark `write`.
2832///
2833/// A short list rather than a flag on every row, because six is what it is and
2834/// the only thing that asks is the pause gate below. It goes away with the flag
2835/// if anything else ever needs the same question answered.
2836const MAY_REPLICATE: [&str; 6] = ["eval", "evalsha", "fcall", "pfcount", "publish", "spublish"];
2837
2838/// Whether `CLIENT PAUSE WRITE` holds this command.
2839///
2840/// The writes, the six above, and `EXEC` when the transaction it is about to run
2841/// holds one of either. That last part is why this is asked of the session as
2842/// well as of the command: a transaction of nothing but reads runs through a
2843/// write pause, and one write anywhere in it makes the whole transaction wait.
2844fn may_replicate(spec: &Spec, session: &Session) -> bool {
2845    spec.flags.contains(&"write")
2846        || MAY_REPLICATE.contains(&spec.name)
2847        || (spec.name == "exec" && session.queued_writes())
2848}
2849
2850/// Whether a monitor is refused this command, which is anything that goes near
2851/// the keyspace.
2852///
2853/// The writes, the reads and the six above, which is Redis's list read out of
2854/// the same three questions in the same order. `EXEC` is not on it and does not
2855/// need to be: a monitor cannot have queued one of these, because the refusal is
2856/// in front of the queue.
2857fn touches_keyspace(spec: &Spec) -> bool {
2858    spec.flags.contains(&"write")
2859        || spec.flags.contains(&"readonly")
2860        || MAY_REPLICATE.contains(&spec.name)
2861}
2862
2863/// The same, for a caller that has already found the command.
2864///
2865/// The engine frames a command before it runs it, and between those two it also
2866/// asks which key the command touches so the record can be prefetched. That is
2867/// two more chances to look the name up, and looking it up three times to run it
2868/// once is three times the cost of the cheapest thing in the path. So the engine
2869/// resolves the name where it frames the command, carries the answer on the
2870/// framed command, and both the other two take it from there.
2871///
2872/// `spec` is `None` for a name that is not a command, which is the same thing
2873/// [`lookup`] says and lands in the same reply.
2874pub fn resolved(
2875    server: &Server,
2876    session: &mut Session,
2877    spec: Option<&'static Spec>,
2878    args: Args<'_>,
2879    out: &mut Out,
2880) -> Flow {
2881    if args.is_empty() {
2882        return Flow::Continue;
2883    }
2884    server.mine().stats.commands.bump();
2885
2886    // The four refusals below are the ones a real server makes in
2887    // `processCommand`, before the command's own body is reached, and they are
2888    // the ones that kill an open transaction. That is the whole of the rule: an
2889    // error raised here means `EXEC` will refuse to run anything, and an error
2890    // raised by a command body does not, which is why `MULTI` inside `MULTI`
2891    // complains and leaves the transaction alive.
2892    let Some(spec) = spec else {
2893        multi::refuse(server, session, None, &args::unknown_command(args), out);
2894        return Flow::Continue;
2895    };
2896    if !arity_ok(spec, args.len()) {
2897        server.mine().cmdstats.at(spec).rejected.bump();
2898        multi::refuse(
2899            server,
2900            session,
2901            Some(spec),
2902            &args::wrong_arity(spec.name),
2903            out,
2904        );
2905        return Flow::Continue;
2906    }
2907    // What this connection is doing, which only `CLIENT` reads back. Here and
2908    // not further down because a command that is about to be refused or queued
2909    // is still the last command the connection sent, which is what a real
2910    // server reports: it notes the name in `processCommand` before any of the
2911    // decisions below.
2912    let argv = (0..args.len()).map(|i| args.get(i).len() as u64).sum();
2913    session.ran(
2914        table::index_of(spec),
2915        container_sub(spec, &args),
2916        argv,
2917        server.now_ms(),
2918    );
2919
2920    // The password, and this is the whole of it on the command path: one
2921    // acquire load on a server nobody gave a password to. Here, after the two
2922    // refusals above and before everything below, which is where a real server
2923    // puts it, so a command with the wrong number of arguments is told that
2924    // rather than told to authenticate, and everything else is told to
2925    // authenticate before it is told anything at all.
2926    //
2927    // The commands carrying `no_auth` go through, which is `AUTH` itself and the
2928    // three that a client has to be able to send before it has a password
2929    // accepted: `HELLO`, which carries the option that authenticates, `RESET`,
2930    // which is how a client says it is starting over, and `QUIT`.
2931    if server.guarded()
2932        && !session.authenticated()
2933        && !session.serving_master()
2934        && !spec.flags.contains(&"no_auth")
2935    {
2936        server.mine().cmdstats.at(spec).rejected.bump();
2937        if spec.name == "exec" {
2938            multi::abort(server, session, auth::NOAUTH, out);
2939        } else {
2940            session.dirty_multi();
2941            out.error(auth::NOAUTH.as_bytes());
2942        }
2943        return Flow::Continue;
2944    }
2945
2946    if session.in_multi()
2947        && let Some(e) = multi::refused_in_multi(spec)
2948    {
2949        server.mine().cmdstats.at(spec).rejected.bump();
2950        multi::refuse(server, session, Some(spec), &e, out);
2951        return Flow::Continue;
2952    }
2953
2954    // The ACL, here and in this order because this is where a real server puts
2955    // it: after the refusal above and before the memory limit, so a user who may
2956    // not run a command is told that rather than told the server is full.
2957    //
2958    // One relaxed load on a server nobody has written an ACL for, which is every
2959    // server that only ever set `requirepass`, because setting a password leaves
2960    // the default user able to do everything and a user who can do everything
2961    // cannot be refused anything.
2962    if server.restricted()
2963        && !session.serving_master()
2964        && let Some(said) = acl::gate(server, session, spec, args, out)
2965    {
2966        server.mine().cmdstats.at(spec).rejected.bump();
2967        if spec.name == "exec" {
2968            multi::abort(server, session, &said, out);
2969        } else {
2970            session.dirty_multi();
2971            out.error(said.as_bytes());
2972        }
2973        return Flow::Continue;
2974    }
2975
2976    // Where this command's keys say it should run, which is the whole of
2977    // routing and is one field read on a server that is not a cluster node,
2978    // which is nearly every server there is. Here, after the access control list
2979    // and before the queue below, which is where a real server puts it: a user
2980    // who may not touch a key is told that rather than told to go somewhere
2981    // else, and a command queued inside a transaction is refused as it is queued
2982    // so the whole transaction comes back as an `EXECABORT`.
2983    //
2984    // A command the master sent goes through untouched. A replica applies
2985    // whatever its master wrote, including writes to slots the master owned and
2986    // it does not, and a replica that redirected its own master would be a
2987    // replica that stopped following.
2988    if server.cluster_enabled()
2989        && !session.serving_master()
2990        && let Some(said) = cluster::gate(
2991            server,
2992            session.db,
2993            session.asking || cluster::asks(spec),
2994            spec,
2995            args,
2996        )
2997    {
2998        server.mine().cmdstats.at(spec).rejected.bump();
2999        if spec.name == "exec" {
3000            multi::abort(server, session, said.message(), out);
3001        } else {
3002            session.dirty_multi();
3003            out.error(said.message().as_bytes());
3004        }
3005        return Flow::Continue;
3006    }
3007
3008    // The limit first, so a server with no `maxmemory`, which is the default and
3009    // is nearly all of them, pays one comparison against a field that is already
3010    // warm. Every command and not only the writes, because that is where Redis
3011    // puts it: making room is the server's job whatever the client asked for,
3012    // and the flag only decides who gets told no when there is no room to make.
3013    //
3014    // The flag is Redis's own `denyoom` and the list of commands carrying it is
3015    // Redis's list, so a command that only frees is let through with nothing
3016    // left, which is what lets a client dig itself out with `DEL`.
3017    if server.maxmemory() != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
3018        server.mine().cmdstats.at(spec).rejected.bump();
3019        session.dirty_multi();
3020        out.error_line(b"OOM ", OOM);
3021        return Flow::Continue;
3022    }
3023
3024    // A write from a client on a replica is refused, which is what
3025    // `replica-read-only` is and is on by default. Here, after the memory limit
3026    // and before the queue below, which is where a real server puts it, so a
3027    // write queued inside a transaction on a replica is refused as it is queued
3028    // and the whole transaction comes back as an `EXECABORT`.
3029    //
3030    // Two loads on a server that is nobody's replica, both of a bool that is
3031    // false, and the first of them is the one that is nearly always the answer.
3032    // The master's own stream goes through, which is the entire point: a replica
3033    // that refused its master's writes would be a replica of nothing.
3034    if server.read_only_replica() && !session.serving_master() && spec.flags.contains(&"write") {
3035        server.mine().cmdstats.at(spec).rejected.bump();
3036        if spec.name == "exec" {
3037            multi::abort(server, session, follow::READONLY, out);
3038        } else {
3039            session.dirty_multi();
3040            out.error(follow::READONLY.as_bytes());
3041        }
3042        return Flow::Continue;
3043    }
3044
3045    // A RESP2 connection that has subscribed to something may only send a
3046    // handful of commands, because RESP2 sends a published message as an
3047    // ordinary array and a client with a reply outstanding could not tell the
3048    // two apart. Here, after the refusals above and before the queue below,
3049    // which is where a real server puts it: `EXEC` sent while subscribed comes
3050    // back as an `EXECABORT` rather than as this error, and a command `EXEC`
3051    // hands over is not asked at all.
3052    if let Some(e) = pubsub::refused(session, spec, out) {
3053        server.mine().cmdstats.at(spec).rejected.bump();
3054        multi::refuse(server, session, Some(spec), &e, out);
3055        return Flow::Continue;
3056    }
3057
3058    // A monitor may not touch the keyspace. Redis flags one a replica and this
3059    // is the refusal a replica gets, which reads like an accident of the
3060    // implementation and is not one: a monitor is exempt from the pause below,
3061    // so a connection that could pause the server and then become a monitor
3062    // would have a way past its own pause that nothing else has.
3063    //
3064    // Here, in front of the pause and in front of the queue, which is where a
3065    // real server puts it. In front of the queue is what makes `MULTI`, `GET x`,
3066    // `EXEC` on a monitor come back as an `EXECABORT`: the `GET` is refused as
3067    // it is queued rather than as it runs.
3068    if session.monitoring() && touches_keyspace(spec) {
3069        server.mine().cmdstats.at(spec).rejected.bump();
3070        multi::refuse(server, session, Some(spec), &monitor::replica(), out);
3071        return Flow::Continue;
3072    }
3073
3074    // `CLIENT PAUSE`, and this is the whole of it on the command path: one
3075    // relaxed load on a server nobody has paused. Here, after every refusal
3076    // above and before the queue below, which is where a real server puts it. So
3077    // a command that would have been refused is still refused while the server
3078    // is paused, and `MULTI` on a paused server waits rather than opening a
3079    // transaction that would queue commands nobody is allowed to send yet.
3080    //
3081    // Nothing is exempt but a monitor, not even `CLIENT UNPAUSE`, which is
3082    // Redis's behaviour and is worth being clear about: a `CLIENT PAUSE 10000
3083    // ALL` cannot be called off, by anybody, until it runs out. The monitor is
3084    // exempt because a real server exempts its replicas and a monitor is flagged
3085    // one, and it costs nothing to let through because the gate above has
3086    // already refused it everything that reaches a key.
3087    // A command `EXEC` is replaying is not a command the client just sent, and a
3088    // real server runs those through `call` rather than through
3089    // `processCommand`, so the gate is not in front of them. Holding one would
3090    // mean a transaction that has written half of itself and stopped.
3091    if !session.running
3092        && !session.monitoring()
3093        && !session.serving_master()
3094        && let Some(all) = server.paused(server.now_ms())
3095        && (all || may_replicate(spec, session))
3096    {
3097        return Flow::Hold;
3098    }
3099
3100    // And the same thing for the moment a full resync is taking its image. A
3101    // write held here runs a moment later against a keyspace it has not missed
3102    // anything of, which is the whole reason it is held: the image and the
3103    // offset stamped with it have to be the two halves of one instant, and a
3104    // write that landed between them would be in both or in neither. Only the
3105    // writes, and never a command `EXEC` is replaying, for the same reasons the
3106    // pause above gives.
3107    if !session.running && server.frozen() && may_replicate(spec, session) {
3108        return Flow::Hold;
3109    }
3110
3111    // Held rather than run, and the reply is `QUEUED`. After the refusals above
3112    // and before everything below, which is where a real server puts it: a
3113    // command has to be a real command with the right number of arguments to be
3114    // queued at all, and nothing it would have done gets done now.
3115    if session.queues(spec.name) {
3116        return multi::queue(session, spec, args, out);
3117    }
3118
3119    // Which databases the maintenance turn after this batch has to ask. Marked
3120    // for every command and not only for the writes, because a read can make
3121    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
3122    // record it dropped is exactly the kind of thing the collector is for.
3123    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
3124    // two groups that hold them mark all of them rather than the session's.
3125    server.mine().mark(match spec.group {
3126        "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
3127        | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
3128            1u64 << session.db
3129        }
3130        _ => ALL_DATABASES,
3131    });
3132
3133    // Everybody watching, told about a command that is going to run. The load is
3134    // what this costs a server nobody is watching, which is nearly all of them.
3135    //
3136    // A script is reported before it runs and everything else after, because a
3137    // script's own calls come back through here and a reader wants the `EVAL`
3138    // in front of what it did. Every other command goes below, next to where a
3139    // real server feeds from, which is what puts `EXEC` after the commands it
3140    // replayed rather than in front of them.
3141    let watched = server.monitored() && !monitor::hidden(spec, args);
3142    if watched && monitor::SCRIPTS.contains(&spec.name) {
3143        monitor::feed(server, session, args);
3144    }
3145
3146    let mark = out.len();
3147    // Before the group, because the five that block are list commands and would
3148    // otherwise land in `lists`, which is handed one database and nothing that
3149    // could park a client. The flag is the right thing to branch on rather than
3150    // a list of names: it is what `COMMAND INFO` reports about exactly these
3151    // commands, and the sorted set and stream ones that arrive later carry it
3152    // too.
3153    // What the command is about to do to the keyspace, for anybody subscribed to
3154    // hear about it. Armed here and drained after the group, because the bodies
3155    // below are handed a database and their arguments and have no way to reach
3156    // the pub/sub registry from there. Off costs one thread local store.
3157    let armed = notify::arm(server, session.db);
3158    // And whether what it does has to reach a replica as well, which the bodies
3159    // ask about for the same reason and get an answer by the same route. That
3160    // arming is done by `notify::arm` above, since the two listeners hear about
3161    // an expired key through the same hook and only one of them can install it.
3162    // What is left here is whether the command as the client sent it would be a
3163    // fair thing to hand a replica, which is the write flag and nothing else: a
3164    // read sends only what its body pushed, which is normally nothing.
3165    let copying = server.replicated();
3166    let verbatim = spec.flags.contains(&"write");
3167    // Which of the keys this command reads are not there. A real server says
3168    // this from inside each lookup and this says all of them in front, which is
3169    // the same order for every command whose first act is to read what it was
3170    // given, and that is nearly all of them.
3171    misses::report(&server.dbs[session.db], session.db, spec, args);
3172    // And whether the lookups it is about to make are reads, for the two
3173    // counters in `INFO stats`. Armed after the walk above so that the walk's
3174    // own probes are not counted, and dropped after the body so that nothing the
3175    // dispatcher does afterwards is either.
3176    let reading = lookups::reading(misses::reading(spec, args));
3177    let done = if spec.flags.contains(&"blocking") {
3178        blocking::execute(server, session, spec, args, out)
3179    } else {
3180        match spec.group {
3181            "string" => {
3182                let db = session.db;
3183                strings::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3184            }
3185            // Its own group and its own file, and the same values underneath:
3186            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
3187            // something a `SET` left behind works.
3188            "bitmap" => {
3189                let db = session.db;
3190                bits::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3191            }
3192            // The same again: a sketch is a string with a documented layout, so
3193            // `GET` hands one to a client and `SET` takes it back.
3194            "hyperloglog" => {
3195                let db = session.db;
3196                hll::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3197            }
3198            "set" => {
3199                let db = session.db;
3200                sets::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3201            }
3202            // The one hash command whose state is not in the keyspace. A
3203            // fieldset belongs to the connection, so this is handed the session
3204            // as well as the database, the same exception `MIGRATE` gets in the
3205            // keyspace group for the socket it keeps.
3206            "hash" if spec.name == "himport" => {
3207                let db = session.db;
3208                himport::execute(&server.dbs[db], &mut session.sets, args, out)
3209                    .map(|()| Flow::Continue)
3210            }
3211            // The one group that reaches back into the server after it has
3212            // written its reply, because a hash is what a search index is
3213            // made of. What comes back is what the indexes have to be told,
3214            // which is not the same as whether the command was a write.
3215            "hash" => {
3216                let db = session.db;
3217                let changed = hashes::execute(&server.dbs[db], db, spec, args, out);
3218                changed.map(|changed| {
3219                    indexing::changed(server, db, args.get(1), changed);
3220                    Flow::Continue
3221                })
3222            }
3223            "list" => {
3224                let db = session.db;
3225                lists::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3226            }
3227            "zset" => {
3228                let db = session.db;
3229                zsets::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3230            }
3231            // A geo key is a sorted set and these are sorted set commands with
3232            // arithmetic on the way in and on the way out, so a client can ZREM
3233            // a place out of one and ZCARD it to count them.
3234            "geo" => {
3235                let db = session.db;
3236                geo::execute(&server.dbs[db], db, spec, args, out).map(|()| Flow::Continue)
3237            }
3238            "array" => {
3239                let db = session.db;
3240                arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3241            }
3242            "graph" => {
3243                let db = session.db;
3244                graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3245            }
3246            // A document under a key, reached by a path. The group is Redis's
3247            // module surface and the storage is ours, the same trade the vector
3248            // set group makes.
3249            "json" => {
3250                let db = session.db;
3251                json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3252            }
3253            "vector" => {
3254                let db = session.db;
3255                vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3256            }
3257            "bloom" => {
3258                let db = session.db;
3259                bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3260            }
3261            "cuckoo" => {
3262                let db = session.db;
3263                cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3264            }
3265            "cms" => {
3266                let db = session.db;
3267                cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3268            }
3269            "topk" => {
3270                let db = session.db;
3271                topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3272            }
3273            "tdigest" => {
3274                let db = session.db;
3275                tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3276            }
3277            "ts" => {
3278                let db = session.db;
3279                ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3280            }
3281            // The clock is read before the database is borrowed, because every
3282            // stream command needs the time and it lives on the server. An
3283            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
3284            // `XINFO` reporting it all have to agree about what moment this is.
3285            "stream" => {
3286                let db = session.db;
3287                let now = server.now_ms();
3288                streams::execute(&server.dbs[db], db, spec, args, now, out).map(|()| Flow::Continue)
3289            }
3290            // The one keyspace command that needs more than the databases,
3291            // because the socket it talks down is held on the server between
3292            // commands and not opened again for each one.
3293            "keyspace" if spec.name == "migrate" => {
3294                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
3295            }
3296            // Every database and not the one the session is on, because `COPY` takes
3297            // a `DB n` and writes into a database nobody selected. The other group
3298            // that reaches back into the server afterwards, and it hands back a list
3299            // rather than one answer, because `DEL a b c` is three keys and a rename
3300            // is two.
3301            // `RESTORE-ASKING` is `RESTORE` with an `ASKING` built into it and
3302            // runs the same body, but the reference files it under the server
3303            // group rather than the keyspace one, so it has to be named here to
3304            // reach the arm below.
3305            "keyspace" | "server" if spec.group == "keyspace" || spec.name == "restore-asking" => {
3306                let mut touched = indexing::Touched::new(server);
3307                let done =
3308                    keyspace::execute(&server.dbs, session.db, spec, args, out, &mut touched);
3309                done.map(|()| {
3310                    indexing::touched(server, &touched);
3311                    Flow::Continue
3312                })
3313            }
3314            // No database at all, because an index is not a key. The registry
3315            // is the whole of what these sixteen commands touch, and then
3316            // `FT.CREATE` hands back the name it made so the keys that
3317            // already match its prefix can be read into it. The lock goes
3318            // before the scan runs, since the scan takes it again for every
3319            // key it reads.
3320            "search" if spec.name == "FT.SEARCH" => {
3321                // The two search commands that read documents, and so the two
3322                // that need the keyspace as well as the registry. They take and
3323                // let go of the registry themselves, because they cannot hold
3324                // that and a stripe at the same time.
3325                search::find(server, session.db, args, out).map(|()| Flow::Continue)
3326            }
3327            "search" if spec.name == "FT.AGGREGATE" => {
3328                search::roll(server, session.db, args, out).map(|()| Flow::Continue)
3329            }
3330            "search" if spec.name == "FT.HYBRID" => {
3331                search::hybrid(server, session.db, args, out).map(|()| Flow::Continue)
3332            }
3333            "search" if spec.name == "FT.PROFILE" => {
3334                // Which is one of those two with the working shown, so it needs
3335                // everything they need and takes the same route to it.
3336                search::profiled(server, session.db, args, out).map(|()| Flow::Continue)
3337            }
3338            // The four search commands that name a key rather than an index.
3339            // A suggestion dictionary is a real key with a type of its own, so
3340            // these are handed a database and never touch the registry.
3341            "search" if spec.name.starts_with("FT.SUG") => {
3342                let db = session.db;
3343                suggest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
3344            }
3345            // The five deprecated document commands, which are the other search
3346            // commands that need the keyspace as well as the registry: what they
3347            // write and read is an ordinary hash.
3348            "search"
3349                if matches!(
3350                    spec.name,
3351                    "FT.ADD" | "FT.SAFEADD" | "FT.GET" | "FT.MGET" | "FT.DEL"
3352                ) =>
3353            {
3354                let db = session.db;
3355                search::docs::execute(server, db, spec, args, out).map(|()| Flow::Continue)
3356            }
3357            "search" if spec.name == "FT.CURSOR" => {
3358                // Its own arm because the cursors are not in the registry, and
3359                // it takes and lets go of the registry itself to look up the
3360                // index name it is given.
3361                search::cursor::execute(server, args, out).map(|()| Flow::Continue)
3362            }
3363            "search" => {
3364                let db = session.db;
3365                let made = search::execute(server, &mut server.search.lock(), db, spec, args, out);
3366                made.map(|made| {
3367                    match made {
3368                        Some(search::After::Scan(fill)) => indexing::scan(server, db, &fill),
3369                        Some(search::After::Sweep(keys)) => indexing::sweep(server, db, &keys),
3370                        None => {}
3371                    }
3372                    Flow::Continue
3373                })
3374            }
3375            "scripting" => {
3376                scripting::execute(server, session, spec, args, out).map(|()| Flow::Continue)
3377            }
3378            "transactions" => multi::execute(server, session, spec, args, out),
3379            // No database either, and the one group whose replies do not all go
3380            // to the connection that asked. The session is in it because a
3381            // subscription is connection state as well as server state.
3382            "pubsub" => pubsub::execute(server, session, spec, args, out),
3383            _ => server::execute(server, session, spec, args, out),
3384        }
3385    };
3386    drop(reading);
3387    // The other half of the feed. After the body, so that `SELECT 3` is reported
3388    // on the database it moved to, and before the reply is written, which is
3389    // where a real server has it.
3390    if watched && !monitor::SCRIPTS.contains(&spec.name) {
3391        monitor::feed(server, session, args);
3392    }
3393    // Before the error is written and not after, because a command that failed
3394    // half way through still changed whatever it changed before it failed and a
3395    // real server has already published those. Draining here also keeps the
3396    // notifications of a command run by `EXEC` in front of the next one's.
3397    // And back out the misses reported in front of a command that turned out to
3398    // have failed on its own arguments, since a server that fires from inside
3399    // the lookup never reached one.
3400    if let Err(e) = &done {
3401        misses::undo(spec, e);
3402    }
3403    notify::drain(server, armed);
3404
3405    // And copy it to the replicas. Only the writes, because a read changes
3406    // nothing there is anything to copy, and only the ones that got through,
3407    // because a command that was refused on its own arguments would be refused
3408    // there too and sending it would be asking a second server to make the same
3409    // mistake. `EVAL` and `EXEC` are not writes and are not sent: what they did
3410    // came through here one command at a time and each of those was sent on its
3411    // own, which is effect replication and is what a real server settled on for
3412    // the same reason.
3413    //
3414    // On the database the command ran on rather than the one the session is on
3415    // now, which are the same thing for everything but `SELECT`, and `SELECT` is
3416    // not a write.
3417    // Whatever went away on its own goes first, ahead of the command's own
3418    // effect and whether or not the command has one. A read that reaped a key on
3419    // the way past has a deletion to send and nothing else.
3420    repl::swept(server, session.db);
3421    if copying {
3422        if done.is_ok() {
3423            repl::feed(server, session.db, args, verbatim);
3424        } else {
3425            repl::forget();
3426        }
3427    }
3428
3429    let flow = match done {
3430        Ok(flow) => flow,
3431        Err(e) => {
3432            out.truncate(mark);
3433            write_error(out, &e);
3434            Flow::Continue
3435        }
3436    };
3437
3438    // After the command rather than before, so that whether each key it named is
3439    // there is read at the moment a real server would have signalled the change.
3440    // The load is what this costs a server nobody has sent `WATCH` to, and the
3441    // flag is Redis's own, so a command that only reads is never asked.
3442    if server.watching() && spec.flags.contains(&"write") {
3443        multi::touched(server, session, spec, args);
3444    }
3445
3446    // Counted here and not before the call, which is where Redis counts it, so
3447    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
3448    // same way theirs does.
3449    //
3450    // Failure is read off the reply rather than off the `Result`, because the
3451    // two are not the same set. A command that ran out of arguments comes back
3452    // as an `Err` and a command that was sent the wrong password writes its own
3453    // error line and comes back `Ok`, and both of those are a call that failed.
3454    // The first byte at the mark is what a client would branch on, and it is `-`
3455    // for an error on either protocol and `!` for RESP3's long form.
3456    let row = server.mine().cmdstats.at(spec);
3457    row.calls.bump();
3458    if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
3459        row.failed.bump();
3460    }
3461    // Last of all, because the reply the client is being hung up on still has to
3462    // be written first. A command that asked for this has decided the connection
3463    // is not one it wants to keep talking to, which so far is only a client
3464    // caught reaching for the slot migration protocol.
3465    if session.hanging_up() {
3466        return Flow::Close;
3467    }
3468    flow
3469}
3470
3471/// The error line for an error value.
3472///
3473/// The prefix is what a client branches on, and there are three of them:
3474/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
3475/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
3476/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
3477/// than routed through here. `OOM` is not a [`Code`] of its own because
3478/// [`Code::Full`] already covers the string that is too long for
3479/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
3480fn write_error(out: &mut Out, e: &Error) {
3481    let prefix: &[u8] = match e.code() {
3482        Code::WrongType => b"WRONGTYPE ",
3483        // Only the HyperLogLog commands answer this one, and the prefix is the
3484        // sentence a client branches on to tell a sketch it cannot read from a
3485        // sketch it sent wrong.
3486        Code::Corrupt => b"INVALIDOBJ ",
3487        _ => b"ERR ",
3488    };
3489    out.error_line(prefix, e.message().as_bytes());
3490}
3491
3492#[cfg(test)]
3493mod tests {
3494    use super::*;
3495    use crate::proto::{Limits, Proto};
3496    use crate::request::Argv;
3497
3498    /// Build the wire bytes for a command.
3499    ///
3500    /// Tests go through the codec rather than around it, so an argument in a
3501    /// test is the same borrowed slice a connection produces.
3502    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
3503        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
3504        for p in parts {
3505            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
3506            wire.extend_from_slice(p);
3507            wire.extend_from_slice(b"\r\n");
3508        }
3509        wire
3510    }
3511
3512    /// A server, a connection and a buffer, driven the way the reactor will.
3513    struct Fixture {
3514        server: Server,
3515        session: Session,
3516        argv: Argv,
3517        out: Out,
3518        /// How far into the replication stream [`Fixture::crossed`] has read.
3519        mark: u64,
3520    }
3521
3522    /// The number out of an integer reply, for a test that compares two of them
3523    /// rather than checking one against a constant.
3524    fn int_of(reply: &str) -> i64 {
3525        reply
3526            .strip_prefix(':')
3527            .and_then(|s| s.strip_suffix("\r\n"))
3528            .unwrap_or_else(|| panic!("not an integer reply: {reply:?}"))
3529            .parse()
3530            .expect("an integer reply holds an integer")
3531    }
3532
3533    impl Fixture {
3534        fn new() -> Fixture {
3535            Fixture::on(Server::new())
3536        }
3537
3538        /// The same, on a server whose databases are cut into `width` stripes.
3539        fn striped(width: usize) -> Fixture {
3540            Fixture::on(Server::with_width(width))
3541        }
3542
3543        fn on(server: Server) -> Fixture {
3544            Fixture {
3545                server,
3546                session: Session::new(7),
3547                argv: Argv::new(),
3548                out: Out::new(Proto::Resp2),
3549                mark: 0,
3550            }
3551        }
3552
3553        /// The same, on a server that believes it has a replica.
3554        ///
3555        /// Nothing is attached to it. What the tests below read is the
3556        /// replication stream itself, which is written whether or not there is
3557        /// anybody to send it to, so a server told this is a master in every
3558        /// way that these tests can see.
3559        fn replicated() -> Fixture {
3560            let f = Fixture::new();
3561            f.server.pretend_replica();
3562            f
3563        }
3564
3565        /// Run one command and answer with what crossed to a replica.
3566        ///
3567        /// Only what this command added, so a test reads one line rather than
3568        /// the whole history, and the `SELECT` the stream opens with is part of
3569        /// the first answer for the same reason it is part of the stream.
3570        fn crossed(&mut self, parts: &[&[u8]]) -> String {
3571            self.run(parts);
3572            let (text, upto) = self.server.stream_since(self.mark);
3573            self.mark = upto;
3574            text
3575        }
3576
3577        /// Run one command and answer with the bytes it wrote.
3578        fn run(&mut self, parts: &[&[u8]]) -> String {
3579            self.flow(parts).1
3580        }
3581
3582        /// Run one command and answer with the bytes exactly as written.
3583        ///
3584        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
3585        /// every reply that is text and destroys a `DUMP` payload, since a
3586        /// payload is arbitrary bytes and a checksum on the end of them.
3587        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
3588            let wire = encode(parts);
3589            self.argv.decode(&wire, &Limits::default()).unwrap();
3590            self.out.clear();
3591            execute(
3592                &self.server,
3593                &mut self.session,
3594                Args::new(&self.argv, &wire),
3595                &mut self.out,
3596            );
3597            self.out.as_slice().to_vec()
3598        }
3599
3600        /// Move every clock in the server on by `ms`.
3601        fn advance(&mut self, ms: u64) {
3602            self.server.advance_clock_ms(ms);
3603        }
3604
3605        /// Run one command as a second connection to the same server.
3606        ///
3607        /// What `WATCH` is for is a write another connection made, and a test
3608        /// that only has one connection cannot tell the two apart.
3609        fn other(&mut self, parts: &[&[u8]]) -> String {
3610            self.other_in(self.session.db(), parts)
3611        }
3612
3613        /// The same, on a database of its own.
3614        fn other_in(&mut self, db: usize, parts: &[&[u8]]) -> String {
3615            let mut session = Session::new(8);
3616            session.db = db;
3617            let reply = self.by(&mut session, parts);
3618            forget_session(&self.server, &mut session);
3619            reply
3620        }
3621
3622        /// Run one command on a session the caller holds.
3623        fn by(&mut self, session: &mut Session, parts: &[&[u8]]) -> String {
3624            let wire = encode(parts);
3625            let mut argv = Argv::new();
3626            argv.decode(&wire, &Limits::default()).unwrap();
3627            let mut out = Out::new(Proto::Resp2);
3628            execute(&self.server, session, Args::new(&argv, &wire), &mut out);
3629            String::from_utf8_lossy(out.as_slice()).into_owned()
3630        }
3631
3632        /// The same, with what the connection should do next.
3633        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
3634            let wire = encode(parts);
3635            self.argv.decode(&wire, &Limits::default()).unwrap();
3636            self.out.clear();
3637            let flow = execute(
3638                &self.server,
3639                &mut self.session,
3640                Args::new(&self.argv, &wire),
3641                &mut self.out,
3642            );
3643            (
3644                flow,
3645                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
3646            )
3647        }
3648    }
3649
3650    #[test]
3651    fn multi_holds_commands_and_exec_runs_them() {
3652        let mut f = Fixture::new();
3653        assert_eq!(f.run(&[b"MULTI"]), "+OK\r\n");
3654        assert_eq!(f.run(&[b"SET", b"k", b"1"]), "+QUEUED\r\n");
3655        assert_eq!(f.run(&[b"INCR", b"k"]), "+QUEUED\r\n");
3656        // Nothing ran while it was being queued.
3657        assert_eq!(f.other(&[b"GET", b"k"]), "$-1\r\n");
3658        assert_eq!(f.run(&[b"EXEC"]), "*2\r\n+OK\r\n:2\r\n");
3659        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n2\r\n");
3660    }
3661
3662    /// The test the `high_water` claim in `multi::exec` asks for.
3663    ///
3664    /// A `Vec` reaches the allocator exactly when its capacity changes, so a
3665    /// replay buffer whose room is the same before and after is one that did
3666    /// not allocate. The first transaction is what sets the room, which is the
3667    /// high water mark, and the second is the one that has to be free. Before
3668    /// the buffer moved onto the session this failed on every transaction,
3669    /// because `exec` made a new one each time and the room went back to zero.
3670    #[test]
3671    fn the_second_exec_of_a_shape_does_not_grow_the_buffer() {
3672        let mut f = Fixture::new();
3673        for _ in 0..2 {
3674            f.run(&[b"MULTI"]);
3675            f.run(&[b"SET", b"k", b"1"]);
3676            f.run(&[b"INCR", b"k"]);
3677            f.run(&[b"EXEC"]);
3678        }
3679        let room = f.session.replay.room();
3680        assert!(room > 0, "the first transaction should have set the room");
3681        f.run(&[b"MULTI"]);
3682        f.run(&[b"SET", b"k", b"1"]);
3683        f.run(&[b"INCR", b"k"]);
3684        f.run(&[b"EXEC"]);
3685        assert_eq!(f.session.replay.room(), room);
3686    }
3687
3688    #[test]
3689    fn an_empty_transaction_answers_an_empty_array() {
3690        let mut f = Fixture::new();
3691        f.run(&[b"MULTI"]);
3692        assert_eq!(f.run(&[b"EXEC"]), "*0\r\n");
3693    }
3694
3695    #[test]
3696    fn exec_and_discard_want_a_transaction_to_be_open() {
3697        let mut f = Fixture::new();
3698        assert_eq!(f.run(&[b"EXEC"]), "-ERR EXEC without MULTI\r\n");
3699        assert_eq!(f.run(&[b"DISCARD"]), "-ERR DISCARD without MULTI\r\n");
3700        // And `UNWATCH` does not, which is the one of the three that is happy
3701        // being sent for no reason.
3702        assert_eq!(f.run(&[b"UNWATCH"]), "+OK\r\n");
3703    }
3704
3705    #[test]
3706    fn an_error_a_command_body_raises_leaves_the_transaction_alive() {
3707        let mut f = Fixture::new();
3708        f.run(&[b"MULTI"]);
3709        assert_eq!(
3710            f.run(&[b"MULTI"]),
3711            "-ERR MULTI calls can not be nested\r\n",
3712            "nested MULTI is raised by the command and not by the funnel"
3713        );
3714        assert_eq!(
3715            f.run(&[b"WATCH", b"k"]),
3716            "-ERR WATCH inside MULTI is not allowed\r\n"
3717        );
3718        f.run(&[b"SET", b"k", b"1"]);
3719        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n+OK\r\n");
3720    }
3721
3722    #[test]
3723    fn an_error_the_funnel_raises_kills_the_transaction() {
3724        for bad in [
3725            &[b"NOSUCHCOMMAND".as_slice()] as &[&[u8]],
3726            &[b"GET".as_slice()],
3727        ] {
3728            let mut f = Fixture::new();
3729            f.run(&[b"MULTI"]);
3730            assert!(f.run(bad).starts_with("-ERR "));
3731            assert_eq!(
3732                f.run(&[b"SET", b"k", b"1"]),
3733                "+QUEUED\r\n",
3734                "a dead transaction still answers QUEUED, which is Redis"
3735            );
3736            assert_eq!(
3737                f.run(&[b"EXEC"]),
3738                "-EXECABORT Transaction discarded because of previous errors.\r\n"
3739            );
3740            assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3741        }
3742    }
3743
3744    #[test]
3745    fn exec_with_an_argument_is_an_abort_and_not_an_arity_error() {
3746        let mut f = Fixture::new();
3747        f.run(&[b"MULTI"]);
3748        f.run(&[b"SET", b"k", b"1"]);
3749        assert_eq!(
3750            f.run(&[b"EXEC", b"x"]),
3751            "-EXECABORT Transaction discarded because of: wrong number of arguments for 'exec' command\r\n"
3752        );
3753        assert_eq!(f.run(&[b"EXEC"]), "-ERR EXEC without MULTI\r\n");
3754        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3755    }
3756
3757    #[test]
3758    fn a_command_a_transaction_may_not_hold_kills_it() {
3759        let mut f = Fixture::new();
3760        f.run(&[b"MULTI"]);
3761        assert_eq!(
3762            f.run(&[b"SHUTDOWN", b"NOSAVE"]),
3763            "-ERR Command not allowed inside a transaction\r\n"
3764        );
3765        assert_eq!(
3766            f.run(&[b"EXEC"]),
3767            "-EXECABORT Transaction discarded because of previous errors.\r\n"
3768        );
3769    }
3770
3771    #[test]
3772    fn a_failing_command_inside_exec_is_an_element_and_the_rest_still_runs() {
3773        let mut f = Fixture::new();
3774        f.run(&[b"RPUSH", b"l", b"v"]);
3775        f.run(&[b"MULTI"]);
3776        f.run(&[b"INCR", b"l"]);
3777        f.run(&[b"SET", b"y", b"2"]);
3778        assert_eq!(
3779            f.run(&[b"EXEC"]),
3780            "*2\r\n-WRONGTYPE Operation against a key holding the wrong kind of value\r\n+OK\r\n"
3781        );
3782        assert_eq!(f.run(&[b"GET", b"y"]), "$1\r\n2\r\n");
3783    }
3784
3785    #[test]
3786    fn discard_and_reset_both_throw_the_queue_away() {
3787        let mut f = Fixture::new();
3788        f.run(&[b"MULTI"]);
3789        f.run(&[b"SET", b"k", b"1"]);
3790        assert_eq!(f.run(&[b"DISCARD"]), "+OK\r\n");
3791        assert_eq!(f.run(&[b"EXEC"]), "-ERR EXEC without MULTI\r\n");
3792        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3793
3794        f.run(&[b"MULTI"]);
3795        f.run(&[b"SET", b"k", b"1"]);
3796        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
3797        assert_eq!(f.run(&[b"EXEC"]), "-ERR EXEC without MULTI\r\n");
3798        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3799    }
3800
3801    #[test]
3802    fn select_is_queued_and_applied_when_exec_runs_it() {
3803        let mut f = Fixture::new();
3804        f.run(&[b"MULTI"]);
3805        assert_eq!(f.run(&[b"SELECT", b"3"]), "+QUEUED\r\n");
3806        f.run(&[b"SET", b"k", b"1"]);
3807        assert_eq!(f.run(&[b"EXEC"]), "*2\r\n+OK\r\n+OK\r\n");
3808        assert_eq!(f.session.db(), 3, "the SELECT applied and stayed applied");
3809        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n1\r\n");
3810    }
3811
3812    #[test]
3813    fn a_write_by_another_connection_fails_the_transaction() {
3814        let mut f = Fixture::new();
3815        f.run(&[b"SET", b"k", b"1"]);
3816        assert_eq!(f.run(&[b"WATCH", b"k"]), "+OK\r\n");
3817        f.other(&[b"SET", b"k", b"2"]);
3818        f.run(&[b"MULTI"]);
3819        f.run(&[b"GET", b"k"]);
3820        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
3821    }
3822
3823    #[test]
3824    fn a_write_that_puts_the_same_value_back_still_fails_it() {
3825        let mut f = Fixture::new();
3826        f.run(&[b"SET", b"k", b"1"]);
3827        f.run(&[b"WATCH", b"k"]);
3828        f.other(&[b"SET", b"k", b"1"]);
3829        f.run(&[b"MULTI"]);
3830        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
3831    }
3832
3833    #[test]
3834    fn a_read_by_another_connection_does_not() {
3835        let mut f = Fixture::new();
3836        f.run(&[b"SET", b"k", b"1"]);
3837        f.run(&[b"WATCH", b"k"]);
3838        f.other(&[b"GET", b"k"]);
3839        f.other(&[b"STRLEN", b"k"]);
3840        f.run(&[b"MULTI"]);
3841        f.run(&[b"GET", b"k"]);
3842        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n$1\r\n1\r\n");
3843    }
3844
3845    #[test]
3846    fn deleting_a_key_that_was_never_there_does_not_fail_a_watch_on_it() {
3847        let mut f = Fixture::new();
3848        f.run(&[b"WATCH", b"k"]);
3849        f.other(&[b"DEL", b"k"]);
3850        f.run(&[b"MULTI"]);
3851        f.run(&[b"PING"]);
3852        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n+PONG\r\n");
3853        // And creating it does, which is the other half of the same rule.
3854        f.run(&[b"WATCH", b"k"]);
3855        f.other(&[b"SET", b"k", b"1"]);
3856        f.run(&[b"MULTI"]);
3857        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
3858    }
3859
3860    #[test]
3861    fn a_watched_key_that_expires_fails_the_transaction() {
3862        let mut f = Fixture::new();
3863        f.run(&[b"SET", b"k", b"1", b"PX", b"50"]);
3864        f.run(&[b"WATCH", b"k"]);
3865        f.run(&[b"MULTI"]);
3866        f.advance(100);
3867        assert_eq!(
3868            f.run(&[b"EXEC"]),
3869            "*-1\r\n",
3870            "nothing wrote to the key, so only the liveness check can catch this"
3871        );
3872    }
3873
3874    #[test]
3875    fn every_way_a_transaction_ends_lets_go_of_the_watches() {
3876        for end in [
3877            &[b"EXEC".as_slice()] as &[&[u8]],
3878            &[b"DISCARD".as_slice()],
3879            &[b"UNWATCH".as_slice()],
3880            &[b"RESET".as_slice()],
3881        ] {
3882            let mut f = Fixture::new();
3883            f.run(&[b"SET", b"k", b"1"]);
3884            f.run(&[b"WATCH", b"k"]);
3885            if end[0] != b"UNWATCH" && end[0] != b"RESET" {
3886                f.run(&[b"MULTI"]);
3887            }
3888            f.run(end);
3889            assert!(!f.server.watching(), "{end:?} left a row behind");
3890            // And the connection can start again with nothing carried over.
3891            f.other(&[b"SET", b"k", b"2"]);
3892            f.run(&[b"MULTI"]);
3893            f.run(&[b"GET", b"k"]);
3894            assert_eq!(f.run(&[b"EXEC"]), "*1\r\n$1\r\n2\r\n");
3895        }
3896    }
3897
3898    #[test]
3899    fn a_connection_going_away_lets_go_of_its_watches() {
3900        let mut f = Fixture::new();
3901        f.run(&[b"SET", b"k", b"1"]);
3902        f.run(&[b"WATCH", b"k"]);
3903        assert!(f.server.watching());
3904        forget_session(&f.server, &mut f.session);
3905        assert!(!f.server.watching());
3906    }
3907
3908    #[test]
3909    fn watching_the_same_key_twice_is_one_watch() {
3910        let mut f = Fixture::new();
3911        f.run(&[b"SET", b"k", b"1"]);
3912        f.run(&[b"WATCH", b"k", b"k"]);
3913        f.run(&[b"UNWATCH"]);
3914        assert!(
3915            !f.server.watching(),
3916            "the row counts watchers, so a doubled watch would leave one behind"
3917        );
3918    }
3919
3920    #[test]
3921    fn two_connections_can_watch_the_same_key() {
3922        let mut f = Fixture::new();
3923        f.run(&[b"SET", b"k", b"1"]);
3924        f.run(&[b"WATCH", b"k"]);
3925        let mut second = Session::new(9);
3926        second.db = f.session.db();
3927        assert_eq!(f.by(&mut second, &[b"WATCH", b"k"]), "+OK\r\n");
3928        // One lets go and the other's watch still works.
3929        forget_session(&f.server, &mut second);
3930        assert!(f.server.watching());
3931        f.other(&[b"SET", b"k", b"2"]);
3932        f.run(&[b"MULTI"]);
3933        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
3934    }
3935
3936    #[test]
3937    fn flushdb_fails_a_watch_on_a_key_that_was_there() {
3938        let mut f = Fixture::new();
3939        f.run(&[b"SET", b"k", b"1"]);
3940        f.run(&[b"WATCH", b"k"]);
3941        f.other(&[b"FLUSHDB"]);
3942        f.run(&[b"MULTI"]);
3943        assert_eq!(f.run(&[b"EXEC"]), "*-1\r\n");
3944    }
3945
3946    #[test]
3947    fn flushdb_does_not_fail_a_watch_on_a_key_that_was_not() {
3948        let mut f = Fixture::new();
3949        f.run(&[b"WATCH", b"k"]);
3950        f.other(&[b"FLUSHDB"]);
3951        f.run(&[b"MULTI"]);
3952        f.run(&[b"PING"]);
3953        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n+PONG\r\n");
3954    }
3955
3956    #[test]
3957    fn a_watch_is_on_a_database_and_a_key_and_not_on_a_key() {
3958        let mut f = Fixture::new();
3959        f.run(&[b"SET", b"k", b"1"]);
3960        f.run(&[b"WATCH", b"k"]);
3961        // The same name in another database is another key.
3962        let elsewhere = f.session.db() + 1;
3963        f.other_in(elsewhere, &[b"SET", b"k", b"9"]);
3964        f.run(&[b"MULTI"]);
3965        f.run(&[b"GET", b"k"]);
3966        assert_eq!(f.run(&[b"EXEC"]), "*1\r\n$1\r\n1\r\n");
3967    }
3968
3969    #[test]
3970    fn a_write_that_reaches_a_key_it_did_not_name_still_fails_a_watch() {
3971        let mut f = Fixture::new();
3972        f.run(&[b"RPUSH", b"src", b"1"]);
3973        f.run(&[b"WATCH", b"dst"]);
3974        f.other(&[b"SORT", b"src", b"STORE", b"dst"]);
3975        f.run(&[b"MULTI"]);
3976        assert_eq!(
3977            f.run(&[b"EXEC"]),
3978            "*-1\r\n",
3979            "SORT is movablekeys, so every watched key in the database is asked"
3980        );
3981    }
3982
3983    #[test]
3984    fn a_server_nobody_is_watching_says_so() {
3985        let mut f = Fixture::new();
3986        assert!(!f.server.watching());
3987        f.run(&[b"SET", b"k", b"1"]);
3988        assert!(!f.server.watching());
3989    }
3990
3991    /// The count on the end of a subscribe reply is channels and patterns
3992    /// together, which is a thing a client uses to know when it is out of
3993    /// subscribe mode and so has to be the number the mode is decided on.
3994    /// Shard channels are counted on their own because they are their own
3995    /// namespace.
3996    #[test]
3997    fn the_count_a_subscribe_answers_covers_channels_and_patterns() {
3998        let mut f = Fixture::new();
3999        assert_eq!(
4000            f.run(&[b"SUBSCRIBE", b"a", b"b"]),
4001            "*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"
4002        );
4003        assert_eq!(
4004            f.run(&[b"PSUBSCRIBE", b"c*"]),
4005            "*3\r\n$10\r\npsubscribe\r\n$2\r\nc*\r\n:3\r\n"
4006        );
4007        assert_eq!(
4008            f.run(&[b"SSUBSCRIBE", b"s"]),
4009            "*3\r\n$10\r\nssubscribe\r\n$1\r\ns\r\n:1\r\n"
4010        );
4011        // Subscribing again to something already held answers again with the
4012        // count unchanged, rather than counting it twice or saying nothing.
4013        assert_eq!(
4014            f.run(&[b"SUBSCRIBE", b"a"]),
4015            "*3\r\n$9\r\nsubscribe\r\n$1\r\na\r\n:3\r\n"
4016        );
4017    }
4018
4019    /// Unsubscribe has three shapes and a client has to be able to tell them
4020    /// apart, because the last one is what tells it the mode is over.
4021    #[test]
4022    fn unsubscribe_answers_for_names_it_was_not_holding_too() {
4023        let mut f = Fixture::new();
4024        f.run(&[b"SUBSCRIBE", b"a"]);
4025
4026        // A name that was never subscribed still gets a reply, with the count
4027        // as it stands.
4028        assert_eq!(
4029            f.run(&[b"UNSUBSCRIBE", b"zz"]),
4030            "*3\r\n$11\r\nunsubscribe\r\n$2\r\nzz\r\n:1\r\n"
4031        );
4032        // With no names, one reply per channel held, counting down.
4033        f.run(&[b"SUBSCRIBE", b"b"]);
4034        f.run(&[b"PSUBSCRIBE", b"p*"]);
4035        assert_eq!(
4036            f.run(&[b"UNSUBSCRIBE"]),
4037            "*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"
4038        );
4039        // With no names and none of that family held, one reply with a nil
4040        // where the name goes and the count that is left.
4041        assert_eq!(
4042            f.run(&[b"UNSUBSCRIBE"]),
4043            "*3\r\n$11\r\nunsubscribe\r\n$-1\r\n:1\r\n",
4044            "the pattern is still held, so the count is one"
4045        );
4046        assert_eq!(
4047            f.run(&[b"SUNSUBSCRIBE"]),
4048            "*3\r\n$12\r\nsunsubscribe\r\n$-1\r\n:0\r\n",
4049            "shard channels are counted on their own"
4050        );
4051    }
4052
4053    /// The gate is on the funnel and the funnel is what `EXEC` goes through
4054    /// for the commands it queued, so it has to know it is running one.
4055    /// Redis lets a queued command through, and a transaction that subscribes
4056    /// and then reads is the case that says which way round it is.
4057    #[test]
4058    fn the_subscribe_gate_does_not_reach_inside_exec() {
4059        let mut f = Fixture::new();
4060        f.run(&[b"SET", b"k", b"1"]);
4061        f.run(&[b"MULTI"]);
4062        assert_eq!(f.run(&[b"SUBSCRIBE", b"z"]), "+QUEUED\r\n");
4063        assert_eq!(f.run(&[b"GET", b"k"]), "+QUEUED\r\n");
4064        assert_eq!(
4065            f.run(&[b"EXEC"]),
4066            "*2\r\n*3\r\n$9\r\nsubscribe\r\n$1\r\nz\r\n:1\r\n$1\r\n1\r\n"
4067        );
4068        // And once EXEC is done the connection really is subscribed, so the
4069        // gate is back on.
4070        assert_eq!(
4071            f.run(&[b"GET", b"k"]),
4072            "-ERR Can't execute 'get': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context\r\n"
4073        );
4074    }
4075
4076    /// `EXEC` sent by a subscribed RESP2 client is refused by the gate like
4077    /// anything else, and a refusal on the funnel kills the transaction.
4078    #[test]
4079    fn exec_sent_by_a_subscriber_aborts_the_transaction() {
4080        let mut f = Fixture::new();
4081        f.run(&[b"MULTI"]);
4082        f.run(&[b"SET", b"k", b"1"]);
4083        f.run(&[b"SUBSCRIBE", b"z"]);
4084        f.run(&[b"EXEC"]);
4085        f.run(&[b"MULTI"]);
4086        assert_eq!(
4087            f.run(&[b"EXEC"]),
4088            "-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"
4089        );
4090    }
4091
4092    /// `RESET` is one of the few things a subscriber may send, and what it
4093    /// resets includes every subscription it is holding.
4094    #[test]
4095    fn reset_lets_go_of_every_subscription() {
4096        let mut f = Fixture::new();
4097        f.run(&[b"SUBSCRIBE", b"a"]);
4098        f.run(&[b"PSUBSCRIBE", b"p*"]);
4099        f.run(&[b"SSUBSCRIBE", b"s"]);
4100        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
4101        assert_eq!(f.run(&[b"PUBSUB", b"NUMPAT"]), ":0\r\n");
4102        assert_eq!(f.run(&[b"PUBSUB", b"CHANNELS"]), "*0\r\n");
4103        assert_eq!(f.run(&[b"PUBSUB", b"SHARDCHANNELS"]), "*0\r\n");
4104        // And the connection takes ordinary commands again.
4105        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
4106    }
4107
4108    /// What `PUBSUB` can be asked, on a server with one subscriber holding one
4109    /// of each.
4110    #[test]
4111    fn pubsub_reports_channels_patterns_and_shard_channels_apart() {
4112        let mut f = Fixture::new();
4113        let mut sub = Session::new(9);
4114        f.by(&mut sub, &[b"SUBSCRIBE", b"a"]);
4115        f.by(&mut sub, &[b"PSUBSCRIBE", b"a*"]);
4116        f.by(&mut sub, &[b"SSUBSCRIBE", b"a"]);
4117
4118        assert_eq!(f.run(&[b"PUBSUB", b"CHANNELS"]), "*1\r\n$1\r\na\r\n");
4119        assert_eq!(f.run(&[b"PUBSUB", b"CHANNELS", b"b*"]), "*0\r\n");
4120        assert_eq!(f.run(&[b"PUBSUB", b"SHARDCHANNELS"]), "*1\r\n$1\r\na\r\n");
4121        assert_eq!(f.run(&[b"PUBSUB", b"NUMPAT"]), ":1\r\n");
4122        assert_eq!(
4123            f.run(&[b"PUBSUB", b"NUMSUB", b"a", b"zz"]),
4124            "*4\r\n$1\r\na\r\n:1\r\n$2\r\nzz\r\n:0\r\n"
4125        );
4126        assert_eq!(
4127            f.run(&[b"PUBSUB", b"SHARDNUMSUB", b"a"]),
4128            "*2\r\n$1\r\na\r\n:1\r\n",
4129            "the shard channel and the channel share a name and not a count"
4130        );
4131        assert_eq!(f.run(&[b"PUBSUB", b"NUMSUB"]), "*0\r\n");
4132
4133        forget_session(&f.server, &mut sub);
4134        assert_eq!(f.run(&[b"PUBSUB", b"NUMPAT"]), ":0\r\n");
4135        assert_eq!(f.run(&[b"PUBSUB", b"CHANNELS"]), "*0\r\n");
4136    }
4137
4138    /// The one setting whose value is neither a number nor a word, and whose
4139    /// spelling on the way out is not the spelling on the way in.
4140    #[test]
4141    fn the_notification_setting_reads_back_in_the_servers_own_spelling() {
4142        let mut f = Fixture::new();
4143        assert_eq!(
4144            f.run(&[b"CONFIG", b"GET", b"notify-keyspace-events"]),
4145            "*2\r\n$22\r\nnotify-keyspace-events\r\n$0\r\n\r\n"
4146        );
4147        assert_eq!(
4148            f.run(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"KEA"]),
4149            "+OK\r\n"
4150        );
4151        // `A` is a class of its own on the way in and stays one on the way out,
4152        // and the two channel letters move to the end.
4153        assert_eq!(
4154            f.run(&[b"CONFIG", b"GET", b"notify-keyspace-events"]),
4155            "*2\r\n$22\r\nnotify-keyspace-events\r\n$3\r\nAKE\r\n"
4156        );
4157        assert_eq!(
4158            f.run(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"Kg"]),
4159            "+OK\r\n"
4160        );
4161        assert_eq!(
4162            f.run(&[b"CONFIG", b"GET", b"notify-keyspace-events"]),
4163            "*2\r\n$22\r\nnotify-keyspace-events\r\n$2\r\ngK\r\n"
4164        );
4165    }
4166
4167    #[test]
4168    fn a_letter_the_notification_setting_does_not_know_is_refused() {
4169        let mut f = Fixture::new();
4170        assert_eq!(
4171            f.run(&[b"CONFIG", b"SET", b"notify-keyspace-events", b"KEQ"]),
4172            "-ERR CONFIG SET failed (possibly related to argument 'notify-keyspace-events') \
4173             - Invalid event class character. Use 'Ag$lshzxeKEtmdnocaSTIV'.\r\n"
4174        );
4175        // And nothing was applied, since the whole setting is parsed before any
4176        // of it is stored.
4177        assert_eq!(
4178            f.run(&[b"CONFIG", b"GET", b"notify-keyspace-events"]),
4179            "*2\r\n$22\r\nnotify-keyspace-events\r\n$0\r\n\r\n"
4180        );
4181    }
4182
4183    /// One mistake in a `PUBSUB` subcommand has two error shapes depending on
4184    /// which subcommand it is, because the ones with a fixed argument count are
4185    /// checked by the subcommand table and the ones without fall through to
4186    /// the generic syntax error. Both are copied here rather than tidied,
4187    /// since a client that matches on the text sees the difference.
4188    #[test]
4189    fn pubsub_says_no_two_different_ways() {
4190        let mut f = Fixture::new();
4191        assert_eq!(
4192            f.run(&[b"PUBSUB"]),
4193            "-ERR wrong number of arguments for 'pubsub' command\r\n"
4194        );
4195        assert_eq!(
4196            f.run(&[b"PUBSUB", b"NOPE"]),
4197            "-ERR unknown subcommand 'NOPE'. Try PUBSUB HELP.\r\n"
4198        );
4199        assert_eq!(
4200            f.run(&[b"PUBSUB", b"CHANNELS", b"a*", b"b"]),
4201            "-ERR unknown subcommand or wrong number of arguments for 'CHANNELS'. Try PUBSUB HELP.\r\n"
4202        );
4203        assert_eq!(
4204            f.run(&[b"PUBSUB", b"NUMPAT", b"x"]),
4205            "-ERR wrong number of arguments for 'pubsub|numpat' command\r\n"
4206        );
4207        assert_eq!(
4208            f.run(&[b"PUBSUB", b"HELP", b"x"]),
4209            "-ERR wrong number of arguments for 'pubsub|help' command\r\n"
4210        );
4211    }
4212
4213    /// Publishing to nobody costs a lookup and answers zero, which is the
4214    /// common case on a server that has pub/sub compiled in and not in use.
4215    #[test]
4216    fn publishing_to_nobody_answers_zero() {
4217        let mut f = Fixture::new();
4218        assert_eq!(f.run(&[b"PUBLISH", b"a", b"hi"]), ":0\r\n");
4219        assert_eq!(f.run(&[b"SPUBLISH", b"a", b"hi"]), ":0\r\n");
4220        // An empty channel name is a name like any other.
4221        assert_eq!(f.run(&[b"PUBLISH", b"", b"hi"]), ":0\r\n");
4222    }
4223
4224    /// A publish counts everybody it reached, which is not the same as the
4225    /// number of subscribers: one connection holding two patterns that both
4226    /// match is two.
4227    #[test]
4228    fn a_publish_counts_the_deliveries_and_not_the_clients() {
4229        let mut f = Fixture::new();
4230        let mut sub = Session::new(9);
4231        f.by(&mut sub, &[b"SUBSCRIBE", b"news"]);
4232        f.by(&mut sub, &[b"PSUBSCRIBE", b"ne*"]);
4233        f.by(&mut sub, &[b"PSUBSCRIBE", b"n*s"]);
4234        assert_eq!(f.run(&[b"PUBLISH", b"news", b"hi"]), ":3\r\n");
4235        forget_session(&f.server, &mut sub);
4236    }
4237
4238    /// What a client does all day: write the same keys again and again. Every
4239    /// one of those writes leaves the previous record behind, so a server that
4240    /// never compacts holds every version of every key it has ever been sent.
4241    ///
4242    /// Not under Miri, and not because of anything it would find. The bound
4243    /// only means something once several megabytes have gone through the
4244    /// arena, which reclaims a segment at a time and has segments of two
4245    /// megabytes, so a server that reclaimed nothing would still be under the
4246    /// bound in any smaller version of this. Thirty two megabytes is thirty
4247    /// two thousand commands and was over forty minutes interpreted. The paths
4248    /// it walks are walked by the hundreds of tests around it that write a key
4249    /// and read it back, which do run there.
4250    #[cfg_attr(miri, ignore = "megabytes through the arena")]
4251    #[test]
4252    fn rewriting_the_same_keys_does_not_grow_the_server() {
4253        let mut f = Fixture::new();
4254        let val = vec![b'v'; 1024];
4255        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
4256
4257        for k in &keys {
4258            f.run(&[b"SET", k, &val]);
4259        }
4260        f.server.compact_step();
4261        let after_first = f.server.memory_bytes();
4262
4263        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
4264        // of it. Thirty two megabytes written to hold sixty four kilobytes,
4265        // which is the shape of a real workload and is enough churn to fill
4266        // sixteen segments if nothing ever comes back.
4267        for _ in 0..500 {
4268            for k in &keys {
4269                f.run(&[b"SET", k, &val]);
4270            }
4271            f.server.compact_step();
4272        }
4273
4274        assert!(
4275            f.server.memory_bytes() <= after_first * 2,
4276            "held {} after five hundred passes against {after_first} after one",
4277            f.server.memory_bytes()
4278        );
4279        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
4280        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
4281    }
4282
4283    /// The same churn on a database nobody starts on, either side of a quiet
4284    /// spell long enough for the maintenance turn to stop asking about it.
4285    ///
4286    /// The turn after each batch skips a database that has already said it has
4287    /// nothing to collect and has not been touched since, which is what keeps a
4288    /// server whose clients are all on database zero from loading and storing
4289    /// in the other fifteen every batch to be told no. Two things could go
4290    /// wrong with that. A database might never be marked at all, so this uses
4291    /// database nine, which nothing marks by accident. And a database whose
4292    /// mark was cleared might never get it back, so this drains the collector
4293    /// until it says there is nothing left, checks the mark really is gone, and
4294    /// then writes another thirty two megabytes through the same sixty four
4295    /// keys. If either went wrong the server would hold all of it.
4296    ///
4297    /// Not under Miri, for the reason on the test above: the volume is the
4298    /// claim, and the volume is what the interpreter charges for.
4299    #[cfg_attr(miri, ignore = "megabytes through the arena")]
4300    #[test]
4301    fn a_database_nobody_started_on_is_still_collected() {
4302        let mut f = Fixture::new();
4303        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
4304        let val = vec![b'v'; 1024];
4305        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
4306
4307        for k in &keys {
4308            f.run(&[b"SET", k, &val]);
4309        }
4310        // A call looks at [`COMPACT_LOOKS`] stripes and not at all of them, so
4311        // draining takes calls in proportion to the width and one call saying
4312        // there was nothing to move is not the whole database saying it.
4313        let drain = |f: &Fixture| {
4314            for _ in 0..4 * f.server.slots() {
4315                if f.server.compact_step().is_none() && !f.server.mine().wanted(9) {
4316                    return;
4317                }
4318            }
4319            panic!("compaction never got to the end of database nine");
4320        };
4321        drain(&f);
4322        assert!(
4323            !f.server.mine().wanted(9),
4324            "database nine was drained and should not be asked again until it is written to"
4325        );
4326        let after_first = f.server.memory_bytes();
4327
4328        for _ in 0..500 {
4329            for k in &keys {
4330                f.run(&[b"SET", k, &val]);
4331            }
4332            f.server.compact_step();
4333        }
4334
4335        assert!(
4336            f.server.memory_bytes() <= after_first * 2,
4337            "held {} after five hundred passes against {after_first} after one",
4338            f.server.memory_bytes()
4339        );
4340        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
4341        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
4342        // And nothing landed anywhere else on the way.
4343        f.run(&[b"SELECT", b"0"]);
4344        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4345    }
4346
4347    /// The maintenance turn moves its own cursor and leaves the server's alone.
4348    ///
4349    /// The turn runs after every batch on every thread, so anything it writes
4350    /// that the whole server can see is a line every thread is writing to at
4351    /// batch rate, and the cost of that goes up with the thread count instead
4352    /// of staying still. The cursor is the last thing in the turn that was
4353    /// shared, and it was shared for a reason that only ever applied to the
4354    /// other caller: [`Server::compact_hard_step`] runs when a server is over
4355    /// its memory limit, which is a rare thing and not a per batch thing.
4356    ///
4357    /// What is checked is both halves of that. The turn is asked to walk, and
4358    /// afterwards this thread's cursor has moved and the server's has not.
4359    #[test]
4360    fn the_maintenance_turn_does_not_write_a_shared_cursor() {
4361        let f = Fixture::new();
4362        let before = f.server.next_db.load(Relaxed);
4363        let mine = f.server.mine().compact_db.load(Relaxed);
4364        // Nothing to compact, which is the case that matters: a turn that found
4365        // nothing is nearly every turn, and it used to write the shared cursor
4366        // anyway just to say where the next one should start.
4367        assert!(f.server.compact_step().is_none());
4368        assert_eq!(
4369            f.server.next_db.load(Relaxed),
4370            before,
4371            "the turn wrote the cursor the over limit path reads"
4372        );
4373        assert_ne!(
4374            f.server.mine().compact_db.load(Relaxed),
4375            mine,
4376            "the turn did not move on, so it will look at the same stripes forever"
4377        );
4378    }
4379
4380    /// Two threads start their walk in different places.
4381    ///
4382    /// Splitting the cursor gave up the one thing sharing bought, which is two
4383    /// threads not arriving at the same database at the same moment. Seeding
4384    /// each thread's cursor at its own index buys most of it back for nothing,
4385    /// and this is that: a server built for eight threads has eight cursors and
4386    /// no two of them start together.
4387    #[test]
4388    fn each_thread_starts_its_compaction_somewhere_else() {
4389        let mut server = Server::new();
4390        server.set_threads(8);
4391        let starts: Vec<usize> = server
4392            .locals
4393            .iter()
4394            .map(|t| t.compact_db.load(Relaxed))
4395            .collect();
4396        assert_eq!(starts, (0..8).collect::<Vec<usize>>());
4397    }
4398
4399    #[test]
4400    fn a_command_goes_from_bytes_to_bytes() {
4401        let mut f = Fixture::new();
4402        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
4403        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
4404        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
4405        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
4406        // The name is matched whatever case it came in, and so are the options.
4407        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
4408        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
4409    }
4410
4411    #[test]
4412    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
4413        let mut f = Fixture::new();
4414        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
4415        // A key named twice exists twice and can only be deleted once, and both
4416        // of those are Redis's answers rather than tidier ones.
4417        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
4418        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
4419        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
4420        // UNLINK is the same body and reports the same way.
4421        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
4422        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4423    }
4424
4425    #[test]
4426    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
4427        let mut f = Fixture::new();
4428        f.run(&[b"SET", b"k", b"v"]);
4429        // A simple string on both protocols, which is unusual: most replies
4430        // that carry a word are bulk strings.
4431        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
4432        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
4433    }
4434
4435    #[test]
4436    fn touch_counts_the_way_exists_counts() {
4437        let mut f = Fixture::new();
4438        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
4439        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
4440        assert_eq!(
4441            f.run(&[b"TOUCH", b"a", b"a"]),
4442            ":2\r\n",
4443            "twice counts twice"
4444        );
4445        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
4446        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
4447    }
4448
4449    #[test]
4450    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
4451        let mut f = Fixture::new();
4452        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
4453        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
4454
4455        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
4456        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
4457        assert_eq!(
4458            f.run(&[b"TTL", b"b"]),
4459            ":100\r\n",
4460            "the source's and not b's"
4461        );
4462        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
4463    }
4464
4465    #[test]
4466    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
4467        let mut f = Fixture::new();
4468        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
4469        // The source is checked before the destination, so this is the error
4470        // and not the zero RENAMENX would otherwise answer for a taken name.
4471        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
4472    }
4473
4474    #[test]
4475    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
4476        let mut f = Fixture::new();
4477        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
4478
4479        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
4480        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
4481        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
4482        // one call the two disagree about and neither does any work for.
4483        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
4484        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
4485        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
4486        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
4487    }
4488
4489    #[test]
4490    fn renaming_a_set_does_not_touch_a_member() {
4491        let mut f = Fixture::new();
4492        for i in 0..300 {
4493            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
4494        }
4495        let before = f.server.memory_bytes();
4496
4497        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
4498        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
4499        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
4500        assert!(
4501            f.server.memory_bytes().abs_diff(before) < 256,
4502            "the members were copied: {} against {before}",
4503            f.server.memory_bytes()
4504        );
4505    }
4506
4507    #[test]
4508    fn a_copy_is_a_second_value_and_not_a_second_name() {
4509        let mut f = Fixture::new();
4510        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
4511
4512        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
4513        f.run(&[b"SADD", b"t", b"m3"]);
4514        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
4515        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
4516    }
4517
4518    /// Every type a key can hold, copied, because two of them used to panic.
4519    ///
4520    /// `COPY` reads the value out of the source through one match on the type
4521    /// tag, and that match had a catch all at the bottom from back when a set
4522    /// and a hash were the only bodies. The list and the sorted set landed after
4523    /// it and nobody came back, so `COPY mylist other` took the shard down. It
4524    /// is an ordinary command against a type the server supports everywhere
4525    /// else, so this walks all five rather than the two that were broken: the
4526    /// point is that the next type cannot land the same way.
4527    #[test]
4528    fn every_type_can_be_copied() {
4529        let mut f = Fixture::new();
4530        f.run(&[b"SET", b"str", b"v1"]);
4531        f.run(&[b"SADD", b"set", b"m1"]);
4532        f.run(&[b"HSET", b"hash", b"f", b"v"]);
4533        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
4534        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
4535
4536        for name in [
4537            &b"str"[..],
4538            &b"set"[..],
4539            &b"hash"[..],
4540            &b"list"[..],
4541            &b"zset"[..],
4542        ] {
4543            let dst = [name, b":copy"].concat();
4544            assert_eq!(
4545                f.run(&[b"COPY", name, &dst]),
4546                ":1\r\n",
4547                "copying {}",
4548                String::from_utf8_lossy(name)
4549            );
4550            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
4551        }
4552
4553        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
4554            let mut want = String::from("*2\r\n");
4555            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
4556            want
4557        });
4558        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
4559
4560        // And the copy is its own value, not a second name for the source.
4561        f.run(&[b"RPUSH", b"list:copy", b"c"]);
4562        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
4563        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
4564    }
4565
4566    #[test]
4567    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
4568        let mut f = Fixture::new();
4569        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
4570        f.run(&[b"SET", b"b", b"v2"]);
4571
4572        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
4573        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
4574        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
4575        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
4576        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
4577        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
4578    }
4579
4580    #[test]
4581    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
4582        let mut f = Fixture::new();
4583        f.run(&[b"SET", b"a", b"v1"]);
4584
4585        // Same key, different database, so this is not the same object and is
4586        // an ordinary copy. Same key in the same database is the error below.
4587        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
4588        f.run(&[b"SELECT", b"1"]);
4589        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
4590        assert_eq!(
4591            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
4592            ":0\r\n",
4593            "taken"
4594        );
4595        assert_eq!(
4596            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
4597            ":1\r\n"
4598        );
4599    }
4600
4601    #[test]
4602    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
4603        let mut f = Fixture::new();
4604        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
4605        assert_eq!(
4606            f.run(&[b"SORT", b"l"]),
4607            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
4608        );
4609        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
4610        assert_eq!(
4611            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
4612            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
4613        );
4614        assert_eq!(
4615            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
4616            "*1\r\n$1\r\n2\r\n"
4617        );
4618    }
4619
4620    #[test]
4621    fn sort_reads_a_key_per_element_for_by_and_for_get() {
4622        let mut f = Fixture::new();
4623        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
4624        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
4625        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
4626        // misses, which is a nil in the middle of the array and not a short one.
4627        assert_eq!(
4628            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
4629            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
4630        );
4631    }
4632
4633    #[test]
4634    fn sort_store_writes_a_list_and_answers_its_length() {
4635        let mut f = Fixture::new();
4636        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
4637        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
4638        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
4639        assert_eq!(
4640            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
4641            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
4642        );
4643        // An empty result takes the destination with it rather than leaving a
4644        // list that holds nothing.
4645        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
4646        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
4647    }
4648
4649    #[test]
4650    fn sort_ro_does_not_know_the_word_store() {
4651        let mut f = Fixture::new();
4652        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
4653        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
4654        assert_eq!(
4655            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
4656            "-ERR syntax error\r\n"
4657        );
4658        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4659    }
4660
4661    #[test]
4662    fn sort_refuses_what_it_cannot_sort() {
4663        let mut f = Fixture::new();
4664        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
4665        f.run(&[b"SET", b"s", b"x"]);
4666        assert_eq!(
4667            f.run(&[b"SORT", b"s"]),
4668            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
4669        );
4670        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
4671        assert_eq!(
4672            f.run(&[b"SORT", b"words"]),
4673            "-ERR One or more scores can't be converted into double\r\n"
4674        );
4675        assert_eq!(
4676            f.run(&[b"SORT", b"words", b"ALPHA"]),
4677            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
4678        );
4679        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
4680    }
4681
4682    #[test]
4683    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
4684        let mut f = Fixture::new();
4685        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
4686        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
4687        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
4688        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4689        assert_eq!(
4690            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
4691            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
4692        );
4693        // And back, which proves the body survived the trip rather than being
4694        // rebuilt from a copy that happened to look the same.
4695        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
4696        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
4697    }
4698
4699    #[test]
4700    fn move_answers_zero_when_either_end_says_no() {
4701        let mut f = Fixture::new();
4702        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
4703        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
4704        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4705        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
4706        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
4707        // The destination is taken, so nothing moves and the source is still
4708        // there with what it had.
4709        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
4710        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
4711        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4712        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
4713    }
4714
4715    #[test]
4716    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
4717        let mut f = Fixture::new();
4718        assert_eq!(
4719            f.run(&[b"MOVE", b"a", b"0"]),
4720            "-ERR source and destination objects are the same\r\n"
4721        );
4722        assert_eq!(
4723            f.run(&[b"MOVE", b"a", b"99"]),
4724            "-ERR DB index is out of range\r\n"
4725        );
4726        assert_eq!(
4727            f.run(&[b"MOVE", b"a", b"-1"]),
4728            "-ERR DB index is out of range\r\n"
4729        );
4730        assert_eq!(
4731            f.run(&[b"MOVE", b"a", b"x"]),
4732            "-ERR value is not an integer or out of range\r\n"
4733        );
4734    }
4735
4736    #[test]
4737    fn swapdb_swaps_what_two_connections_would_see() {
4738        let mut f = Fixture::new();
4739        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
4740        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4741        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
4742        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
4743
4744        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
4745        // Still on database zero, and database zero is a different database.
4746        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
4747        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4748        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
4749        // A database swapped with itself is fine and changes nothing.
4750        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
4751        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
4752    }
4753
4754    /// Every database on a server reads the server's clock and not one of its
4755    /// own. They used to be told the time one at a time and now they share the
4756    /// reading, so a server that built its databases from a second clock would
4757    /// answer a deadline worked out against a time nobody had set.
4758    #[test]
4759    fn a_wide_server_puts_its_databases_on_its_own_clock() {
4760        let mut f = Fixture::striped(8);
4761        f.server.set_clock_ms(1_700_000_000_000);
4762        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"100"]), "+OK\r\n");
4763        assert_eq!(f.run(&[b"EXPIRETIME", b"k"]), ":1700000100\r\n");
4764        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
4765        f.server.set_clock_ms(1_700_000_050_000);
4766        assert_eq!(f.run(&[b"TTL", b"k"]), ":50\r\n");
4767    }
4768
4769    /// The swap is stripe by stripe, so a database cut into more than one
4770    /// stripe is the case that would catch it exchanging some of the keys and
4771    /// leaving the rest. Sixteen keys over four stripes is enough that every
4772    /// stripe has something in it whatever the hashes come out as.
4773    #[test]
4774    fn swapdb_swaps_every_stripe_of_a_wide_database() {
4775        let mut f = Fixture::striped(4);
4776        for i in 0..16u32 {
4777            let key = format!("k{i}");
4778            assert_eq!(f.run(&[b"SET", key.as_bytes(), b"zero"]), "+OK\r\n");
4779        }
4780        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4781        assert_eq!(f.run(&[b"SET", b"only", b"one"]), "+OK\r\n");
4782        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
4783
4784        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
4785        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4786        assert_eq!(f.run(&[b"GET", b"only"]), "$3\r\none\r\n");
4787        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
4788        assert_eq!(f.run(&[b"DBSIZE"]), ":16\r\n");
4789        for i in 0..16u32 {
4790            let key = format!("k{i}");
4791            assert_eq!(f.run(&[b"GET", key.as_bytes()]), "$4\r\nzero\r\n");
4792        }
4793    }
4794
4795    #[test]
4796    fn swapdb_says_which_index_it_could_not_read() {
4797        let mut f = Fixture::new();
4798        assert_eq!(
4799            f.run(&[b"SWAPDB", b"x", b"1"]),
4800            "-ERR invalid first DB index\r\n"
4801        );
4802        assert_eq!(
4803            f.run(&[b"SWAPDB", b"0", b"y"]),
4804            "-ERR invalid second DB index\r\n"
4805        );
4806        // A number too big to be an index on a server that keeps one in an int
4807        // is the same complaint, and a plausible one that is not ours is the
4808        // range complaint instead. The split is Redis's.
4809        assert_eq!(
4810            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
4811            "-ERR invalid first DB index\r\n"
4812        );
4813        assert_eq!(
4814            f.run(&[b"SWAPDB", b"0", b"99"]),
4815            "-ERR DB index is out of range\r\n"
4816        );
4817        assert_eq!(
4818            f.run(&[b"SWAPDB", b"-1", b"0"]),
4819            "-ERR DB index is out of range\r\n"
4820        );
4821    }
4822
4823    #[test]
4824    fn wait_answers_zero_replicas_without_waiting() {
4825        let mut f = Fixture::new();
4826        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
4827        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
4828        // A replica that is never going to arrive, and a timeout that would be
4829        // a real wait on a server that had one.
4830        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
4831        // Negative replicas is not an error, because zero is already more than
4832        // it asked for.
4833        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
4834        assert_eq!(
4835            f.run(&[b"WAIT", b"x", b"0"]),
4836            "-ERR value is not an integer or out of range\r\n"
4837        );
4838        assert_eq!(
4839            f.run(&[b"WAIT", b"0", b"-1"]),
4840            "-ERR timeout is negative\r\n"
4841        );
4842        assert_eq!(
4843            f.run(&[b"WAIT", b"0", b"1.5"]),
4844            "-ERR timeout is not an integer or out of range\r\n"
4845        );
4846    }
4847
4848    #[test]
4849    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
4850        let mut f = Fixture::new();
4851        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
4852        assert_eq!(
4853            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
4854            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
4855        );
4856        assert_eq!(
4857            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
4858            "-ERR value is out of range, value must between 0 and 1\r\n"
4859        );
4860        assert_eq!(
4861            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
4862            "-ERR value is out of range, must be positive\r\n"
4863        );
4864        // The arguments are all read before the server looks at itself, so a
4865        // bad timeout beats the append only complaint even with numlocal set.
4866        assert_eq!(
4867            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
4868            "-ERR timeout is negative\r\n"
4869        );
4870    }
4871
4872    /// The bytes inside a bulk reply, with the header and the trailing break
4873    /// taken off. Every `DUMP` test needs this and none of them care how the
4874    /// length was written.
4875    fn payload(reply: &[u8]) -> Vec<u8> {
4876        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
4877        reply[head + 2..reply.len() - 2].to_vec()
4878    }
4879
4880    #[test]
4881    fn a_value_survives_a_dump_and_a_restore() {
4882        let mut f = Fixture::new();
4883        f.run(&[b"SET", b"s", b"hello"]);
4884        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
4885        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
4886        f.run(&[b"SADD", b"u", b"x", b"y"]);
4887        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
4888        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
4889
4890        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
4891            let mut copy = key.to_vec();
4892            copy.push(b'2');
4893            let bytes = payload(&f.raw(&[b"DUMP", key]));
4894            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
4895            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
4896        }
4897
4898        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
4899        assert_eq!(
4900            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
4901            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4902        );
4903        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
4904        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
4905        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
4906        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
4907        // The encoding survives too, since the payload names the plainest legal
4908        // type and the loader puts the value back on the rung it belongs on.
4909        assert_eq!(
4910            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
4911            f.run(&[b"OBJECT", b"ENCODING", b"t"])
4912        );
4913    }
4914
4915    #[test]
4916    fn a_dumped_hash_keeps_its_field_deadlines() {
4917        let mut f = Fixture::new();
4918        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
4919        assert_eq!(
4920            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
4921            "*1\r\n:1\r\n"
4922        );
4923        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
4924        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
4925        assert_eq!(
4926            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
4927            "*2\r\n:-1\r\n:100\r\n"
4928        );
4929    }
4930
4931    #[test]
4932    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
4933        let mut f = Fixture::new();
4934        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
4935        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
4936        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
4937        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
4938        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
4939        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
4940        // An absolute deadline that has already gone is not an error. The key is
4941        // not created and the reply is the same OK a live one gets.
4942        assert_eq!(
4943            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
4944            "+OK\r\n"
4945        );
4946        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4947    }
4948
4949    #[test]
4950    fn dump_answers_nothing_for_a_key_that_is_not_there() {
4951        let mut f = Fixture::new();
4952        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
4953        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
4954        f.advance(50);
4955        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
4956    }
4957
4958    #[test]
4959    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
4960        let mut f = Fixture::new();
4961        f.run(&[b"SET", b"a", b"first"]);
4962        f.run(&[b"SET", b"b", b"second"]);
4963        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
4964        assert_eq!(
4965            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
4966            "-BUSYKEY Target key name already exists.\r\n"
4967        );
4968        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
4969        assert_eq!(
4970            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
4971            "+OK\r\n"
4972        );
4973        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
4974    }
4975
4976    /// The busy key comes before the payload, which is not the order the
4977    /// arguments read in. Whether a key is taken should not depend on whether
4978    /// the bytes behind it happened to be good.
4979    #[test]
4980    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
4981        let mut f = Fixture::new();
4982        f.run(&[b"SET", b"a", b"v"]);
4983        assert_eq!(
4984            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
4985            "-BUSYKEY Target key name already exists.\r\n"
4986        );
4987        // And the options come before even that, so a bad FREQ beats the busy
4988        // key the same way a bad DB beats a missing source in COPY.
4989        assert_eq!(
4990            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
4991            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
4992        );
4993    }
4994
4995    #[test]
4996    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
4997        let mut f = Fixture::new();
4998        f.run(&[b"SET", b"a", b"hello"]);
4999        let good = payload(&f.raw(&[b"DUMP", b"a"]));
5000
5001        let mut flipped = good.clone();
5002        flipped[2] ^= 0x40;
5003        assert_eq!(
5004            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
5005            "-ERR DUMP payload version or checksum are wrong\r\n"
5006        );
5007        assert_eq!(
5008            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
5009            "-ERR DUMP payload version or checksum are wrong\r\n"
5010        );
5011        // A footer that is right over a body that is not. The type byte says
5012        // string and there is nothing behind it, so the checksum agrees and the
5013        // value does not exist.
5014        let mut truncated = good[..1].to_vec();
5015        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
5016        let crc = yo_common::crc::crc64(0, &truncated);
5017        truncated.extend_from_slice(&crc.to_le_bytes());
5018        assert_eq!(
5019            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
5020            "-ERR Bad data format\r\n"
5021        );
5022        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
5023    }
5024
5025    #[test]
5026    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
5027        let mut f = Fixture::new();
5028        f.run(&[b"SET", b"a", b"v"]);
5029        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
5030        assert_eq!(
5031            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
5032            "-ERR Invalid TTL value, must be >= 0\r\n"
5033        );
5034        assert_eq!(
5035            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
5036            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
5037        );
5038        assert_eq!(
5039            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
5040            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
5041        );
5042        // Both are accepted and both are then dropped, which is D-26.
5043        assert_eq!(
5044            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
5045            "+OK\r\n"
5046        );
5047        assert_eq!(
5048            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
5049            "+OK\r\n"
5050        );
5051    }
5052
5053    /// Neither word is refused for being the wrong one. Each is only accepted
5054    /// while the other is unset, so the second of the two falls through to the
5055    /// plain syntax error rather than getting a message of its own.
5056    #[test]
5057    fn restore_takes_idletime_or_freq_and_not_both() {
5058        let mut f = Fixture::new();
5059        f.run(&[b"SET", b"a", b"v"]);
5060        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
5061        assert_eq!(
5062            f.run(&[
5063                b"RESTORE",
5064                b"b",
5065                b"0",
5066                &bytes,
5067                b"IDLETIME",
5068                b"1",
5069                b"FREQ",
5070                b"2"
5071            ]),
5072            "-ERR syntax error\r\n"
5073        );
5074        assert_eq!(
5075            f.run(&[
5076                b"RESTORE",
5077                b"b",
5078                b"0",
5079                &bytes,
5080                b"FREQ",
5081                b"2",
5082                b"IDLETIME",
5083                b"1"
5084            ]),
5085            "-ERR syntax error\r\n"
5086        );
5087        assert_eq!(
5088            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
5089            "-ERR syntax error\r\n"
5090        );
5091        assert_eq!(
5092            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
5093            "-ERR syntax error\r\n"
5094        );
5095    }
5096
5097    #[test]
5098    fn copy_checks_its_options_before_it_looks_for_anything() {
5099        let mut f = Fixture::new();
5100        // No key exists at all, and every one of these is still the option
5101        // complaint rather than a zero, which is the order a real server uses.
5102        assert_eq!(
5103            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
5104            "-ERR DB index is out of range\r\n"
5105        );
5106        assert_eq!(
5107            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
5108            "-ERR DB index is out of range\r\n"
5109        );
5110        assert_eq!(
5111            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
5112            "-ERR value is not an integer or out of range\r\n"
5113        );
5114        assert_eq!(
5115            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
5116            "-ERR syntax error\r\n"
5117        );
5118        assert_eq!(
5119            f.run(&[b"COPY", b"a", b"a"]),
5120            "-ERR source and destination objects are the same\r\n"
5121        );
5122        // Repeated, reordered and lowercased, and the last DB wins.
5123        assert_eq!(
5124            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
5125            ":0\r\n"
5126        );
5127    }
5128
5129    #[test]
5130    fn time_is_two_bulk_strings_and_moves() {
5131        let mut f = Fixture::new();
5132        let first = f.run(&[b"TIME"]);
5133        assert!(first.starts_with("*2\r\n$"), "got {first}");
5134        let parts: Vec<&str> = first.split("\r\n").collect();
5135        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
5136        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
5137        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
5138        assert!((0..1_000_000).contains(&micros), "got {micros}");
5139        // The coarse clock the keyspace uses is a cached millisecond that a
5140        // background tick refreshes, so a TIME built on it would answer the
5141        // same microsecond twice in a row here.
5142        assert_ne!(first, f.run(&[b"TIME"]));
5143    }
5144
5145    #[test]
5146    fn a_keyspace_scan_walks_every_key_once() {
5147        // The count below is thirty two, so ninety six keys is three pages of
5148        // cursor and says the same thing as five hundred at a fifth of the
5149        // interpreted work.
5150        let n = if cfg!(miri) { 96 } else { 500 };
5151        let mut f = Fixture::new();
5152        for i in 0..n {
5153            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
5154        }
5155
5156        let mut seen: Vec<String> = Vec::new();
5157        let mut cursor = "0".to_owned();
5158        let mut calls = 0;
5159        loop {
5160            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
5161            seen.extend(keys);
5162            cursor = next;
5163            calls += 1;
5164            assert!(calls < 10_000, "the cursor is not advancing");
5165            if cursor == "0" {
5166                break;
5167            }
5168        }
5169
5170        seen.sort();
5171        seen.dedup();
5172        assert_eq!(seen.len(), n, "every key once and only once");
5173        // And more than one call to get them, or the COUNT is being ignored and
5174        // the loop above proved nothing about resuming.
5175        assert!(calls > 1, "{n} keys came back in one batch");
5176    }
5177
5178    #[test]
5179    fn a_scan_narrows_by_pattern_and_by_type() {
5180        let mut f = Fixture::new();
5181        f.run(&[b"SET", b"str", b"v"]);
5182        f.run(&[b"SADD", b"members", b"a"]);
5183        f.run(&[b"HSET", b"fields", b"f", b"v"]);
5184
5185        let all = |f: &mut Fixture, args: &[&[u8]]| {
5186            let mut out: Vec<String> = Vec::new();
5187            let mut cursor = "0".to_owned();
5188            loop {
5189                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
5190                line.extend_from_slice(args);
5191                let (next, keys) = scan_reply(&f.run(&line));
5192                out.extend(keys);
5193                cursor = next;
5194                if cursor == "0" {
5195                    break;
5196                }
5197            }
5198            out.sort();
5199            out
5200        };
5201
5202        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
5203        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
5204        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
5205        // Case insensitive, the same as Redis's own comparison.
5206        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
5207        // A type nothing can hold is not an error, it just matches nothing.
5208        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
5209        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
5210        // Both filters at once, and they are an and rather than an or.
5211        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
5212    }
5213
5214    #[test]
5215    fn a_scan_says_what_is_wrong_with_it() {
5216        let mut f = Fixture::new();
5217        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
5218        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
5219        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
5220        assert_eq!(
5221            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
5222            "-ERR syntax error\r\n"
5223        );
5224        assert_eq!(
5225            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
5226            "-ERR value is not an integer or out of range\r\n"
5227        );
5228        assert_eq!(
5229            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
5230            "-ERR syntax error\r\n"
5231        );
5232        // A cursor the client made up is a cursor. It resumes somewhere
5233        // arbitrary and answers whatever is there, which is what Redis does and
5234        // is the only behaviour that does not need the server to remember every
5235        // cursor it has handed out.
5236        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
5237    }
5238
5239    #[test]
5240    fn keys_and_randomkey_look_at_the_whole_database() {
5241        let mut f = Fixture::new();
5242        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
5243        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
5244
5245        for name in ["one", "two", "three"] {
5246            f.run(&[b"SET", name.as_bytes(), b"v"]);
5247        }
5248        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
5249        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
5250        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
5251
5252        for _ in 0..50 {
5253            let got = f.run(&[b"RANDOMKEY"]);
5254            assert!(
5255                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
5256                "got {got}"
5257            );
5258        }
5259    }
5260
5261    #[test]
5262    fn a_walk_does_not_answer_keys_that_have_expired() {
5263        let mut f = Fixture::new();
5264        f.run(&[b"SET", b"alive", b"v"]);
5265        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
5266        f.server.advance_clock_ms(2);
5267        assert_eq!(
5268            f.run(&[b"DBSIZE"]),
5269            ":2\r\n",
5270            "nothing has collected it yet"
5271        );
5272
5273        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
5274        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
5275        assert_eq!(keys, ["alive"]);
5276        for _ in 0..20 {
5277            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
5278        }
5279        // The walk collected it on the way past, which is what makes DBSIZE
5280        // here answer what Redis answers once its own cycle has been round.
5281        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
5282    }
5283
5284    #[test]
5285    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
5286        let mut f = Fixture::new();
5287        f.run(&[b"SET", b"k", b"v"]);
5288        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
5289        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
5290
5291        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
5292        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
5293        let ms = int(&f.run(&[b"PTTL", b"k"]));
5294        assert!((99_000..=100_000).contains(&ms), "got {ms}");
5295
5296        // The absolute pair, derived from the same one number the store kept.
5297        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
5298        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
5299        assert_eq!(at, (at_ms + 500) / 1000);
5300        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5301
5302        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
5303        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
5304        assert_eq!(
5305            f.run(&[b"PERSIST", b"k"]),
5306            ":0\r\n",
5307            "nothing to take off the second time"
5308        );
5309        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
5310        assert_eq!(
5311            f.run(&[b"GET", b"k"]),
5312            "$1\r\nv\r\n",
5313            "and the value went through all of that untouched"
5314        );
5315    }
5316
5317    #[test]
5318    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
5319        let mut f = Fixture::new();
5320        f.run(&[b"SET", b"str", b"v"]);
5321        f.run(&[b"SADD", b"set", b"a", b"b"]);
5322        f.run(&[b"HSET", b"hash", b"f", b"v"]);
5323
5324        for key in [b"str".as_slice(), b"set", b"hash"] {
5325            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
5326            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
5327        }
5328        // The body is not touched by any of that, which is the whole reason the
5329        // deadline lives in the record and the body lives somewhere else.
5330        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
5331        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
5332        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5333    }
5334
5335    #[test]
5336    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
5337        let mut f = Fixture::new();
5338        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
5339            f.run(&[b"SET", key, b"v"]);
5340        }
5341        // Four ways of naming a moment that has passed, and all four are a
5342        // delete answering 1 rather than an error. Zero is a moment, minus one
5343        // is a moment, and the hash field commands refuse the negative one.
5344        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
5345        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
5346        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
5347        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
5348        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5349        assert_eq!(
5350            f.run(&[b"EXPIRE", b"a", b"100"]),
5351            ":0\r\n",
5352            "and the key really went, so there is nothing to put a deadline on"
5353        );
5354    }
5355
5356    #[test]
5357    fn the_four_conditions_decide_whether_the_deadline_moves() {
5358        let mut f = Fixture::new();
5359        f.run(&[b"SET", b"k", b"v"]);
5360
5361        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
5362        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
5363        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
5364        assert_eq!(
5365            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
5366            ":1\r\n",
5367            "no deadline reads as infinitely far away, so LT passes where GT fails"
5368        );
5369
5370        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
5371        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
5372        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
5373        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
5374        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
5375        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
5376
5377        // The condition is answered before the past check, so this is a 0 and
5378        // the key survives. The other order would delete it.
5379        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
5380        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
5381        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
5382        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
5383    }
5384
5385    #[test]
5386    fn the_conditions_are_a_set_and_not_a_keyword() {
5387        let mut f = Fixture::new();
5388        f.run(&[b"SET", b"k", b"v"]);
5389
5390        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
5391        assert_eq!(
5392            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
5393            ":0\r\n",
5394            "the same keyword twice means it once, and NX now has a deadline to fail on"
5395        );
5396
5397        // XX with LT is the one pair that is not either of them on its own: LT
5398        // alone would accept a key with no deadline and this does not.
5399        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
5400        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
5401        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
5402        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
5403        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
5404        f.run(&[b"PERSIST", b"k"]);
5405        assert_eq!(
5406            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
5407            ":0\r\n",
5408            "where LT on its own would have taken it"
5409        );
5410        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
5411    }
5412
5413    #[test]
5414    fn a_key_is_gone_once_its_moment_passes() {
5415        let mut f = Fixture::new();
5416        f.run(&[b"SET", b"k", b"v"]);
5417        f.run(&[b"EXPIRE", b"k", b"100"]);
5418
5419        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
5420        f.server.set_clock_ms(at as u64 + 1);
5421        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
5422        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
5423        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5424        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5425    }
5426
5427    #[test]
5428    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
5429        let mut f = Fixture::new();
5430        f.run(&[b"SET", b"k", b"v"]);
5431        for (bad, want) in [
5432            (
5433                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
5434                "-ERR value is not an integer or out of range\r\n",
5435            ),
5436            (
5437                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
5438                "-ERR Unsupported option MAYBE\r\n",
5439            ),
5440            (
5441                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
5442                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
5443            ),
5444            (
5445                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
5446                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
5447            ),
5448            (
5449                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
5450                "-ERR GT and LT options at the same time are not compatible\r\n",
5451            ),
5452            // Seconds that overflow when multiplied into milliseconds. Every
5453            // message names the command it came from.
5454            (
5455                &[b"EXPIRE", b"k", b"9223372036854775807"],
5456                "-ERR invalid expire time in 'expire' command\r\n",
5457            ),
5458            (
5459                &[b"EXPIREAT", b"k", b"9223372036854775807"],
5460                "-ERR invalid expire time in 'expireat' command\r\n",
5461            ),
5462            (
5463                &[b"PEXPIRE", b"k", b"9223372036854775807"],
5464                "-ERR invalid expire time in 'pexpire' command\r\n",
5465            ),
5466        ] {
5467            assert_eq!(f.run(bad), want, "for {bad:?}");
5468        }
5469        assert_eq!(
5470            f.run(&[b"TTL", b"k"]),
5471            ":-1\r\n",
5472            "and none of those put a deadline on anything"
5473        );
5474
5475        // The one of the four that has no arithmetic to overflow. Redis takes
5476        // it and holds the number as given, and a record here holds forty six
5477        // bits, so it lands in the year 4199 instead. D-17.
5478        assert_eq!(
5479            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
5480            ":1\r\n"
5481        );
5482        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
5483    }
5484
5485    #[test]
5486    fn flushing_empties_this_database_or_every_one_of_them() {
5487        let mut f = Fixture::new();
5488        f.run(&[b"SELECT", b"0"]);
5489        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
5490        f.run(&[b"SELECT", b"1"]);
5491        f.run(&[b"SET", b"c", b"3"]);
5492        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
5493        // ASYNC and SYNC are both taken and neither changes anything, since the
5494        // keyspace is empty before the OK goes out either way.
5495        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
5496        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5497        // Only database one was emptied.
5498        f.run(&[b"SELECT", b"0"]);
5499        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
5500        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
5501        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5502        f.run(&[b"SELECT", b"1"]);
5503        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5504        // Anything else after the name is a syntax error, and so is a third
5505        // argument even when the second one is a word we take.
5506        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
5507        assert_eq!(
5508            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
5509            "-ERR syntax error\r\n"
5510        );
5511    }
5512
5513    #[test]
5514    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
5515        let mut f = Fixture::new();
5516        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
5517        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
5518        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
5519        // Nothing is cached, so nothing is there, one answer per hash asked
5520        // about.
5521        assert_eq!(
5522            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
5523            "*2\r\n:0\r\n:0\r\n"
5524        );
5525        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
5526        assert_eq!(
5527            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
5528            "*0\r\n"
5529        );
5530        assert_eq!(
5531            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
5532            "-ERR Library not found\r\n"
5533        );
5534
5535        // Redis's two messages here are its own, one per container, and one of
5536        // them reads like a typo.
5537        assert_eq!(
5538            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
5539            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
5540        );
5541        assert_eq!(
5542            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
5543            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
5544        );
5545        // A second argument after the mode is the generic one instead, because
5546        // the count is checked before the word is looked at. The subcommand in
5547        // the sentence is the client's own spelling and not the canonical one,
5548        // which is the same thing `unknown subcommand` does.
5549        assert_eq!(
5550            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
5551            "-ERR unknown subcommand or wrong number of arguments for 'FLUSH'. Try FUNCTION HELP.\r\n"
5552        );
5553        assert_eq!(
5554            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
5555            "-ERR Unknown argument bogus\r\n"
5556        );
5557        assert_eq!(
5558            f.run(&[b"SCRIPT", b"EXISTS"]),
5559            "-ERR wrong number of arguments for 'script|exists' command\r\n"
5560        );
5561
5562        assert_eq!(
5563            f.run(&[b"FUNCTION", b"NOPE"]),
5564            "-ERR unknown subcommand 'NOPE'. Try FUNCTION HELP.\r\n"
5565        );
5566    }
5567
5568    #[test]
5569    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5570    fn the_script_cache_holds_what_was_loaded_into_it() {
5571        let mut f = Fixture::new();
5572        // The hash is the sha1 of the body and nothing else, so it is the same
5573        // number a real server answers and a client can compute it itself.
5574        let sha = b"e0e1f9fabfc9d4800c877a703b823ac0578ff8db";
5575        assert_eq!(
5576            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
5577            "$40\r\ne0e1f9fabfc9d4800c877a703b823ac0578ff8db\r\n"
5578        );
5579        assert_eq!(f.run(&[b"SCRIPT", b"EXISTS", sha]), "*1\r\n:1\r\n");
5580        assert_eq!(f.run(&[b"EVALSHA", sha, b"0"]), ":1\r\n");
5581        // Loading is idempotent and a body that will not parse is refused
5582        // where it was written rather than where it is called.
5583        assert_eq!(
5584            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
5585            "$40\r\ne0e1f9fabfc9d4800c877a703b823ac0578ff8db\r\n"
5586        );
5587        assert!(
5588            f.run(&[b"SCRIPT", b"LOAD", b"this is not lua"])
5589                .starts_with("-ERR Error compiling script"),
5590        );
5591
5592        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
5593        assert_eq!(f.run(&[b"SCRIPT", b"EXISTS", sha]), "*1\r\n:0\r\n");
5594        assert_eq!(
5595            f.run(&[b"EVALSHA", sha, b"0"]),
5596            "-NOSCRIPT No matching script. Please use EVAL.\r\n"
5597        );
5598
5599        // Running the body puts it in the cache too, which is what makes the
5600        // load then call then fall back to load pattern a client uses work.
5601        assert_eq!(f.run(&[b"EVAL", b"return 1", b"0"]), ":1\r\n");
5602        assert_eq!(f.run(&[b"SCRIPT", b"EXISTS", sha]), "*1\r\n:1\r\n");
5603
5604        // Nothing here can run long enough to be killed, which is D-101, so
5605        // the answer is the one a real server gives when nothing is stuck.
5606        assert_eq!(
5607            f.run(&[b"SCRIPT", b"KILL"]),
5608            "-NOTBUSY No scripts in execution right now.\r\n"
5609        );
5610        assert_eq!(f.run(&[b"SCRIPT", b"DEBUG", b"NO"]), "+OK\r\n");
5611        assert_eq!(f.run(&[b"SCRIPT", b"DEBUG", b"yes"]), "+OK\r\n");
5612        assert_eq!(
5613            f.run(&[b"SCRIPT", b"DEBUG", b"maybe"]),
5614            "-ERR Use SCRIPT DEBUG YES/SYNC/NO\r\n"
5615        );
5616    }
5617
5618    #[test]
5619    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5620    fn eval_counts_its_keys_before_it_compiles_anything() {
5621        let mut f = Fixture::new();
5622        assert_eq!(
5623            f.run(&[b"EVAL", b"return 1"]),
5624            "-ERR wrong number of arguments for 'eval' command\r\n"
5625        );
5626        assert_eq!(
5627            f.run(&[b"EVAL", b"return 1", b"abc"]),
5628            "-ERR value is not an integer or out of range\r\n"
5629        );
5630        assert_eq!(
5631            f.run(&[b"EVAL", b"return 1", b"-1"]),
5632            "-ERR Number of keys can't be negative\r\n"
5633        );
5634        assert_eq!(
5635            f.run(&[b"EVAL", b"return 1", b"1"]),
5636            "-ERR Number of keys can't be greater than number of args\r\n"
5637        );
5638        // The count splits the tail, and everything past the keys is ARGV.
5639        assert_eq!(
5640            f.run(&[
5641                b"EVAL",
5642                b"return {KEYS[1],KEYS[2],ARGV[1]}",
5643                b"2",
5644                b"a",
5645                b"b",
5646                b"c"
5647            ]),
5648            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5649        );
5650        assert_eq!(
5651            f.run(&[b"EVAL", b"return #KEYS", b"0", b"a", b"b"]),
5652            ":0\r\n"
5653        );
5654        assert_eq!(
5655            f.run(&[b"EVAL", b"return #ARGV", b"0", b"a", b"b"]),
5656            ":2\r\n"
5657        );
5658    }
5659
5660    #[test]
5661    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5662    fn a_lua_value_comes_back_as_the_reply_it_maps_to() {
5663        let mut f = Fixture::new();
5664        let eval = |f: &mut Fixture, body: &[u8]| f.run(&[b"EVAL", body, b"0"]);
5665
5666        // A number is truncated toward zero rather than rounded, and the two
5667        // ends of the range saturate the way the cast does.
5668        assert_eq!(eval(&mut f, b"return 3.99"), ":3\r\n");
5669        assert_eq!(eval(&mut f, b"return -3.99"), ":-3\r\n");
5670        assert_eq!(eval(&mut f, b"return 0.5"), ":0\r\n");
5671        assert_eq!(eval(&mut f, b"return 2^63"), ":9223372036854775807\r\n");
5672        assert_eq!(eval(&mut f, b"return -2^63"), ":-9223372036854775808\r\n");
5673        assert_eq!(eval(&mut f, b"return 1/0"), ":9223372036854775807\r\n");
5674        assert_eq!(eval(&mut f, b"return 0/0"), ":0\r\n");
5675
5676        assert_eq!(eval(&mut f, b"return 'hello'"), "$5\r\nhello\r\n");
5677        assert_eq!(eval(&mut f, b"return true"), ":1\r\n");
5678        // Everything that is not there is the same nothing.
5679        assert_eq!(eval(&mut f, b"return false"), "$-1\r\n");
5680        assert_eq!(eval(&mut f, b"return nil"), "$-1\r\n");
5681        assert_eq!(eval(&mut f, b"return"), "$-1\r\n");
5682        assert_eq!(eval(&mut f, b""), "$-1\r\n");
5683
5684        // A table is an array that stops at the first hole, which is what makes
5685        // a script build a reply by appending rather than by indexing.
5686        assert_eq!(eval(&mut f, b"return {}"), "*0\r\n");
5687        assert_eq!(eval(&mut f, b"return {1,2,nil,4}"), "*2\r\n:1\r\n:2\r\n");
5688        assert_eq!(
5689            eval(&mut f, b"return {1,'a',{2}}"),
5690            "*3\r\n:1\r\n$1\r\na\r\n*1\r\n:2\r\n"
5691        );
5692
5693        // The named fields, in the order a real server looks for them.
5694        assert_eq!(eval(&mut f, b"return {ok='fine'}"), "+fine\r\n");
5695        assert_eq!(eval(&mut f, b"return {err='mine'}"), "-mine\r\n");
5696        assert_eq!(eval(&mut f, b"return {err='a', ok='b'}"), "-a\r\n");
5697        assert_eq!(eval(&mut f, b"return {ok='b', double=1.5}"), "+b\r\n");
5698        // A line break inside one of them becomes a space, because the reply is
5699        // a single line and a client that saw the break would lose the frame.
5700        assert_eq!(eval(&mut f, b"return {ok='a\\r\\nb'}"), "+a  b\r\n");
5701        // A field of the wrong type is not that kind of reply at all, and falls
5702        // through to the array walk, which finds nothing.
5703        assert_eq!(eval(&mut f, b"return {ok=1}"), "*0\r\n");
5704        assert_eq!(eval(&mut f, b"return {err={}}"), "*0\r\n");
5705    }
5706
5707    #[test]
5708    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5709    fn the_protocol_the_client_asked_for_is_the_one_a_table_answers_in() {
5710        let mut f = Fixture::new();
5711        // Under RESP2 the four typed tables have to come back as something a
5712        // client that only knows RESP2 can read.
5713        assert_eq!(
5714            f.run(&[b"EVAL", b"return {double=3.5}", b"0"]),
5715            "$3\r\n3.5\r\n"
5716        );
5717        assert_eq!(
5718            f.run(&[b"EVAL", b"return {big_number='123'}", b"0"]),
5719            "$3\r\n123\r\n"
5720        );
5721        assert_eq!(
5722            f.run(&[b"EVAL", b"return {map={a='b'}}", b"0"]),
5723            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
5724        );
5725        assert_eq!(
5726            f.run(&[b"EVAL", b"return {set={a=true}}", b"0"]),
5727            "*1\r\n$1\r\na\r\n"
5728        );
5729        assert_eq!(f.run(&[b"EVAL", b"return false", b"0"]), "$-1\r\n");
5730
5731        f.out = Out::new(Proto::Resp3);
5732        assert_eq!(f.run(&[b"EVAL", b"return {double=3.5}", b"0"]), ",3.5\r\n");
5733        assert_eq!(
5734            f.run(&[b"EVAL", b"return {big_number='123'}", b"0"]),
5735            "(123\r\n"
5736        );
5737        assert_eq!(
5738            f.run(&[b"EVAL", b"return {map={a='b'}}", b"0"]),
5739            "%1\r\n$1\r\na\r\n$1\r\nb\r\n"
5740        );
5741        assert_eq!(
5742            f.run(&[b"EVAL", b"return {set={a=true}}", b"0"]),
5743            "~1\r\n$1\r\na\r\n"
5744        );
5745        assert_eq!(f.run(&[b"EVAL", b"return false", b"0"]), "_\r\n");
5746    }
5747
5748    #[test]
5749    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5750    fn a_reply_comes_back_into_lua_as_the_value_it_maps_to() {
5751        let mut f = Fixture::new();
5752        f.run(&[b"SET", b"s", b"hello"]);
5753        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
5754        let eval = |f: &mut Fixture, body: &[u8]| f.run(&[b"EVAL", body, b"0"]);
5755
5756        assert_eq!(
5757            eval(&mut f, b"return type(redis.call('get','s'))"),
5758            "$6\r\nstring\r\n"
5759        );
5760        assert_eq!(
5761            eval(&mut f, b"return type(redis.call('llen','l'))"),
5762            "$6\r\nnumber\r\n"
5763        );
5764        assert_eq!(
5765            eval(&mut f, b"return type(redis.call('lrange','l',0,-1))"),
5766            "$5\r\ntable\r\n"
5767        );
5768        // A status is a table with one field, which is what lets a script pass
5769        // one straight back out again.
5770        assert_eq!(
5771            eval(&mut f, b"return redis.call('set','s','v')['ok']"),
5772            "$2\r\nOK\r\n"
5773        );
5774        // A missing key is false under RESP2 and nil once the script asks for
5775        // RESP3, which is the one conversion the script gets to choose.
5776        assert_eq!(
5777            eval(&mut f, b"return tostring(redis.call('get','nosuch'))"),
5778            "$5\r\nfalse\r\n"
5779        );
5780        assert_eq!(
5781            eval(
5782                &mut f,
5783                b"redis.setresp(3) return tostring(redis.call('get','nosuch'))"
5784            ),
5785            "$3\r\nnil\r\n"
5786        );
5787        // The choice does not outlive the script that made it.
5788        assert_eq!(
5789            eval(&mut f, b"return tostring(redis.call('get','nosuch'))"),
5790            "$5\r\nfalse\r\n"
5791        );
5792    }
5793
5794    #[test]
5795    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5796    fn an_error_from_a_script_names_the_line_it_came_from() {
5797        let mut f = Fixture::new();
5798        // The position is the script's own, not the prelude's, and the suffix
5799        // names the script so a client can find it in the cache.
5800        assert_eq!(
5801            f.run(&[b"EVAL", b"error('boom')", b"0"]),
5802            "-ERR user_script:1: boom script: \
5803             82903a0434f1503e152f89c03c9acd881a0e8150, on @user_script:1.\r\n"
5804        );
5805        // Level zero says the message already knows where it came from.
5806        assert_eq!(
5807            f.run(&[b"EVAL", b"error('boom', 0)", b"0"]),
5808            "-ERR boom script: 90724e16396e5864c1184910ba6d7440461cee4f, on @user_script:1.\r\n"
5809        );
5810        // A table with an err field keeps its own text and gets the suffix.
5811        assert!(
5812            f.run(&[b"EVAL", b"error({err='structured'})", b"0"])
5813                .starts_with("-structured script: "),
5814        );
5815        // A script that will not parse is refused before it runs, so there is
5816        // no script and nothing to name.
5817        assert_eq!(
5818            f.run(&[b"EVAL", b"return this is not lua", b"0"]),
5819            "-ERR Error compiling script (new function): user_script:1: '<eof>' expected near 'is'\r\n"
5820        );
5821
5822        // A table that came out of pcall is a string by the time the script
5823        // sees it, which is a real server's own wrapping and not Lua's.
5824        assert_eq!(
5825            f.run(&[
5826                b"EVAL",
5827                b"local a, b = pcall(function() error({err='z'}) end) return type(b) .. ':' .. tostring(b)",
5828                b"0"
5829            ]),
5830            "$8\r\nstring:z\r\n"
5831        );
5832        assert_eq!(
5833            f.run(&[
5834                b"EVAL",
5835                b"local a, b = pcall(function() error({a=1}) end) return type(b)",
5836                b"0"
5837            ]),
5838            "$5\r\ntable\r\n"
5839        );
5840    }
5841
5842    #[test]
5843    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5844    fn redis_call_refuses_what_it_cannot_run_and_pcall_hands_it_back() {
5845        let mut f = Fixture::new();
5846        let sentence = |f: &mut Fixture, body: &[u8]| {
5847            let reply = f.run(&[b"EVAL", body, b"0"]);
5848            reply.split(" script: ").next().unwrap().to_owned()
5849        };
5850
5851        assert_eq!(
5852            sentence(&mut f, b"return redis.call()"),
5853            "-ERR Please specify at least one argument for this redis lib call"
5854        );
5855        assert_eq!(
5856            sentence(&mut f, b"return redis.call('get', {})"),
5857            "-ERR Lua redis lib command arguments must be strings or integers"
5858        );
5859        assert_eq!(
5860            sentence(&mut f, b"return redis.call('nosuchcmd')"),
5861            "-ERR Unknown Redis command called from script"
5862        );
5863        assert_eq!(
5864            sentence(&mut f, b"return redis.call('get')"),
5865            "-ERR Wrong number of args calling Redis command from script"
5866        );
5867        // The commands that make no sense inside a script are refused by name
5868        // rather than by not being implemented, so the sentence is the same one
5869        // a real server writes for each of them.
5870        for name in [
5871            &b"return redis.call('multi')"[..],
5872            b"return redis.call('exec')",
5873            b"return redis.call('watch','k')",
5874            b"return redis.call('subscribe','c')",
5875            b"return redis.call('debug','jmap')",
5876            b"return redis.call('eval','return 1',0)",
5877            b"return redis.call('config','get','maxmemory')",
5878        ] {
5879            assert_eq!(
5880                sentence(&mut f, name),
5881                "-ERR This Redis command is not allowed from script",
5882                "for {}",
5883                String::from_utf8_lossy(name)
5884            );
5885        }
5886        // HELP is the one subcommand of a refused container that is allowed,
5887        // because it reads nothing and changes nothing.
5888        assert!(
5889            f.run(&[b"EVAL", b"return redis.call('config','help')", b"0"])
5890                .starts_with('*'),
5891        );
5892
5893        // pcall answers the same sentence as a value instead of raising it, and
5894        // the value has an err field a script can read.
5895        assert_eq!(
5896            f.run(&[
5897                b"EVAL",
5898                b"local x = redis.pcall('nosuchcmd') return x.err",
5899                b"0"
5900            ]),
5901            "$44\r\nERR Unknown Redis command called from script\r\n"
5902        );
5903        // Returning it unread raises it, because the table has an err field.
5904        assert_eq!(
5905            f.run(&[b"EVAL", b"return redis.pcall('nosuchcmd')", b"0"]),
5906            "-ERR Unknown Redis command called from script\r\n"
5907        );
5908    }
5909
5910    #[test]
5911    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5912    fn a_read_only_script_is_stopped_at_the_write_and_not_at_the_door() {
5913        let mut f = Fixture::new();
5914        f.run(&[b"SET", b"k", b"v"]);
5915        assert_eq!(
5916            f.run(&[b"EVAL_RO", b"return redis.call('get', KEYS[1])", b"1", b"k"]),
5917            "$1\r\nv\r\n"
5918        );
5919        assert!(
5920            f.run(&[
5921                b"EVAL_RO",
5922                b"return redis.call('set', KEYS[1], 'x')",
5923                b"1",
5924                b"k"
5925            ])
5926            .starts_with("-ERR Write commands are not allowed from read-only scripts."),
5927        );
5928        // The write did not happen, and the same body under EVAL does.
5929        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
5930        assert_eq!(
5931            f.run(&[
5932                b"EVAL",
5933                b"return redis.call('set', KEYS[1], 'x')",
5934                b"1",
5935                b"k"
5936            ]),
5937            "+OK\r\n"
5938        );
5939        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nx\r\n");
5940
5941        // EVALSHA_RO runs a cached body under the same rule.
5942        let sha = b"e0e1f9fabfc9d4800c877a703b823ac0578ff8db";
5943        f.run(&[b"SCRIPT", b"LOAD", b"return 1"]);
5944        assert_eq!(f.run(&[b"EVALSHA_RO", sha, b"0"]), ":1\r\n");
5945    }
5946
5947    #[test]
5948    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
5949    fn a_script_cannot_leave_anything_behind_for_the_next_one() {
5950        let mut f = Fixture::new();
5951        // A plain global write and a write through a name on the redis table
5952        // both raise, with the position the script wrote them at.
5953        for body in [&b"x = 1"[..], b"pcall = 1", b"redis = 1", b"redis.call = 1"] {
5954            let reply = f.run(&[b"EVAL", body, b"0"]);
5955            assert!(
5956                reply
5957                    .starts_with("-ERR user_script:1: Attempt to modify a readonly table script: "),
5958                "{body:?} gave {reply}",
5959            );
5960        }
5961        // Walking round the guard with rawset or setmetatable raises too, and
5962        // without the position, which is where a real server raises it from.
5963        for body in [
5964            &b"rawset(redis, 'call', 1)"[..],
5965            b"rawset(_G, 'zz', 1)",
5966            b"setmetatable(_G, {})",
5967            b"setmetatable(redis, {})",
5968        ] {
5969            let reply = f.run(&[b"EVAL", body, b"0"]);
5970            assert!(
5971                reply.starts_with("-ERR Attempt to modify a readonly table script: "),
5972                "{body:?} gave {reply}",
5973            );
5974        }
5975        // Reading a name that is not there is a mistake rather than a nil, so a
5976        // misspelled global stops the script instead of doing nothing quietly.
5977        assert!(
5978            f.run(&[b"EVAL", b"return nosuchglobal", b"0"])
5979                .contains("Script attempted to access nonexistent global variable 'nosuchglobal'"),
5980        );
5981        // Reading a name that is not on the redis table is a nil, which is how
5982        // a script tests for a helper that an older server does not have.
5983        assert_eq!(
5984            f.run(&[b"EVAL", b"return tostring(redis.nosuchfield)", b"0"]),
5985            "$3\r\nnil\r\n"
5986        );
5987
5988        // The one write that lands, D-103, is taken back out before the next
5989        // script starts, so nothing a script does reaches the one after it.
5990        assert_eq!(f.run(&[b"EVAL", b"_G.pcall = 1 return 1", b"0"]), ":1\r\n");
5991        assert_eq!(
5992            f.run(&[b"EVAL", b"return type(pcall)", b"0"]),
5993            "$8\r\nfunction\r\n"
5994        );
5995        assert_eq!(
5996            f.run(&[b"EVAL", b"return type(redis.call)", b"0"]),
5997            "$8\r\nfunction\r\n"
5998        );
5999    }
6000
6001    #[test]
6002    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6003    fn a_script_can_walk_the_redis_table_it_is_not_allowed_to_write_to() {
6004        let mut f = Fixture::new();
6005        // The guard in front of the table is empty, so the three base library
6006        // readers that skip a metatable are pointed at the real table behind
6007        // it. A script counts what a real server counts.
6008        assert_eq!(
6009            f.run(&[
6010                b"EVAL",
6011                b"local n = 0 for k in pairs(redis) do n = n + 1 end return n",
6012                b"0",
6013            ]),
6014            ":23\r\n"
6015        );
6016        assert_eq!(
6017            f.run(&[
6018                b"EVAL",
6019                b"local t = {} for k in pairs(redis) do t[#t+1] = k end \
6020                  table.sort(t) return table.concat(t, ' ')",
6021                b"0",
6022            ]),
6023            "$243\r\nLOG_DEBUG LOG_NOTICE LOG_VERBOSE LOG_WARNING REDIS_VERSION \
6024             REDIS_VERSION_NUM REPL_ALL REPL_AOF REPL_NONE REPL_REPLICA REPL_SLAVE \
6025             acl_check_cmd breakpoint call debug error_reply log pcall replicate_commands \
6026             set_repl setresp sha1hex status_reply\r\n"
6027        );
6028        // The loop hands over the values as well as the names, so the twelve
6029        // helpers are callable from inside a traversal and not just findable.
6030        assert_eq!(
6031            f.run(&[
6032                b"EVAL",
6033                b"local n = 0 for k, v in pairs(redis) do \
6034                  if type(v) == 'function' then n = n + 1 end end return n",
6035                b"0",
6036            ]),
6037            ":12\r\n"
6038        );
6039        // The other two readers agree with it.
6040        assert_eq!(
6041            f.run(&[b"EVAL", b"return type(next(redis))", b"0"]),
6042            "$6\r\nstring\r\n"
6043        );
6044        assert_eq!(
6045            f.run(&[b"EVAL", b"return type(rawget(redis, 'call'))", b"0"]),
6046            "$8\r\nfunction\r\n"
6047        );
6048        assert_eq!(
6049            f.run(&[
6050                b"EVAL",
6051                b"return tostring(rawget(redis, 'nosuchfield'))",
6052                b"0",
6053            ]),
6054            "$3\r\nnil\r\n"
6055        );
6056        // Reading round the guard is the only thing that was given back. A
6057        // write still lands on the guard and still raises.
6058        for body in [&b"redis.call = 1"[..], b"rawset(redis, 'call', 1)"] {
6059            assert!(
6060                f.run(&[b"EVAL", body, b"0"])
6061                    .contains("Attempt to modify a readonly table script: "),
6062                "{body:?}",
6063            );
6064        }
6065        // A table nobody guards walks the way it always did, whether a script
6066        // made it or the standard library did.
6067        assert_eq!(
6068            f.run(&[
6069                b"EVAL",
6070                b"local t = {a=1,b=2} local n = 0 for k in pairs(t) do n = n + 1 end return n",
6071                b"0",
6072            ]),
6073            ":2\r\n"
6074        );
6075        assert_eq!(
6076            f.run(&[b"EVAL", b"return tostring(next({}))", b"0"]),
6077            "$3\r\nnil\r\n"
6078        );
6079        assert_eq!(
6080            f.run(&[
6081                b"EVAL",
6082                b"local f for k, v in pairs(string) do if k == 'sub' then f = v end end \
6083                  return type(f)",
6084                b"0",
6085            ]),
6086            "$8\r\nfunction\r\n"
6087        );
6088    }
6089
6090    #[test]
6091    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6092    fn a_script_gets_the_bit_library_a_real_server_carries() {
6093        let mut f = Fixture::new();
6094        // Every answer is a signed word, which is why the ones past two to the
6095        // thirty one come back negative.
6096        for (body, want) in [
6097            ("bit.tobit(1)", ":1\r\n"),
6098            ("bit.tobit(2^32 + 1)", ":1\r\n"),
6099            ("bit.tobit(2^31)", ":-2147483648\r\n"),
6100            ("bit.tobit(0xffffffff)", ":-1\r\n"),
6101            // The rounding is to the nearest and not toward zero.
6102            ("bit.tobit(1.5)", ":2\r\n"),
6103            ("bit.tobit(2.5)", ":2\r\n"),
6104            ("bit.bnot(0)", ":-1\r\n"),
6105            ("bit.band(0xff, 0x0f)", ":15\r\n"),
6106            ("bit.band(1, 2, 3)", ":0\r\n"),
6107            ("bit.bor(1, 2, 4)", ":7\r\n"),
6108            ("bit.bxor(0xff, 0x0f)", ":240\r\n"),
6109            // Only the low five bits of a count are read.
6110            ("bit.lshift(1, 31)", ":-2147483648\r\n"),
6111            ("bit.lshift(1, 32)", ":1\r\n"),
6112            ("bit.lshift(1, 33)", ":2\r\n"),
6113            ("bit.rshift(-1, 1)", ":2147483647\r\n"),
6114            ("bit.arshift(-1, 1)", ":-1\r\n"),
6115            ("bit.rol(0x12345678, 8)", ":878082066\r\n"),
6116            ("bit.ror(0x12345678, 8)", ":2014458966\r\n"),
6117            ("bit.bswap(0x12345678)", ":2018915346\r\n"),
6118            // A string that reads as a number is a number, which is Lua's rule
6119            // and not a courtesy of this library.
6120            ("bit.tobit('0x10')", ":16\r\n"),
6121        ] {
6122            let script = format!("return {body}");
6123            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6124        }
6125        // The digits are the low ones, a negative count asks for upper case,
6126        // and a count outside eight is brought back to it.
6127        for (body, want) in [
6128            ("bit.tohex(1)", "00000001"),
6129            ("bit.tohex(-1)", "ffffffff"),
6130            ("bit.tohex(255, 2)", "ff"),
6131            ("bit.tohex(255, -8)", "000000FF"),
6132            ("bit.tohex(0x87654321, 4)", "4321"),
6133            ("bit.tohex(1, 0)", ""),
6134            ("bit.tohex(1, 9)", "00000001"),
6135        ] {
6136            let script = format!("return {body}");
6137            assert_eq!(
6138                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6139                format!("${}\r\n{want}\r\n", want.len()),
6140                "{body}",
6141            );
6142        }
6143        // A bad argument names the position, the function and what was passed,
6144        // and the line in front of it is the script's own.
6145        for (body, want) in [
6146            (
6147                "return bit.band()",
6148                "bad argument #1 to 'band' (number expected, got no value)",
6149            ),
6150            (
6151                "return bit.band('x')",
6152                "bad argument #1 to 'band' (number expected, got string)",
6153            ),
6154            (
6155                "return bit.tobit(true)",
6156                "bad argument #1 to 'tobit' (number expected, got boolean)",
6157            ),
6158            (
6159                "return bit.lshift(1)",
6160                "bad argument #2 to 'lshift' (number expected, got no value)",
6161            ),
6162        ] {
6163            let reply = f.run(&[b"EVAL", body.as_bytes(), b"0"]);
6164            assert!(
6165                reply.starts_with(&format!("-ERR user_script:1: {want} script: ")),
6166                "{body} gave {reply}",
6167            );
6168        }
6169        // The name in the message is the one the call site used, so a call that
6170        // went through `pcall` has no name to report.
6171        assert_eq!(
6172            f.run(&[
6173                b"EVAL",
6174                b"local ok, e = pcall(bit.band, 'x') return tostring(e)",
6175                b"0",
6176            ]),
6177            "$52\r\nbad argument #1 to '?' (number expected, got string)\r\n"
6178        );
6179        // The table is readable and not writable, the same as `redis`.
6180        assert_eq!(
6181            f.run(&[
6182                b"EVAL",
6183                b"local t = {} for k in pairs(bit) do t[#t+1] = k end \
6184                  table.sort(t) return table.concat(t, ' ')",
6185                b"0",
6186            ]),
6187            "$66\r\narshift band bnot bor bswap bxor lshift rol ror rshift tobit tohex\r\n"
6188        );
6189        for body in [&b"bit.band = 1"[..], b"rawset(bit, 'zz', 1)"] {
6190            assert!(
6191                f.run(&[b"EVAL", body, b"0"])
6192                    .contains("Attempt to modify a readonly table script: "),
6193                "{body:?}",
6194            );
6195        }
6196    }
6197
6198    #[test]
6199    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6200    fn a_script_gets_the_cjson_library_a_real_server_carries() {
6201        let mut f = Fixture::new();
6202        // Encoding, including the three shapes nobody guesses right: an empty
6203        // table is an object, a number is fourteen significant digits, and a
6204        // hole in an array is a null rather than a shorter array.
6205        for (body, want) in [
6206            ("cjson.encode(nil)", "null"),
6207            ("cjson.encode(true)", "true"),
6208            ("cjson.encode(cjson.null)", "null"),
6209            ("cjson.encode(100)", "100"),
6210            ("cjson.encode(1/3)", "0.33333333333333"),
6211            ("cjson.encode(1e300)", "1e+300"),
6212            ("cjson.encode(2^53)", "9.007199254741e+15"),
6213            ("cjson.encode({})", "{}"),
6214            ("cjson.encode({1,2,3})", "[1,2,3]"),
6215            ("cjson.encode({a=1})", "{\"a\":1}"),
6216            ("cjson.encode({[1]=1,[3]=3})", "[1,null,3]"),
6217            ("cjson.encode({[0]=1})", "{\"0\":1}"),
6218            ("cjson.encode('a\\nb')", "\"a\\nb\""),
6219            // A tab and a backslash have short escapes, a vertical tab does not.
6220            ("cjson.encode('\\t\\\\')", "\"\\t\\\\\""),
6221            ("cjson.encode('\\11')", "\"\\u000b\""),
6222            // Reading and writing again is the shortest way to say the decoder
6223            // built what the encoder expected.
6224            (
6225                "cjson.encode(cjson.decode('[1,[2,{\"a\":null}]]'))",
6226                "[1,[2,{\"a\":null}]]",
6227            ),
6228            // An empty array comes back as an object, because a table with
6229            // nothing in it has nothing to say about which it was.
6230            ("cjson.encode(cjson.decode('[]'))", "{}"),
6231        ] {
6232            let script = format!("return {body}");
6233            assert_eq!(
6234                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6235                format!("${}\r\n{want}\r\n", want.len()),
6236                "{body}",
6237            );
6238        }
6239        // Decoding, where the leniency about numbers is on by default and a
6240        // null is a value of its own rather than a missing key.
6241        for (body, want) in [
6242            ("cjson.decode('[1,2,3]')[2]", ":2\r\n"),
6243            ("cjson.decode('{\"a\":41}').a + 1", ":42\r\n"),
6244            ("cjson.decode('0x10')", ":16\r\n"),
6245            ("cjson.decode('+1')", ":1\r\n"),
6246            ("cjson.decode('01')", ":1\r\n"),
6247            ("cjson.decode(1) + 1", ":2\r\n"),
6248            // A long bracket, because Lua 5.1 would eat the backslash first.
6249            ("cjson.decode([[\"\\u0041\"]]) == 'A' and 1 or 0", ":1\r\n"),
6250            ("cjson.decode('null') == cjson.null and 1 or 0", ":1\r\n"),
6251            ("cjson.decode('null') == nil and 1 or 0", ":0\r\n"),
6252        ] {
6253            let script = format!("return {body}");
6254            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6255        }
6256        // The settings, each of which answers with what it now holds.
6257        for (body, want) in [
6258            (
6259                "cjson.encode_number_precision(3) return cjson.encode(1/3)",
6260                "0.333",
6261            ),
6262            (
6263                "cjson.encode_invalid_numbers('null') return cjson.encode(1/0)",
6264                "null",
6265            ),
6266            (
6267                "cjson.encode_invalid_numbers(true) return cjson.encode(1/0)",
6268                "inf",
6269            ),
6270            (
6271                "cjson.encode_sparse_array(true) return cjson.encode({[1]=1,[100]=1})",
6272                "{\"1\":1,\"100\":1}",
6273            ),
6274            (
6275                "cjson.decode_array_with_array_mt(true) return cjson.encode(cjson.decode('[]'))",
6276                "[]",
6277            ),
6278            ("return tostring(cjson.encode_max_depth())", "1000"),
6279            ("return tostring(cjson.encode_keep_buffer(false))", "false"),
6280            ("return tostring(cjson.encode_sparse_array())", "false"),
6281            // A setting one script changed is not a setting the next one sees,
6282            // which is D-105.
6283            ("return tostring(cjson.encode_number_precision())", "14"),
6284        ] {
6285            assert_eq!(
6286                f.run(&[b"EVAL", body.as_bytes(), b"0"]),
6287                format!("${}\r\n{want}\r\n", want.len()),
6288                "{body}",
6289            );
6290        }
6291        // A failure names what stopped it and, when it was the text, where.
6292        for (body, want) in [
6293            (
6294                "return cjson.encode(1/0)",
6295                "Cannot serialise number: must not be NaN or Inf",
6296            ),
6297            (
6298                "return cjson.encode({[1]=1,[100]=1})",
6299                "Cannot serialise table: excessively sparse array",
6300            ),
6301            (
6302                "return cjson.encode({[true]=1})",
6303                "Cannot serialise boolean: table key must be a number or string",
6304            ),
6305            (
6306                "return cjson.encode(tostring)",
6307                "Cannot serialise function: type not supported",
6308            ),
6309            (
6310                "return cjson.encode()",
6311                "bad argument #1 to 'encode' (expected 1 argument)",
6312            ),
6313            (
6314                "return cjson.decode('[1,2')",
6315                "Expected comma or array end but found T_END at character 5",
6316            ),
6317            (
6318                "return cjson.decode('{\"a\" 1}')",
6319                "Expected colon but found T_NUMBER at character 6",
6320            ),
6321            (
6322                "return cjson.decode('tru')",
6323                "Expected value but found invalid token at character 1",
6324            ),
6325            (
6326                "return cjson.decode('[1] 2')",
6327                "Expected the end but found T_NUMBER at character 5",
6328            ),
6329            (
6330                "return cjson.encode_max_depth(0)",
6331                "bad argument #1 to 'encode_max_depth' (expected integer between 1 and 2147483647)",
6332            ),
6333            (
6334                "return cjson.encode_invalid_numbers('yes')",
6335                "bad argument #1 to 'encode_invalid_numbers' (invalid option 'yes')",
6336            ),
6337            (
6338                "return cjson.encode_max_depth(1, 2)",
6339                "bad argument #2 to 'encode_max_depth' (found too many arguments)",
6340            ),
6341        ] {
6342            let reply = f.run(&[b"EVAL", body.as_bytes(), b"0"]);
6343            assert!(
6344                reply.starts_with(&format!("-ERR user_script:1: {want} script: ")),
6345                "{body} gave {reply}",
6346            );
6347        }
6348        // A module of its own, with settings of its own and no guard on it,
6349        // which is what a real server hands back.
6350        assert_eq!(
6351            f.run(&[
6352                b"EVAL",
6353                b"local n = cjson.new() n.encode_number_precision(3) \
6354                  return cjson.encode(1/3) .. ' ' .. n.encode(1/3)",
6355                b"0",
6356            ]),
6357            "$22\r\n0.33333333333333 0.333\r\n"
6358        );
6359        // The table is readable and not writable, the same as `redis`.
6360        let names = "_NAME _VERSION decode decode_array_with_array_mt decode_invalid_numbers \
6361                     decode_max_depth encode encode_invalid_numbers encode_keep_buffer \
6362                     encode_max_depth encode_number_precision encode_sparse_array new null";
6363        assert_eq!(
6364            f.run(&[
6365                b"EVAL",
6366                b"local t = {} for k in pairs(cjson) do t[#t+1] = k end \
6367                  table.sort(t) return table.concat(t, ' ')",
6368                b"0",
6369            ]),
6370            format!("${}\r\n{names}\r\n", names.len())
6371        );
6372        for body in [&b"cjson.encode = 1"[..], b"rawset(cjson, 'zz', 1)"] {
6373            assert!(
6374                f.run(&[b"EVAL", body, b"0"])
6375                    .contains("Attempt to modify a readonly table script: "),
6376                "{body:?}",
6377            );
6378        }
6379    }
6380
6381    #[test]
6382    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6383    fn a_script_gets_the_struct_library_a_real_server_carries() {
6384        let mut f = Fixture::new();
6385        // Packing, where the sizes are the ones a sixty four bit build gives
6386        // and the order is the machine's own unless the format says otherwise.
6387        for (body, want) in [
6388            ("#struct.pack('i4', 1)", ":4\r\n"),
6389            ("#struct.pack('l', 1)", ":8\r\n"),
6390            ("#struct.pack('d', 1)", ":8\r\n"),
6391            ("#struct.pack('f', 1)", ":4\r\n"),
6392            ("#struct.pack('s', 'abc')", ":4\r\n"),
6393            ("#struct.pack('c3', 'abcdef')", ":3\r\n"),
6394            ("#struct.pack('x')", ":1\r\n"),
6395            ("string.byte(struct.pack('i4', 1), 1)", ":1\r\n"),
6396            ("string.byte(struct.pack('>i4', 1), 4)", ":1\r\n"),
6397            ("string.byte(struct.pack('<i4', 1), 1)", ":1\r\n"),
6398            // Past eight bytes the C shifts an unsigned long off the end, so
6399            // the rest of the bytes are zero and a negative is not carried.
6400            ("string.byte(struct.pack('i16', -1), 9)", ":0\r\n"),
6401            ("string.byte(struct.pack('i8', -1), 8)", ":255\r\n"),
6402            // A count of zero on `c` writes the whole string, `s` adds the
6403            // terminator, and `x` writes a zero byte nobody reads back.
6404            ("#struct.pack('c0', 'abcd')", ":4\r\n"),
6405            ("string.byte(struct.pack('s', 'a'), 2)", ":0\r\n"),
6406            ("string.byte(struct.pack('bxb', 1, 2), 2)", ":0\r\n"),
6407        ] {
6408            let script = format!("return {body}");
6409            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6410        }
6411        // Sizes, including the two the C is lenient about: an unknown letter
6412        // and a bare digit are both nothing at all rather than a complaint.
6413        for (body, want) in [
6414            ("struct.size('i')", ":4\r\n"),
6415            ("struct.size('l')", ":8\r\n"),
6416            ("struct.size('T')", ":8\r\n"),
6417            ("struct.size('h')", ":2\r\n"),
6418            ("struct.size('c10')", ":10\r\n"),
6419            ("struct.size('ic')", ":5\r\n"),
6420            ("struct.size('!8ic')", ":5\r\n"),
6421            ("struct.size('!4i')", ":4\r\n"),
6422            // Nothing is padded until `!` turns alignment on, and then a
6423            // double is pushed out to the next eight byte boundary.
6424            ("struct.size('bd')", ":9\r\n"),
6425            ("struct.size('!bd')", ":16\r\n"),
6426            ("struct.size('A')", ":0\r\n"),
6427            ("struct.size('7')", ":0\r\n"),
6428        ] {
6429            let script = format!("return {body}");
6430            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6431        }
6432        // Unpacking, which hands back the values and then where it stopped, so
6433        // the last number can be passed straight back in as the next offset.
6434        for (body, want) in [
6435            ("select('#', struct.unpack('i4', '\\1\\0\\0\\0'))", ":2\r\n"),
6436            ("select(1, struct.unpack('i4', '\\1\\0\\0\\0'))", ":1\r\n"),
6437            ("select(2, struct.unpack('i4', '\\1\\0\\0\\0'))", ":5\r\n"),
6438            ("select(1, struct.unpack('i1', '\\255'))", ":-1\r\n"),
6439            ("select(1, struct.unpack('I1', '\\255'))", ":255\r\n"),
6440            (
6441                "select(1, struct.unpack('i4', struct.pack('i4', -70000)))",
6442                ":-70000\r\n",
6443            ),
6444            ("select(2, struct.unpack('i1', 'abc', 2))", ":3\r\n"),
6445            // A `c0` takes its length from the value read just before it and
6446            // swallows it, so one byte says how long the next three are and
6447            // only the string and the position come back.
6448            ("select('#', struct.unpack('bc0', '\\3abcd'))", ":2\r\n"),
6449            ("select(2, struct.unpack('bc0', '\\3abcd'))", ":5\r\n"),
6450        ] {
6451            let script = format!("return {body}");
6452            assert_eq!(f.run(&[b"EVAL", script.as_bytes(), b"0"]), want, "{body}");
6453        }
6454        for (body, want) in [
6455            ("select(1, struct.unpack('bc0', '\\3abcd'))", "abc"),
6456            ("select(1, struct.unpack('s', 'ab\\0cd'))", "ab"),
6457            ("select(1, struct.unpack('c3', 'abcdef'))", "abc"),
6458        ] {
6459            let script = format!("return {body}");
6460            assert_eq!(
6461                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6462                format!("${}\r\n{want}\r\n", want.len()),
6463                "{body}",
6464            );
6465        }
6466        // A failure names the argument the C names, which is not always the
6467        // argument a reader would pick.
6468        for (body, want) in [
6469            (
6470                "return struct.pack()",
6471                "bad argument #1 to 'pack' (string expected, got no value)",
6472            ),
6473            // The C pushes a nil before it reads anything, so a missing value
6474            // is a nil rather than nothing at all.
6475            (
6476                "return struct.pack('i4')",
6477                "bad argument #2 to 'pack' (number expected, got nil)",
6478            ),
6479            // And it reads the string with a post increment before it checks
6480            // the length, so the number here is one past the real argument.
6481            (
6482                "return struct.pack('c6', 'abc')",
6483                "bad argument #3 to 'pack' (string too short)",
6484            ),
6485            (
6486                "return struct.pack('A', 'x')",
6487                "bad argument #1 to 'pack' (invalid format option 'A')",
6488            ),
6489            (
6490                "return struct.pack('i33', 1)",
6491                "integral size 33 is larger than limit of 32",
6492            ),
6493            (
6494                "return struct.pack('!3i', 1)",
6495                "alignment 3 is not a power of 2",
6496            ),
6497            (
6498                "return struct.unpack()",
6499                "bad argument #1 to 'unpack' (string expected, got no value)",
6500            ),
6501            (
6502                "return struct.unpack('i4')",
6503                "bad argument #2 to 'unpack' (string expected, got no value)",
6504            ),
6505            (
6506                "return struct.unpack('i4', 'ab')",
6507                "bad argument #2 to 'unpack' (data string too short)",
6508            ),
6509            (
6510                "return struct.unpack('i1', 'abc', 0)",
6511                "bad argument #3 to 'unpack' (offset must be 1 or greater)",
6512            ),
6513            (
6514                "return struct.unpack('c0', 'abc')",
6515                "format 'c0' needs a previous size",
6516            ),
6517            (
6518                "return struct.unpack('s', 'abc')",
6519                "unfinished string in data",
6520            ),
6521            (
6522                "return struct.size()",
6523                "bad argument #1 to 'size' (string expected, got no value)",
6524            ),
6525            (
6526                "return struct.size('s')",
6527                "bad argument #1 to 'size' (option 's' has no fixed size)",
6528            ),
6529            (
6530                "return struct.size('c0')",
6531                "bad argument #1 to 'size' (option 'c0' has no fixed size)",
6532            ),
6533        ] {
6534            let reply = f.run(&[b"EVAL", body.as_bytes(), b"0"]);
6535            assert!(
6536                reply.starts_with(&format!("-ERR user_script:1: {want} script: ")),
6537                "{body} gave {reply}",
6538            );
6539        }
6540        // Three members and no version, which is all the C registers.
6541        let names = "pack size unpack";
6542        assert_eq!(
6543            f.run(&[
6544                b"EVAL",
6545                b"local t = {} for k in pairs(struct) do t[#t+1] = k end \
6546                  table.sort(t) return table.concat(t, ' ')",
6547                b"0",
6548            ]),
6549            format!("${}\r\n{names}\r\n", names.len())
6550        );
6551        for body in [&b"struct.pack = 1"[..], b"rawset(struct, 'zz', 1)"] {
6552            assert!(
6553                f.run(&[b"EVAL", body, b"0"])
6554                    .contains("Attempt to modify a readonly table script: "),
6555                "{body:?}",
6556            );
6557        }
6558    }
6559
6560    #[test]
6561    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6562    fn a_script_gets_the_cmsgpack_library_a_real_server_carries() {
6563        let mut f = Fixture::new();
6564        // Every value goes out in the shortest form that holds it, and several
6565        // arguments are packed one after another into one string.
6566        let hex = "local function hx(s) return (string.gsub(s, '.', \
6567                   function(c) return string.format('%02x', string.byte(c)) end)) end ";
6568        for (body, want) in [
6569            ("cmsgpack.pack(nil)", "c0"),
6570            ("cmsgpack.pack(true)", "c3"),
6571            ("cmsgpack.pack(false)", "c2"),
6572            ("cmsgpack.pack(0)", "00"),
6573            ("cmsgpack.pack(127)", "7f"),
6574            ("cmsgpack.pack(128)", "cc80"),
6575            ("cmsgpack.pack(-1)", "ff"),
6576            ("cmsgpack.pack(-33)", "d0df"),
6577            ("cmsgpack.pack(65535)", "cdffff"),
6578            ("cmsgpack.pack(4294967296)", "cf0000000100000000"),
6579            ("cmsgpack.pack(2^53)", "cf0020000000000000"),
6580            ("cmsgpack.pack(-2^63)", "d38000000000000000"),
6581            // Past what an integer holds it is a number again, and a number
6582            // goes out narrow whenever four bytes give it back unchanged.
6583            ("cmsgpack.pack(2^64)", "ca5f800000"),
6584            ("cmsgpack.pack(1.5)", "ca3fc00000"),
6585            ("cmsgpack.pack(0.1)", "cb3fb999999999999a"),
6586            ("cmsgpack.pack('abc')", "a3616263"),
6587            ("cmsgpack.pack('')", "a0"),
6588            ("cmsgpack.pack({})", "90"),
6589            ("cmsgpack.pack({1, 2})", "920102"),
6590            ("cmsgpack.pack({a = 1})", "81a16101"),
6591            ("cmsgpack.pack(1, 'a', true)", "01a161c3"),
6592            // Sixteen levels of table are packed and the seventeenth is a nil,
6593            // which is what the C does rather than refusing the whole thing.
6594            (
6595                "(function() local t = {} local c = t \
6596                 for i = 1, 20 do c.n = {} c = c.n end return cmsgpack.pack(t) end)()",
6597                "81a16e81a16e81a16e81a16e81a16e81a16e81a16e81a16e\
6598                 81a16e81a16e81a16e81a16e81a16e81a16e81a16e81a16ec0",
6599            ),
6600        ] {
6601            let script = format!("{hex} return hx({body})");
6602            assert_eq!(
6603                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6604                format!("${}\r\n{want}\r\n", want.len()),
6605                "{body}",
6606            );
6607        }
6608        // Unpacking reads the whole stream, so a string holding three values
6609        // hands back three. The two that take an offset put where they got to
6610        // in front of the values, and answer minus one when nothing is left.
6611        for (body, want) in [
6612            ("cmsgpack.unpack(cmsgpack.pack(42))", 42),
6613            ("select('#', cmsgpack.unpack('\\1\\2\\3'))", 3),
6614            ("select(3, cmsgpack.unpack('\\1\\2\\3'))", 3),
6615            ("select('#', cmsgpack.unpack(''))", 0),
6616            ("select('#', cmsgpack.unpack_one('\\1\\2\\3'))", 2),
6617            ("select(1, cmsgpack.unpack_one('\\1\\2\\3'))", 1),
6618            ("select(2, cmsgpack.unpack_one('\\1\\2\\3'))", 1),
6619            ("select(1, cmsgpack.unpack_one('\\1\\2\\3', 2))", -1),
6620            ("select(1, cmsgpack.unpack_one('\\1'))", -1),
6621            ("select(1, cmsgpack.unpack_one('', 0))", -1),
6622            ("select('#', cmsgpack.unpack_limit('\\1\\2\\3', 2))", 3),
6623            ("select(1, cmsgpack.unpack_limit('\\1\\2\\3', 2))", 2),
6624            // A limit of nothing at all takes the read everything path, which
6625            // has no offset in front of it.
6626            ("select('#', cmsgpack.unpack_limit('\\1\\2\\3', 0, 0))", 3),
6627            ("cmsgpack.unpack(cmsgpack.pack({1, 2, 3}))[2]", 2),
6628        ] {
6629            let script = format!("return {body}");
6630            assert_eq!(
6631                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6632                format!(":{want}\r\n"),
6633                "{body}",
6634            );
6635        }
6636        for (body, want) in [
6637            ("cmsgpack.unpack(cmsgpack.pack({a = 'b'})).a", "b"),
6638            ("tostring(cmsgpack.unpack(cmsgpack.pack(1.5)))", "1.5"),
6639            ("tostring(cmsgpack.unpack(cmsgpack.pack(nil)))", "nil"),
6640            (
6641                "tostring(cmsgpack.unpack(string.char(0xcb, 0x7f, 0xf0, 0, 0, 0, 0, 0, 0)))",
6642                "inf",
6643            ),
6644            ("cmsgpack._NAME", "cmsgpack"),
6645            ("cmsgpack._VERSION", "lua-cmsgpack 0.4.0"),
6646            (
6647                "cmsgpack._COPYRIGHT",
6648                "Copyright (C) 2012, Salvatore Sanfilippo",
6649            ),
6650            (
6651                "cmsgpack._DESCRIPTION",
6652                "MessagePack C implementation for Lua",
6653            ),
6654        ] {
6655            let script = format!("return {body}");
6656            assert_eq!(
6657                f.run(&[b"EVAL", script.as_bytes(), b"0"]),
6658                format!("${}\r\n{want}\r\n", want.len()),
6659                "{body}",
6660            );
6661        }
6662        for (body, want) in [
6663            // The C counts the arguments before it reads any of them, so the
6664            // one it names when there are none is the one before the first.
6665            (
6666                "return cmsgpack.pack()",
6667                "bad argument #0 to 'pack' (MessagePack pack needs input.)",
6668            ),
6669            (
6670                "return cmsgpack.unpack()",
6671                "bad argument #1 to 'unpack' (string expected, got no value)",
6672            ),
6673            (
6674                "return cmsgpack.unpack(string.char(193))",
6675                "Bad data format in input.",
6676            ),
6677            (
6678                "return cmsgpack.unpack(string.char(204))",
6679                "Missing bytes in input.",
6680            ),
6681            (
6682                "return cmsgpack.unpack(string.char(146, 1))",
6683                "Missing bytes in input.",
6684            ),
6685            (
6686                "return cmsgpack.unpack_one('\\1', 5)",
6687                "Start offset 5 greater than input length 1.",
6688            ),
6689            (
6690                "return cmsgpack.unpack_limit('\\1\\2', 1, 5)",
6691                "Start offset 5 greater than input length 2.",
6692            ),
6693            // The second number here is the length of the input rather than
6694            // the limit, which is a mixed up argument in the C kept on purpose.
6695            (
6696                "return cmsgpack.unpack_one('\\1', -1)",
6697                "Invalid request to unpack with offset of -1 and limit of 1.",
6698            ),
6699            (
6700                "return cmsgpack.unpack_limit('\\1', -1, 0)",
6701                "Invalid request to unpack with offset of 0 and limit of 1.",
6702            ),
6703        ] {
6704            let reply = f.run(&[b"EVAL", body.as_bytes(), b"0"]);
6705            assert!(
6706                reply.starts_with(&format!("-ERR user_script:1: {want} script: ")),
6707                "{body} gave {reply}",
6708            );
6709        }
6710        // Four calls and the four names the C sets on the table beside them.
6711        let names = "_COPYRIGHT _DESCRIPTION _NAME _VERSION pack unpack unpack_limit unpack_one";
6712        assert_eq!(
6713            f.run(&[
6714                b"EVAL",
6715                b"local t = {} for k in pairs(cmsgpack) do t[#t+1] = k end \
6716                  table.sort(t) return table.concat(t, ' ')",
6717                b"0",
6718            ]),
6719            format!("${}\r\n{names}\r\n", names.len())
6720        );
6721        for body in [&b"cmsgpack.pack = 1"[..], b"rawset(cmsgpack, 'zz', 1)"] {
6722            assert!(
6723                f.run(&[b"EVAL", body, b"0"])
6724                    .contains("Attempt to modify a readonly table script: "),
6725                "{body:?}",
6726            );
6727        }
6728        // A library is a table like any other from a script's side, so packing
6729        // one walks its members rather than finding the guard in front empty.
6730        assert_eq!(
6731            f.run(&[
6732                b"EVAL",
6733                b"return cmsgpack.unpack(cmsgpack.pack(cmsgpack))._NAME",
6734                b"0",
6735            ]),
6736            "$8\r\ncmsgpack\r\n"
6737        );
6738    }
6739
6740    /// The library used by most of the function tests below.
6741    ///
6742    /// Written out once because every one of them wants a library that has
6743    /// something to call, and because the line numbers in the failures a couple
6744    /// of them check are line numbers in this.
6745    const LIB: &[u8] = b"#!lua name=mylib\n\
6746        local counter = 0\n\
6747        redis.register_function{function_name = 'ping', description = 'says pong',\n\
6748        callback = function(keys, args) return 'pong' end, flags = {'no-writes'}}\n\
6749        redis.register_function('count', function() counter = counter + 1 return counter end)\n\
6750        redis.register_function('echo', function(keys, args) return {keys, args} end)\n\
6751        redis.register_function('setit', function(keys, args) \
6752        return redis.call('SET', keys[1], args[1]) end)\n\
6753        redis.register_function('raise', function() error('boom') end)\n";
6754
6755    /// A second library, for the tests that need two of them.
6756    const OTHER: &[u8] = b"#!lua name=other\n\
6757        redis.register_function('twice', function(keys, args) return 2 end)\n";
6758
6759    #[test]
6760    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6761    fn a_library_is_loaded_once_and_called_by_name_forever_after() {
6762        let mut f = Fixture::new();
6763        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
6764        assert_eq!(f.run(&[b"FCALL", b"ping", b"0"]), "$4\r\npong\r\n");
6765        // The dictionary FCALL looks in is one for the whole server and it does
6766        // not care about case, which is why this finds the same function.
6767        assert_eq!(f.run(&[b"FCALL", b"PiNg", b"0"]), "$4\r\npong\r\n");
6768        // Keys and arguments arrive as the two arguments of the callback rather
6769        // than as globals, and a function that reads KEYS is reading a name
6770        // that is not there.
6771        assert_eq!(
6772            f.run(&[b"FCALL", b"echo", b"1", b"k", b"a", b"b"]),
6773            "*2\r\n*1\r\n$1\r\nk\r\n*2\r\n$1\r\na\r\n$1\r\nb\r\n"
6774        );
6775        assert_eq!(f.run(&[b"FCALL", b"setit", b"1", b"s", b"v"]), "+OK\r\n");
6776        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
6777        // A library's own local outlives the call that made it, which is the
6778        // whole reason a library is not a script.
6779        assert_eq!(f.run(&[b"FCALL", b"count", b"0"]), ":1\r\n");
6780        assert_eq!(f.run(&[b"FCALL", b"count", b"0"]), ":2\r\n");
6781        // The name a failure ends with is the function's, where a script's is
6782        // its digest, and the line is a line in the library.
6783        assert_eq!(
6784            f.run(&[b"FCALL", b"raise", b"0"]),
6785            "-ERR user_function:8: boom script: raise, on @user_function:8.\r\n"
6786        );
6787        // Deleting is by the exact name, so the upper case spelling that found
6788        // the function a moment ago does not find the library.
6789        assert_eq!(
6790            f.run(&[b"FUNCTION", b"DELETE", b"MYLIB"]),
6791            "-ERR Library not found\r\n"
6792        );
6793        assert_eq!(f.run(&[b"FUNCTION", b"DELETE", b"mylib"]), "+OK\r\n");
6794        assert_eq!(
6795            f.run(&[b"FCALL", b"ping", b"0"]),
6796            "-ERR Function not found\r\n"
6797        );
6798    }
6799
6800    #[test]
6801    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6802    fn a_library_that_is_wrong_says_which_way_it_is_wrong() {
6803        let mut f = Fixture::new();
6804        for (code, want) in [
6805            (&b"return 1"[..], "ERR Missing library metadata"),
6806            (b"#!lua name=x", "ERR Invalid library metadata"),
6807            (b"#!\n", "ERR Library name was not given"),
6808            (b"#!lua\nx", "ERR Library name was not given"),
6809            (
6810                b"#!lua name=a name=b\nx",
6811                "ERR Invalid metadata value, name argument was given multiple times",
6812            ),
6813            (
6814                b"#!lua nome=a\nx",
6815                "ERR Invalid metadata value given: nome=a",
6816            ),
6817            (b"#!lua name=\"q\nx", "ERR Invalid library metadata"),
6818            (
6819                b"#!lua name=a-b\nx",
6820                "ERR Library names can only contain letters, numbers, or underscores(_) \
6821                 and must be at least one character long",
6822            ),
6823            (b"#!zz name=x\nx", "ERR Engine 'zz' not found"),
6824            (
6825                b"#!lua name=c\nthis is not lua",
6826                "ERR Error compiling function: user_function:2: '=' expected near 'is'",
6827            ),
6828            // Nothing at all is on the global table during a load except one
6829            // table with eight names on it, so `error` is as absent as anything
6830            // a library misspelled would be.
6831            (
6832                b"#!lua name=r\nerror('boom')",
6833                "ERR Error registering functions: ERR user_function:2: \
6834                 Script attempted to access nonexistent global variable 'error'",
6835            ),
6836            // And `redis` is there but `redis.call` is not, so the name the
6837            // complaint gives is `call` and not `redis`.
6838            (
6839                b"#!lua name=r\nredis.call('PING')",
6840                "ERR Error registering functions: ERR user_function:2: \
6841                 Script attempted to access nonexistent global variable 'call'",
6842            ),
6843            (
6844                b"#!lua name=r\nx = 1",
6845                "ERR Error registering functions: ERR user_function:2: \
6846                 Attempt to modify a readonly table",
6847            ),
6848            (b"#!lua name=n\nlocal x = 1", "ERR No functions registered"),
6849        ] {
6850            assert_eq!(
6851                f.run(&[b"FUNCTION", b"LOAD", code]),
6852                format!("-{want}\r\n"),
6853                "{}",
6854                String::from_utf8_lossy(code),
6855            );
6856        }
6857    }
6858
6859    #[test]
6860    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6861    fn register_function_turns_away_every_call_it_cannot_make_sense_of() {
6862        let mut f = Fixture::new();
6863        for (call, want) in [
6864            (
6865                &b"redis.register_function()"[..],
6866                "wrong number of arguments to redis.register_function",
6867            ),
6868            (
6869                b"redis.register_function('a', function() end, 1)",
6870                "wrong number of arguments to redis.register_function",
6871            ),
6872            (
6873                b"redis.register_function('a')",
6874                "calling redis.register_function with a single argument is only \
6875                 applicable to Lua table (representing named arguments).",
6876            ),
6877            (
6878                b"redis.register_function({foo = 'a'})",
6879                "unknown argument given to redis.register_function",
6880            ),
6881            (
6882                b"redis.register_function({callback = function() end})",
6883                "redis.register_function must get a function name argument",
6884            ),
6885            (
6886                b"redis.register_function({function_name = 'a'})",
6887                "redis.register_function must get a callback argument",
6888            ),
6889            (
6890                b"redis.register_function({function_name = {}, callback = function() end})",
6891                "function_name argument given to redis.register_function must be a string",
6892            ),
6893            (
6894                b"redis.register_function({function_name = 'a', description = {}, \
6895                  callback = function() end})",
6896                "description argument given to redis.register_function must be a string",
6897            ),
6898            (
6899                b"redis.register_function({function_name = 'a', callback = 1})",
6900                "callback argument given to redis.register_function must be a function",
6901            ),
6902            (
6903                b"redis.register_function({function_name = 'a', callback = function() end, \
6904                  flags = 1})",
6905                "flags argument to redis.register_function must be a table \
6906                 representing function flags",
6907            ),
6908            (
6909                b"redis.register_function({function_name = 'a', callback = function() end, \
6910                  flags = {'zz'}})",
6911                "unknown flag given",
6912            ),
6913            (
6914                b"redis.register_function({}, function() end)",
6915                "first argument to redis.register_function must be a string",
6916            ),
6917            (
6918                b"redis.register_function('a', 1)",
6919                "second argument to redis.register_function must be a function",
6920            ),
6921            (
6922                b"redis.register_function('a-b', function() end)",
6923                "Library names can only contain letters, numbers, or underscores(_) \
6924                 and must be at least one character long",
6925            ),
6926            (
6927                b"redis.register_function('d', function() end) \
6928                  redis.register_function('d', function() end)",
6929                "Function already exists in the library",
6930            ),
6931        ] {
6932            let mut code = b"#!lua name=e\n".to_vec();
6933            code.extend_from_slice(call);
6934            // Two `ERR` in a row on purpose. The sentence comes back as a table
6935            // with the code already on it, which is what keeps the position off
6936            // the front of it, and then the code goes on the line as well.
6937            assert_eq!(
6938                f.run(&[b"FUNCTION", b"LOAD", &code]),
6939                format!("-ERR Error registering functions: ERR {want}\r\n"),
6940                "{}",
6941                String::from_utf8_lossy(call),
6942            );
6943        }
6944        // A number is a name, because the C reads an argument that should be a
6945        // string through a helper that takes a number and prints it.
6946        assert_eq!(
6947            f.run(&[
6948                b"FUNCTION",
6949                b"LOAD",
6950                b"#!lua name=n\nredis.register_function(12, function() return 1 end)",
6951            ]),
6952            "$1\r\nn\r\n"
6953        );
6954        assert_eq!(f.run(&[b"FCALL", b"12", b"0"]), ":1\r\n");
6955        // The dictionary inside one library is case sensitive where the one
6956        // across libraries is not, so these are two functions.
6957        assert_eq!(
6958            f.run(&[
6959                b"FUNCTION",
6960                b"LOAD",
6961                b"#!lua name=c\nredis.register_function('d', function() return 1 end) \
6962                  redis.register_function('D', function() return 2 end)",
6963            ]),
6964            "$1\r\nc\r\n"
6965        );
6966    }
6967
6968    #[test]
6969    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
6970    fn a_library_cannot_take_a_name_another_library_already_has() {
6971        let mut f = Fixture::new();
6972        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
6973        assert_eq!(
6974            f.run(&[b"FUNCTION", b"LOAD", LIB]),
6975            "-ERR Library 'mylib' already exists\r\n"
6976        );
6977        // A different library that registers a name the first one already has,
6978        // which is checked without regard to case because the dictionary it is
6979        // checked against is.
6980        assert_eq!(
6981            f.run(&[
6982                b"FUNCTION",
6983                b"LOAD",
6984                b"#!lua name=other\nredis.register_function('PING', function() return 1 end)",
6985            ]),
6986            "-ERR Function PING already exists\r\n"
6987        );
6988        // REPLACE reloads a library over itself, and the collision check leaves
6989        // the library being replaced out or nothing could ever be reloaded.
6990        assert_eq!(
6991            f.run(&[b"FUNCTION", b"LOAD", b"REPLACE", LIB]),
6992            "$5\r\nmylib\r\n"
6993        );
6994        // The counter went back to zero with the reload, since the library is a
6995        // new one and its locals are new with it.
6996        assert_eq!(f.run(&[b"FCALL", b"count", b"0"]), ":1\r\n");
6997        assert_eq!(
6998            f.run(&[b"FUNCTION", b"LOAD", b"NOPE", LIB]),
6999            "-ERR Unknown option given: NOPE\r\n"
7000        );
7001        // The loop that reads the options stops one short of the end, so the
7002        // last argument is the code whatever it looks like.
7003        assert_eq!(
7004            f.run(&[b"FUNCTION", b"LOAD", b"REPLACE"]),
7005            "-ERR Missing library metadata\r\n"
7006        );
7007        assert_eq!(
7008            f.run(&[b"FUNCTION", b"LOAD"]),
7009            "-ERR wrong number of arguments for 'function|load' command\r\n"
7010        );
7011    }
7012
7013    #[test]
7014    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7015    fn fcall_checks_the_name_before_it_looks_at_anything_else() {
7016        let mut f = Fixture::new();
7017        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7018        for (args, want) in [
7019            (&[&b"nosuch"[..], b"x"][..], "ERR Function not found"),
7020            (&[b"ping", b"x"], "ERR Bad number of keys provided"),
7021            (&[b"ping", b"1.5"], "ERR Bad number of keys provided"),
7022            (&[b"ping", b"+1"], "ERR Bad number of keys provided"),
7023            (
7024                &[b"ping", b"99999999999999999999"],
7025                "ERR Bad number of keys provided",
7026            ),
7027            (
7028                &[b"ping", b"3", b"a"],
7029                "ERR Number of keys can't be greater than number of args",
7030            ),
7031            (&[b"ping", b"-1"], "ERR Number of keys can't be negative"),
7032        ] {
7033            let mut wire: Vec<&[u8]> = vec![b"FCALL"];
7034            wire.extend_from_slice(args);
7035            assert_eq!(f.run(&wire), format!("-{want}\r\n"), "{args:?}");
7036        }
7037        // The read-only spelling refuses a function the library did not mark
7038        // no-writes, and it refuses it before anything runs.
7039        assert_eq!(
7040            f.run(&[b"FCALL_RO", b"setit", b"1", b"s", b"v"]),
7041            "-ERR Can not execute a script with write flag using *_ro command.\r\n"
7042        );
7043        assert_eq!(f.run(&[b"FCALL_RO", b"ping", b"0"]), "$4\r\npong\r\n");
7044        assert_eq!(
7045            f.run(&[b"FCALL_RO", b"nosuch", b"0"]),
7046            "-ERR Function not found\r\n"
7047        );
7048        // And a function that was marked no-writes is held to it whichever
7049        // spelling called it.
7050        assert_eq!(
7051            f.run(&[
7052                b"FUNCTION",
7053                b"LOAD",
7054                b"#!lua name=w\nredis.register_function{function_name = 'w', \
7055                  flags = {'no-writes'}, callback = function(keys) \
7056                  return redis.call('SET', keys[1], 'x') end}",
7057            ]),
7058            "$1\r\nw\r\n"
7059        );
7060        assert!(
7061            f.run(&[b"FCALL", b"w", b"1", b"k"])
7062                .starts_with("-ERR Write commands are not allowed from read-only scripts."),
7063        );
7064    }
7065
7066    #[test]
7067    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7068    fn a_function_gets_the_globals_a_script_gets_minus_the_ones_only_eval_has() {
7069        let mut f = Fixture::new();
7070        // The three names on the `redis` table that only mean something inside
7071        // EVAL are not there, and neither is the error handler EVAL installs.
7072        let names = "LOG_DEBUG LOG_NOTICE LOG_VERBOSE LOG_WARNING REDIS_VERSION \
7073                     REDIS_VERSION_NUM REPL_ALL REPL_AOF REPL_NONE REPL_REPLICA REPL_SLAVE \
7074                     acl_check_cmd call error_reply log pcall set_repl setresp sha1hex \
7075                     status_reply";
7076        let globals = "_G _VERSION assert bit cjson cmsgpack collectgarbage coroutine error \
7077                       gcinfo getmetatable ipairs load loadstring math next os pairs pcall \
7078                       rawequal rawget rawset redis select setmetatable string struct table \
7079                       tonumber tostring type unpack xpcall";
7080        assert_eq!(
7081            f.run(&[
7082                b"FUNCTION",
7083                b"LOAD",
7084                b"#!lua name=g\n\
7085                  local function sorted(t) local o = {} for k in pairs(t) do o[#o+1] = k end \
7086                  table.sort(o) return table.concat(o, ' ') end\n\
7087                  redis.register_function('names', function() return sorted(redis) end)\n\
7088                  redis.register_function('globals', function() return sorted(_G) end)\n\
7089                  redis.register_function('keysg', function() return KEYS[1] end)\n\
7090                  redis.register_function('zzz', function() return tostring(redis.zzz) end)\n\
7091                  redis.register_function('wr', function() rawset(_G, 'x', 1) end)\n\
7092                  redis.register_function('gwr', function() _G.pcall = 1 end)\n",
7093            ]),
7094            "$1\r\ng\r\n"
7095        );
7096        assert_eq!(
7097            f.run(&[b"FCALL", b"names", b"0"]),
7098            format!("${}\r\n{names}\r\n", names.len())
7099        );
7100        assert_eq!(
7101            f.run(&[b"FCALL", b"globals", b"0"]),
7102            format!("${}\r\n{globals}\r\n", globals.len())
7103        );
7104        // No `KEYS`, and reading a global that is not there is a mistake rather
7105        // than a nil, so this is the sandbox's own complaint.
7106        assert!(
7107            f.run(&[b"FCALL", b"keysg", b"1", b"k"])
7108                .contains("nonexistent global variable 'KEYS'"),
7109        );
7110        // The `redis` table has no error metatable on it, unlike the global
7111        // table, so a name that is not on it is a nil and not a complaint.
7112        assert_eq!(f.run(&[b"FCALL", b"zzz", b"0"]), "$3\r\nnil\r\n");
7113        // The global table cannot be written to either way round, which is a
7114        // stricter rule than the one a script runs under.
7115        for name in [&b"wr"[..], b"gwr"] {
7116            assert!(
7117                f.run(&[b"FCALL", name, b"0"])
7118                    .contains("Attempt to modify a readonly table"),
7119                "{}",
7120                String::from_utf8_lossy(name),
7121            );
7122        }
7123    }
7124
7125    #[test]
7126    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7127    fn function_list_says_what_every_library_registered() {
7128        let mut f = Fixture::new();
7129        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7130        // One map per library on RESP3, and the functions inside it in the
7131        // order the library registered them, which is D-109.
7132        f.out = Out::new(Proto::Resp3);
7133        let listed = f.run(&[b"FUNCTION", b"LIST"]);
7134        assert!(listed.starts_with("*1\r\n%3\r\n$12\r\nlibrary_name\r\n$5\r\nmylib\r\n"));
7135        assert!(listed.contains("$6\r\nengine\r\n$3\r\nLUA\r\n"));
7136        assert!(listed.contains(
7137            "%3\r\n$4\r\nname\r\n$4\r\nping\r\n\
7138             $11\r\ndescription\r\n$9\r\nsays pong\r\n$5\r\nflags\r\n~1\r\n+no-writes\r\n"
7139        ));
7140        // A function with no description gets a null rather than an empty
7141        // string, and no flags is an empty set rather than a missing field.
7142        assert!(listed.contains(
7143            "$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"
7144        ));
7145        assert!(!listed.contains("library_code"));
7146        assert!(
7147            f.run(&[b"FUNCTION", b"LIST", b"WITHCODE"])
7148                .contains("library_code")
7149        );
7150        // The pattern is matched without regard to case, which is a third rule
7151        // again next to the two the two dictionaries use.
7152        assert!(
7153            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"MY*"])
7154                .starts_with("*1\r\n")
7155        );
7156        assert_eq!(
7157            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"zz*"]),
7158            "*0\r\n"
7159        );
7160        // On RESP2 the same reply is a flat array of six, which is what `map`
7161        // means on a protocol that has no map.
7162        f.out = Out::new(Proto::Resp2);
7163        assert!(f.run(&[b"FUNCTION", b"LIST"]).starts_with("*1\r\n*6\r\n"));
7164        for (args, want) in [
7165            (&[&b"ZZ"[..]][..], "ERR Unknown argument ZZ"),
7166            (&[b"WITHCODE", b"WITHCODE"], "ERR Unknown argument WITHCODE"),
7167            (
7168                &[b"LIBRARYNAME", b"a", b"LIBRARYNAME", b"b"],
7169                "ERR Unknown argument LIBRARYNAME",
7170            ),
7171            (&[b"LIBRARYNAME"], "ERR library name argument was not given"),
7172        ] {
7173            let mut wire: Vec<&[u8]> = vec![b"FUNCTION", b"LIST"];
7174            wire.extend_from_slice(args);
7175            assert_eq!(f.run(&wire), format!("-{want}\r\n"), "{args:?}");
7176        }
7177    }
7178
7179    #[test]
7180    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7181    fn function_stats_counts_what_is_loaded_and_says_nothing_is_running() {
7182        let mut f = Fixture::new();
7183        f.out = Out::new(Proto::Resp3);
7184        assert_eq!(
7185            f.run(&[b"FUNCTION", b"STATS"]),
7186            "%2\r\n$14\r\nrunning_script\r\n_\r\n$7\r\nengines\r\n%1\r\n$3\r\nLUA\r\n\
7187             %2\r\n$15\r\nlibraries_count\r\n:0\r\n$15\r\nfunctions_count\r\n:0\r\n"
7188        );
7189        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7190        assert!(
7191            f.run(&[b"FUNCTION", b"STATS"])
7192                .ends_with("libraries_count\r\n:1\r\n$15\r\nfunctions_count\r\n:5\r\n"),
7193        );
7194        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH"]), "+OK\r\n");
7195        assert!(
7196            f.run(&[b"FUNCTION", b"STATS"])
7197                .ends_with(":0\r\n$15\r\nfunctions_count\r\n:0\r\n")
7198        );
7199    }
7200
7201    #[test]
7202    fn every_function_subcommand_complains_about_its_own_arity() {
7203        let mut f = Fixture::new();
7204        for (args, want) in [
7205            (
7206                &[&b"STATS"[..], b"X"][..],
7207                "ERR wrong number of arguments for 'function|stats' command",
7208            ),
7209            (
7210                &[b"KILL", b"X"],
7211                "ERR wrong number of arguments for 'function|kill' command",
7212            ),
7213            (
7214                &[b"HELP", b"X"],
7215                "ERR wrong number of arguments for 'function|help' command",
7216            ),
7217            (
7218                &[b"DELETE"],
7219                "ERR wrong number of arguments for 'function|delete' command",
7220            ),
7221            (
7222                &[b"DELETE", b"a", b"b"],
7223                "ERR wrong number of arguments for 'function|delete' command",
7224            ),
7225            (
7226                &[b"DUMP", b"X"],
7227                "ERR wrong number of arguments for 'function|dump' command",
7228            ),
7229            (
7230                &[b"RESTORE"],
7231                "ERR wrong number of arguments for 'function|restore' command",
7232            ),
7233            // RESTORE is the other one that falls through to the generic
7234            // sentence, and for the same reason FLUSH does.
7235            (
7236                &[b"RESTORE", b"a", b"FLUSH", b"X"],
7237                "ERR unknown subcommand or wrong number of arguments for 'RESTORE'. \
7238                 Try FUNCTION HELP.",
7239            ),
7240            (
7241                &[b"RESTORE", b"a", b"ZZ"],
7242                "ERR Wrong restore policy given, value should be either FLUSH, APPEND \
7243                 or REPLACE.",
7244            ),
7245            // FLUSH is the one that does not, because it checks the count
7246            // itself before it looks at the argument.
7247            (
7248                &[b"FLUSH", b"SYNC", b"X"],
7249                "ERR unknown subcommand or wrong number of arguments for 'FLUSH'. \
7250                 Try FUNCTION HELP.",
7251            ),
7252            (
7253                &[b"FLUSH", b"ZZ"],
7254                "ERR FUNCTION FLUSH only supports SYNC|ASYNC option",
7255            ),
7256            (&[b"ZZ"], "ERR unknown subcommand 'ZZ'. Try FUNCTION HELP."),
7257        ] {
7258            let mut wire: Vec<&[u8]> = vec![b"FUNCTION"];
7259            wire.extend_from_slice(args);
7260            assert_eq!(f.run(&wire), format!("-{want}\r\n"), "{args:?}");
7261        }
7262        assert_eq!(
7263            f.run(&[b"FUNCTION"]),
7264            "-ERR wrong number of arguments for 'function' command\r\n"
7265        );
7266        assert_eq!(
7267            f.run(&[b"FUNCTION", b"KILL"]),
7268            "-NOTBUSY No scripts in execution right now.\r\n"
7269        );
7270    }
7271
7272    /// The two ends of the same pipe, so they are tested as one.
7273    ///
7274    /// An empty server dumps ten bytes rather than nothing, because the footer
7275    /// is there whether or not a library is in front of it, and restoring those
7276    /// ten bytes is a working no op.
7277    #[test]
7278    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7279    fn a_library_survives_a_dump_and_a_restore() {
7280        let mut f = Fixture::new();
7281        let empty = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7282        assert_eq!(empty.len(), 10);
7283        assert_eq!(f.run(&[b"FUNCTION", b"RESTORE", &empty]), "+OK\r\n");
7284
7285        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7286        let full = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7287        assert!(full.len() > empty.len());
7288
7289        // The default policy is APPEND, so restoring onto the library the
7290        // payload came from is a name collision and not a quiet replacement.
7291        assert_eq!(
7292            f.run(&[b"FUNCTION", b"RESTORE", &full]),
7293            "-ERR Library mylib already exists\r\n"
7294        );
7295        assert_eq!(
7296            f.run(&[b"FUNCTION", b"RESTORE", &full, b"REPLACE"]),
7297            "+OK\r\n"
7298        );
7299        assert_eq!(
7300            f.run(&[b"FUNCTION", b"RESTORE", &full, b"FLUSH"]),
7301            "+OK\r\n"
7302        );
7303        // Whichever way it went back, the functions in it still run.
7304        assert_eq!(f.run(&[b"FCALL", b"ping", b"0"]), "$4\r\npong\r\n");
7305
7306        // FLUSH keeps only what the payload held, so a library that was there
7307        // and is not in the payload is gone.
7308        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", OTHER]), "$5\r\nother\r\n");
7309        assert_eq!(
7310            f.run(&[b"FUNCTION", b"RESTORE", &full, b"FLUSH"]),
7311            "+OK\r\n"
7312        );
7313        assert_eq!(
7314            f.run(&[b"FUNCTION", b"DELETE", b"other"]),
7315            "-ERR Library not found\r\n"
7316        );
7317    }
7318
7319    /// A payload that is going to be refused has to leave the server alone.
7320    ///
7321    /// Every one of these is refused for a different reason and at a different
7322    /// depth, from bytes that are not a payload at all down to a library that
7323    /// compiles and then collides, and the library that was already there has to
7324    /// still be there afterwards in every case.
7325    #[test]
7326    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7327    fn a_restore_that_fails_changes_nothing() {
7328        let mut f = Fixture::new();
7329        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7330        let good = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7331
7332        // Put the footer back on, so that each of these is refused for the
7333        // reason it is meant to be testing rather than for a checksum the edit
7334        // broke on the way.
7335        let reseal = |body: &[u8], version: u16| {
7336            let mut out = body.to_vec();
7337            out.extend_from_slice(&version.to_le_bytes());
7338            let crc = yo_common::crc::crc64(0, &out);
7339            out.extend_from_slice(&crc.to_le_bytes());
7340            out
7341        };
7342        let body = &good[..good.len() - 10];
7343
7344        let mut torn = good.clone();
7345        let n = torn.len();
7346        torn[n - 1] ^= 0xff;
7347        let future = reseal(body, 999);
7348        // The opcode in front of the one library, changed to the one the 7.0
7349        // release candidates wrote and then to one that is not a library at all.
7350        let mut pre_ga = body.to_vec();
7351        pre_ga[0] = 246;
7352        let pre_ga = reseal(&pre_ga, yo_kv::rdb::VERSION);
7353        let mut other = body.to_vec();
7354        other[0] = 0;
7355        let other = reseal(&other, yo_kv::rdb::VERSION);
7356        // A library whose length says there is more of it than there is.
7357        let mut cut = body.to_vec();
7358        cut.truncate(body.len() - 1);
7359        let cut = reseal(&cut, yo_kv::rdb::VERSION);
7360
7361        for (bytes, want) in [
7362            (vec![], "ERR DUMP payload version or checksum are wrong"),
7363            (
7364                b"0123456789".to_vec(),
7365                "ERR DUMP payload version or checksum are wrong",
7366            ),
7367            (torn, "ERR DUMP payload version or checksum are wrong"),
7368            (future, "ERR DUMP payload version or checksum are wrong"),
7369            (pre_ga, "ERR Pre-GA function format not supported"),
7370            (other, "ERR given type is not a function"),
7371            (cut, "ERR Failed loading library payload"),
7372        ] {
7373            assert_eq!(
7374                f.run(&[b"FUNCTION", b"RESTORE", &bytes]),
7375                format!("-{want}\r\n")
7376            );
7377        }
7378
7379        // Still exactly the one library, and it still runs.
7380        assert_eq!(f.run(&[b"FCALL", b"ping", b"0"]), "$4\r\npong\r\n");
7381        let again = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7382        assert_eq!(again, good);
7383    }
7384
7385    /// A REPLACE takes a library's name off another library and still refuses to
7386    /// take a function name off one it is leaving alone.
7387    #[test]
7388    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7389    fn a_restore_will_not_take_a_function_name_off_a_library_it_keeps() {
7390        let mut f = Fixture::new();
7391        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", LIB]), "$5\r\nmylib\r\n");
7392        let full = payload(&f.raw(&[b"FUNCTION", b"DUMP"]));
7393        // A second library registering the name the payload's library uses.
7394        let clash =
7395            b"#!lua name=cl\nredis.register_function('ping', function() return 'other' end)"
7396                .as_slice();
7397        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH"]), "+OK\r\n");
7398        assert_eq!(f.run(&[b"FUNCTION", b"LOAD", clash]), "$2\r\ncl\r\n");
7399        assert_eq!(
7400            f.run(&[b"FUNCTION", b"RESTORE", &full, b"REPLACE"]),
7401            "-ERR Function ping already exists\r\n"
7402        );
7403        // Untouched, so the name still belongs to the library that had it.
7404        assert_eq!(f.run(&[b"FCALL", b"ping", b"0"]), "$5\r\nother\r\n");
7405    }
7406
7407    #[test]
7408    fn command_getkeys_reads_the_key_count_out_of_a_script_call() {
7409        let mut f = Fixture::new();
7410        assert_eq!(
7411            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1", b"1", b"k"]),
7412            "*1\r\n$1\r\nk\r\n"
7413        );
7414        assert_eq!(
7415            f.run(&[
7416                b"COMMAND", b"GETKEYS", b"EVALSHA", b"abc", b"2", b"k1", b"k2"
7417            ]),
7418            "*2\r\n$2\r\nk1\r\n$2\r\nk2\r\n"
7419        );
7420        // None is a real answer for a script and the arguments past the count
7421        // are not keys, so they are not listed.
7422        assert_eq!(
7423            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL_RO", b"return 1", b"0", b"a"]),
7424            "*0\r\n"
7425        );
7426        // A count that makes no sense finds no keys rather than being an error,
7427        // which is what a real server's key spec does with it.
7428        assert_eq!(
7429            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1", b"3", b"k"]),
7430            "*0\r\n"
7431        );
7432        assert_eq!(
7433            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1", b"-1"]),
7434            "*0\r\n"
7435        );
7436        assert_eq!(
7437            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1", b"abc"]),
7438            "*0\r\n"
7439        );
7440        // The count itself has to be there, and that is an arity question.
7441        assert_eq!(
7442            f.run(&[b"COMMAND", b"GETKEYS", b"EVAL", b"return 1"]),
7443            "-ERR Invalid number of arguments specified for command\r\n"
7444        );
7445    }
7446
7447    #[test]
7448    #[cfg_attr(miri, ignore = "a Lua state is C, and Miri interprets Rust")]
7449    fn the_helpers_on_the_redis_table_answer_the_way_they_are_documented() {
7450        let mut f = Fixture::new();
7451        let eval = |f: &mut Fixture, body: &[u8]| f.run(&[b"EVAL", body, b"0"]);
7452
7453        assert_eq!(
7454            eval(&mut f, b"return redis.sha1hex('')"),
7455            "$40\r\nda39a3ee5e6b4b0d3255bfef95601890afd80709\r\n"
7456        );
7457        assert_eq!(
7458            eval(&mut f, b"return redis.sha1hex('return 1')"),
7459            "$40\r\ne0e1f9fabfc9d4800c877a703b823ac0578ff8db\r\n"
7460        );
7461        // A message with no space in it gets the generic code in front, and one
7462        // that already looks like a coded error is left alone.
7463        assert_eq!(
7464            eval(&mut f, b"return redis.error_reply('boom')"),
7465            "-ERR boom\r\n"
7466        );
7467        assert_eq!(
7468            eval(&mut f, b"return redis.error_reply('WRONGTYPE nope')"),
7469            "-WRONGTYPE nope\r\n"
7470        );
7471        assert_eq!(
7472            eval(&mut f, b"return redis.status_reply('fine')"),
7473            "+fine\r\n"
7474        );
7475        // Neither of them raises when it is called wrongly, they answer a value
7476        // that is an error, which is a difference a script can see.
7477        assert_eq!(
7478            eval(&mut f, b"return redis.error_reply(1)"),
7479            "-ERR wrong number or type of arguments\r\n"
7480        );
7481        assert_eq!(
7482            eval(&mut f, b"local x = redis.status_reply() return x.err"),
7483            "$37\r\nERR wrong number or type of arguments\r\n"
7484        );
7485
7486        // The constants a script branches on.
7487        assert_eq!(
7488            eval(
7489                &mut f,
7490                b"return redis.LOG_DEBUG .. redis.LOG_VERBOSE .. redis.LOG_NOTICE .. redis.LOG_WARNING"
7491            ),
7492            "$4\r\n0123\r\n"
7493        );
7494        assert_eq!(
7495            eval(
7496                &mut f,
7497                b"return redis.REPL_NONE .. redis.REPL_AOF .. redis.REPL_SLAVE .. redis.REPL_REPLICA .. redis.REPL_ALL"
7498            ),
7499            "$5\r\n01223\r\n"
7500        );
7501        // The calls that exist so an old script keeps working.
7502        assert_eq!(eval(&mut f, b"return redis.replicate_commands()"), ":1\r\n");
7503        assert_eq!(
7504            eval(&mut f, b"redis.set_repl(redis.REPL_ALL) return 1"),
7505            ":1\r\n"
7506        );
7507        assert_eq!(
7508            eval(&mut f, b"redis.log(redis.LOG_WARNING, 'x') return 1"),
7509            ":1\r\n"
7510        );
7511        assert_eq!(
7512            eval(&mut f, b"return redis.acl_check_cmd('get', 'k')"),
7513            ":1\r\n"
7514        );
7515        // Each of those checks its arguments the way a real server does.
7516        assert!(eval(&mut f, b"redis.setresp(4)").contains("RESP version must be 2 or 3."),);
7517        assert!(eval(&mut f, b"redis.set_repl(9)").contains("Invalid replication flags."));
7518        assert!(
7519            eval(&mut f, b"redis.log('x', 'y')")
7520                .contains("First argument must be a number (log level)."),
7521        );
7522        assert!(
7523            eval(&mut f, b"return redis.acl_check_cmd('nosuchcmd')")
7524                .contains("Invalid command passed to redis.acl_check_cmd()"),
7525        );
7526        assert!(
7527            eval(&mut f, b"return redis.acl_check_cmd('get')")
7528                .contains("Wrong number of args for redis.acl_check_cmd()"),
7529        );
7530    }
7531
7532    #[test]
7533    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
7534        let mut f = Fixture::new();
7535        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
7536        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
7537        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
7538        // Read back as a string it is still an integer, written out as digits
7539        // only because somebody asked for them.
7540        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
7541        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
7542        // A counter that is not a number is the error the store raises and this
7543        // layer only spells, which is the whole point of the split.
7544        f.run(&[b"SET", b"k", b"hello"]);
7545        assert_eq!(
7546            f.run(&[b"INCR", b"k"]),
7547            "-ERR value is not an integer or out of range\r\n"
7548        );
7549        assert_eq!(
7550            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
7551            "-ERR increment would produce NaN or Infinity\r\n"
7552        );
7553    }
7554
7555    /// Every one of these was read off a running 8.8. They are the answers a
7556    /// client library's own test suite checks, and the shapes are not
7557    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
7558    /// integer, `INCREX` is a pair.
7559    #[test]
7560    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
7561        let mut f = Fixture::new();
7562        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
7563        // The same digest a real 8.8 answers for the same five bytes, which is
7564        // what makes `IFDEQ` usable against a mixed deployment.
7565        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
7566        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
7567        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
7568        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
7569        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
7570        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
7571        assert_eq!(
7572            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
7573            "*2\r\n:1\r\n:0\r\n",
7574            "a refused increment reports the value it left alone and applied nothing"
7575        );
7576        assert_eq!(
7577            f.run(&[
7578                b"INCREX",
7579                b"n",
7580                b"BYINT",
7581                b"5",
7582                b"UBOUND",
7583                b"3",
7584                b"SATURATE"
7585            ]),
7586            "*2\r\n:3\r\n:2\r\n"
7587        );
7588        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
7589        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
7590    }
7591
7592    #[test]
7593    fn the_same_answers_come_out_in_resp3_spelling() {
7594        let mut f = Fixture::new();
7595        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
7596        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
7597        // A float counter is a double on RESP3 and the digits in a bulk string
7598        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
7599        assert_eq!(
7600            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
7601            "*2\r\n,1.5\r\n,1.5\r\n"
7602        );
7603        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
7604        // `RESET` puts the protocol back, which is the part that is easy to
7605        // miss and leaves a pooled connection speaking the wrong one.
7606        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
7607        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
7608    }
7609
7610    #[test]
7611    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
7612        let mut f = Fixture::new();
7613        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
7614        assert_eq!(flow, Flow::Continue);
7615        assert_eq!(
7616            reply,
7617            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
7618        );
7619        // A name with a line ending in it cannot write its own frame into the
7620        // stream, which is the reason the error writer maps them to spaces.
7621        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
7622        assert_eq!(reply.matches("\r\n").count(), 1);
7623    }
7624
7625    #[test]
7626    fn arity_is_checked_before_the_command_is() {
7627        let mut f = Fixture::new();
7628        assert_eq!(
7629            f.run(&[b"GET"]),
7630            "-ERR wrong number of arguments for 'get' command\r\n"
7631        );
7632        assert_eq!(
7633            f.run(&[b"MSET", b"k"]),
7634            "-ERR wrong number of arguments for 'mset' command\r\n"
7635        );
7636        // The table says `PING` takes one or more and a real server then
7637        // refuses three, which is the sort of thing that only shows up against
7638        // the real thing.
7639        assert_eq!(
7640            f.run(&[b"PING", b"a", b"b"]),
7641            "-ERR wrong number of arguments for 'ping' command\r\n"
7642        );
7643        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
7644        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
7645        // `DELEX` takes two or four and nothing between.
7646        assert_eq!(
7647            f.run(&[b"DELEX", b"k", b"IFEQ"]),
7648            "-ERR wrong number of arguments for 'delex' command\r\n"
7649        );
7650    }
7651
7652    /// The option rules, all of them measured against 8.8 rather than read off
7653    /// the documentation. The surprising one is that `SET` accepts the same
7654    /// keyword twice and `INCREX` does not.
7655    #[test]
7656    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
7657        let mut f = Fixture::new();
7658        let syntax = "-ERR syntax error\r\n";
7659        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
7660        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
7661        assert_eq!(
7662            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
7663            syntax
7664        );
7665        assert_eq!(
7666            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
7667            syntax
7668        );
7669        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
7670        // Twice is fine, and the last one wins.
7671        assert_eq!(
7672            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
7673            "+OK\r\n"
7674        );
7675        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
7676        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
7677        // `INCREX` refuses what `SET` allows.
7678        assert_eq!(
7679            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
7680            syntax
7681        );
7682        assert_eq!(
7683            f.run(&[b"INCREX", b"n", b"ENX"]),
7684            "-ERR ENX flag requires an expiration\r\n"
7685        );
7686        assert_eq!(
7687            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
7688            "-ERR UBOUND is not an integer or out of range\r\n"
7689        );
7690        assert_eq!(
7691            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
7692            "-ERR LBOUND can't be greater than UBOUND\r\n"
7693        );
7694        assert_eq!(
7695            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
7696            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
7697        );
7698    }
7699
7700    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
7701    /// key that is not there, which answers null without ever looking at the
7702    /// expiration it was given.
7703    #[test]
7704    fn the_expiry_rules_are_redis_own() {
7705        let mut f = Fixture::new();
7706        let bad = "-ERR invalid expire time in 'set' command\r\n";
7707        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
7708        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
7709        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
7710        assert_eq!(
7711            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
7712            bad
7713        );
7714        assert_eq!(
7715            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
7716            "-ERR value is not an integer or out of range\r\n"
7717        );
7718        assert_eq!(
7719            f.run(&[b"SETEX", b"k", b"0", b"v"]),
7720            "-ERR invalid expire time in 'setex' command\r\n"
7721        );
7722        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
7723        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
7724        assert_eq!(
7725            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
7726            "-ERR syntax error\r\n",
7727            "the option list is still checked before the key is looked up"
7728        );
7729        // A deadline in the past is accepted and the key goes with it.
7730        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
7731        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
7732        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
7733    }
7734
7735    #[test]
7736    fn mset_takes_its_pairs_from_the_read_buffer() {
7737        let mut f = Fixture::new();
7738        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
7739        assert_eq!(
7740            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
7741            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
7742        );
7743        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
7744        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
7745        assert_eq!(
7746            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
7747            "-ERR wrong number of key-value pairs\r\n"
7748        );
7749        assert_eq!(
7750            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
7751            "-ERR invalid numkeys value\r\n"
7752        );
7753        assert_eq!(
7754            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
7755            "-ERR invalid numkeys value\r\n"
7756        );
7757    }
7758
7759    #[test]
7760    fn lcs_answers_the_length_the_string_and_the_runs() {
7761        let mut f = Fixture::new();
7762        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
7763        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
7764        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
7765        assert_eq!(
7766            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
7767            "*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"
7768        );
7769        // Without `IDX` the two options that only mean something with it are
7770        // accepted and ignored, which is what a real server does.
7771        assert_eq!(
7772            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
7773            "$6\r\nmytext\r\n"
7774        );
7775    }
7776
7777    #[test]
7778    fn select_moves_the_connection_and_the_databases_stay_apart() {
7779        let mut f = Fixture::new();
7780        f.run(&[b"SET", b"k", b"zero"]);
7781        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
7782        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
7783        f.run(&[b"SET", b"k", b"four"]);
7784        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
7785        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
7786        assert_eq!(
7787            f.run(&[b"SELECT", b"99"]),
7788            "-ERR DB index is out of range\r\n"
7789        );
7790        assert_eq!(
7791            f.run(&[b"SELECT", b"-1"]),
7792            "-ERR DB index is out of range\r\n"
7793        );
7794        assert_eq!(
7795            f.run(&[b"SELECT", b"abc"]),
7796            "-ERR value is not an integer or out of range\r\n"
7797        );
7798        // `RESET` brings it back to zero.
7799        f.run(&[b"SELECT", b"4"]);
7800        f.run(&[b"RESET"]);
7801        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
7802    }
7803
7804    #[test]
7805    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
7806        let mut f = Fixture::new();
7807        let reply = f.run(&[b"HELLO"]);
7808        assert!(reply.starts_with("*14\r\n"), "{reply}");
7809        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
7810        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
7811        assert!(
7812            reply.contains(":7\r\n"),
7813            "the connection id is in there: {reply}"
7814        );
7815        assert_eq!(
7816            f.run(&[b"HELLO", b"4"]),
7817            "-NOPROTO unsupported protocol version\r\n"
7818        );
7819        assert_eq!(
7820            f.run(&[b"HELLO", b"abc"]),
7821            "-ERR Protocol version is not an integer or out of range\r\n"
7822        );
7823        assert_eq!(
7824            f.run(&[b"HELLO", b"3", b"SETNAME"]),
7825            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
7826        );
7827        assert!(
7828            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
7829                .starts_with("%7\r\n")
7830        );
7831        assert_eq!(f.session.name(), b"bob");
7832        f.run(&[b"RESET"]);
7833        assert_eq!(f.session.name(), b"");
7834    }
7835
7836    #[test]
7837    fn command_describes_this_server_in_the_shape_a_driver_reads() {
7838        let mut f = Fixture::new();
7839        let count = format!(":{}\r\n", COMMANDS.len());
7840        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
7841        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
7842        assert_eq!(
7843            info,
7844            "*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\
7845             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*1\r\n*6\r\n\
7846             $5\r\nflags\r\n*2\r\n+RO\r\n+access\r\n\
7847             $12\r\nbegin_search\r\n*4\r\n$4\r\ntype\r\n$5\r\nindex\r\n$4\r\nspec\r\n\
7848             *2\r\n$5\r\nindex\r\n:1\r\n\
7849             $9\r\nfind_keys\r\n*4\r\n$4\r\ntype\r\n$5\r\nrange\r\n$4\r\nspec\r\n\
7850             *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\
7851             *0\r\n"
7852        );
7853        // A null in the list, and the plain one: `$-1` and not `*-1`.
7854        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
7855        assert_eq!(
7856            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
7857            "*1\r\n$8\r\ngetrange\r\n"
7858        );
7859        assert_eq!(
7860            f.run(&[b"COMMAND", b"NOPE"]),
7861            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
7862        );
7863    }
7864
7865    /// A cluster aware client asks this question and then routes on the
7866    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
7867    /// that matters.
7868    #[test]
7869    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
7870        let mut f = Fixture::new();
7871        assert_eq!(
7872            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
7873            "*1\r\n$1\r\nk\r\n"
7874        );
7875        assert_eq!(
7876            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
7877            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
7878        );
7879        assert_eq!(
7880            f.run(&[
7881                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
7882            ]),
7883            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
7884        );
7885        assert_eq!(
7886            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
7887            "-ERR The command has no key arguments\r\n"
7888        );
7889        assert_eq!(
7890            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
7891            "-ERR Invalid number of arguments specified for command\r\n"
7892        );
7893    }
7894
7895    #[test]
7896    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
7897        let mut f = Fixture::new();
7898        assert_eq!(
7899            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
7900            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
7901        );
7902        // A pattern matches more than one, and a setting two patterns both ask
7903        // for is still sent once.
7904        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
7905        assert!(both.starts_with("*6\r\n"), "{both}");
7906        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
7907        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
7908        assert_eq!(
7909            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
7910            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
7911        );
7912        assert_eq!(
7913            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
7914            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
7915        );
7916        assert_eq!(
7917            f.run(&[b"CONFIG", b"GET"]),
7918            "-ERR wrong number of arguments for 'config|get' command\r\n"
7919        );
7920        // Too few arguments and an odd number of them are different
7921        // complaints, which is the sort of thing only the real server tells
7922        // you.
7923        assert_eq!(
7924            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
7925            "-ERR wrong number of arguments for 'config|set' command\r\n"
7926        );
7927        assert_eq!(
7928            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
7929            "-ERR syntax error\r\n"
7930        );
7931        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
7932        assert_eq!(
7933            f.run(&[b"CONFIG", b"REWRITE"]),
7934            "-ERR The server is running without a config file\r\n"
7935        );
7936    }
7937
7938    #[test]
7939    fn the_eviction_policy_reads_back_what_was_written_to_it() {
7940        let mut f = Fixture::new();
7941        assert_eq!(
7942            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
7943            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
7944        );
7945        assert_eq!(
7946            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
7947            "+OK\r\n",
7948            "the name is matched without regard to case, like every other one"
7949        );
7950        assert_eq!(
7951            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
7952            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
7953        );
7954        // And INFO agrees with CONFIG, which it did not when it was a literal.
7955        assert!(
7956            f.run(&[b"INFO", b"memory"])
7957                .contains("maxmemory_policy:allkeys-lfu"),
7958            "INFO and CONFIG disagree about the policy"
7959        );
7960        // The refusal names every legal value in the order the real server's
7961        // enum table lists them, because a client comparing the message compares
7962        // the whole string.
7963        assert_eq!(
7964            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
7965            "-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"
7966        );
7967        // A bad pair leaves the good one in the same command alone, and the
7968        // policy is checked by the same pass that checks the numbers.
7969        assert_eq!(
7970            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
7971            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
7972        );
7973        f.run(&[
7974            b"CONFIG",
7975            b"SET",
7976            b"hash-max-listpack-entries",
7977            b"7",
7978            b"maxmemory-policy",
7979            b"nonsense",
7980        ]);
7981        assert_eq!(
7982            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
7983            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
7984        );
7985    }
7986
7987    #[test]
7988    fn the_three_eviction_numbers_read_back_too() {
7989        let mut f = Fixture::new();
7990        for (name, default, set) in [
7991            ("maxmemory-samples", "5", "12"),
7992            ("lfu-log-factor", "10", "3"),
7993            ("lfu-decay-time", "1", "60"),
7994        ] {
7995            let get = || {
7996                format!(
7997                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
7998                    name.len(),
7999                    default.len()
8000                )
8001            };
8002            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
8003            assert_eq!(
8004                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
8005                "+OK\r\n"
8006            );
8007            assert_eq!(
8008                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
8009                format!(
8010                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
8011                    name.len(),
8012                    set.len()
8013                )
8014            );
8015            // A number that is not a number is refused with the same sentence
8016            // every other number gets, which names the setting the client typed.
8017            assert_eq!(
8018                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
8019                format!(
8020                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
8021                )
8022            );
8023        }
8024    }
8025
8026    #[test]
8027    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
8028        let mut f = Fixture::new();
8029        assert_eq!(
8030            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
8031            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
8032            "no limit is the default"
8033        );
8034        // The pairing is Redis's and it is a trap: the bare letter is a power of
8035        // ten and the one with the b is a power of two.
8036        for (typed, bytes) in [
8037            (&b"1024"[..], "1024"),
8038            (b"1k", "1000"),
8039            (b"1kb", "1024"),
8040            (b"1M", "1000000"),
8041            (b"1Mb", "1048576"),
8042            (b"1gb", "1073741824"),
8043            (b"100mb", "104857600"),
8044        ] {
8045            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
8046            assert_eq!(
8047                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
8048                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
8049                "set {}",
8050                String::from_utf8_lossy(typed)
8051            );
8052        }
8053        assert!(
8054            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
8055            "the report agrees with the setting"
8056        );
8057
8058        // A unit nobody has heard of, and a negative number, which is not a very
8059        // large one however it is spelled.
8060        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
8061            assert_eq!(
8062                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
8063                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
8064                "refused {}",
8065                String::from_utf8_lossy(bad)
8066            );
8067        }
8068        assert!(
8069            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
8070            "and the refusal left the old one alone"
8071        );
8072    }
8073
8074    #[test]
8075    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
8076        let mut f = Fixture::new();
8077        f.run(&[b"SET", b"here", b"already"]);
8078        // A byte, which is under what an empty server holds, so nothing this
8079        // command could do would get it under. The default policy is
8080        // `noeviction`, so nothing is what it does.
8081        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
8082        assert_eq!(
8083            f.run(&[b"SET", b"k", b"v"]),
8084            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
8085        );
8086        assert_eq!(
8087            f.run(&[b"LPUSH", b"l", b"v"]),
8088            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
8089        );
8090        // Reading is allowed, and so is the one thing that would help.
8091        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
8092        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
8093        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
8094
8095        // Taking the limit away lets the write through again.
8096        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
8097        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
8098    }
8099
8100    /// Not under Miri, for the reason in `filled`: what it is watching is a
8101    /// whole two megabyte segment going back, so the megabytes are the claim
8102    /// and there is no smaller version of it that says the same thing.
8103    #[cfg_attr(miri, ignore = "several megabytes of arena, see `filled`")]
8104    #[test]
8105    fn an_allkeys_policy_makes_room_instead_of_refusing() {
8106        let mut f = Fixture::new();
8107        let val = vec![b'v'; 256];
8108        for i in 0..24000u32 {
8109            let k = format!("key:{i:08}");
8110            f.run(&[b"SET", k.as_bytes(), &val]);
8111        }
8112        let full = f.server.memory_bytes();
8113        assert!(
8114            full > 3 * 1024 * 1024,
8115            "the arena is several segments: {full}"
8116        );
8117
8118        // Two megabytes under what it is holding, which is one segment's worth,
8119        // so getting there means giving a whole segment back and not just
8120        // dropping a few records.
8121        let limit = full - 2 * 1024 * 1024;
8122        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
8123        f.run(&[
8124            b"CONFIG",
8125            b"SET",
8126            b"maxmemory",
8127            limit.to_string().as_bytes(),
8128        ]);
8129
8130        // Writes keep working the whole way down. The budget means one command
8131        // does not do it all, so this runs until the server has settled and
8132        // checks that nothing was refused on the way.
8133        for i in 0..2000u32 {
8134            let k = format!("new:{i:08}");
8135            assert_eq!(
8136                f.run(&[b"SET", k.as_bytes(), &val]),
8137                "+OK\r\n",
8138                "write {i} was refused"
8139            );
8140            f.server.refresh_memory();
8141            if f.server.memory_bytes() <= limit {
8142                break;
8143            }
8144        }
8145        assert!(
8146            f.server.memory_bytes() <= limit,
8147            "it never got under: {} against {limit}",
8148            f.server.memory_bytes()
8149        );
8150        let info = f.run(&[b"INFO", b"stats"]);
8151        assert!(!info.contains("evicted_keys:0"), "{info}");
8152        assert!(
8153            f.run(&[b"DBSIZE"]) != ":0\r\n",
8154            "and it did not empty the database to get there"
8155        );
8156    }
8157
8158    /// Not under Miri. Every round is eleven commands over six collections
8159    /// holding two hundred byte values, which is a third of a second each
8160    /// interpreted, and the rounds cannot come down far: one in seven takes an
8161    /// entry back out, so under about a hundred and seventy of them the
8162    /// collections never reach the hundred and twenty eight entries where the
8163    /// small representations give up and become the big ones, and a
8164    /// representation changing under the running total is one of the five
8165    /// things this is here to watch. What is left is an hour, for an accounting
8166    /// claim rather than a safety one, and the commands it sends are sent a few
8167    /// at a time by the tests around it.
8168    #[cfg_attr(miri, ignore = "an hour of commands, and they cannot come down")]
8169    #[test]
8170    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
8171        // The limit is judged against a number kept as the collections move,
8172        // rather than found by asking all of them, and the two have to be the
8173        // same number or the limit is enforced against a fiction. This does the
8174        // things that move it, which is growing a collection, shrinking one,
8175        // changing its representation, deleting it and reusing its slot, across
8176        // all five types, and checks the two against each other as it goes.
8177        let mut f = Fixture::new();
8178        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
8179        let big = vec![b'v'; 200];
8180
8181        for i in 0..400u32 {
8182            let n = i.to_string();
8183            let n = n.as_bytes();
8184            f.run(&[b"SADD", b"s", n]);
8185            f.run(&[b"SADD", b"s2", &big]);
8186            f.run(&[b"HSET", b"h", n, &big]);
8187            f.run(&[b"RPUSH", b"l", &big]);
8188            f.run(&[b"ZADD", b"z", n, n]);
8189            f.run(&[b"ARSET", b"a", n, &big]);
8190            if i % 7 == 0 {
8191                f.run(&[b"SREM", b"s", n]);
8192                f.run(&[b"HDEL", b"h", n]);
8193                f.run(&[b"LPOP", b"l"]);
8194                f.run(&[b"ZREM", b"z", n]);
8195                f.run(&[b"ARDEL", b"a", n]);
8196            }
8197            if i % 53 == 0 {
8198                // Every type deleted and made again, so a slot goes on the free
8199                // list and comes back holding something else.
8200                f.run(&[b"DEL", b"s2"]);
8201            }
8202            assert_eq!(
8203                f.server.settled_memory(),
8204                f.server.memory_bytes(),
8205                "after round {i}"
8206            );
8207        }
8208
8209        // The run has to have built something, or the two numbers agreeing is
8210        // two zeroes agreeing.
8211        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
8212        assert!(
8213            f.server.memory_bytes() > 512 * 1024,
8214            "{}",
8215            f.server.memory_bytes()
8216        );
8217
8218        // And it survives the collections going away entirely.
8219        f.run(&[b"FLUSHALL"]);
8220        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
8221    }
8222
8223    #[test]
8224    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
8225        // A server with no limit does not keep the running total, so setting a
8226        // limit on a database that is already full has to start it from a walk.
8227        // If it did not, the first reading would be zero and the server would
8228        // think it had all the room in the world.
8229        let mut f = Fixture::new();
8230        for i in 0..200u32 {
8231            let n = i.to_string();
8232            f.run(&[b"SADD", b"s", n.as_bytes()]);
8233            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
8234        }
8235        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
8236        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
8237
8238        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
8239        for i in 200..400u32 {
8240            let n = i.to_string();
8241            f.run(&[b"SADD", b"s", n.as_bytes()]);
8242        }
8243        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
8244        assert_eq!(
8245            f.server.settled_memory(),
8246            f.server.memory_bytes(),
8247            "the writes it was not watching are in the number it started from"
8248        );
8249    }
8250
8251    #[test]
8252    fn evicted_keys_and_expired_keys_are_different_numbers() {
8253        let mut f = Fixture::new();
8254        // Nothing has been evicted and nothing can be under the default policy,
8255        // so this stays at zero while the other one moves.
8256        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
8257        f.server.advance_clock_ms(20);
8258        f.run(&[b"GET", b"gone"]);
8259        let info = f.run(&[b"INFO", b"stats"]);
8260        assert!(info.contains("expired_keys:1"), "{info}");
8261        assert!(info.contains("evicted_keys:0"), "{info}");
8262    }
8263
8264    #[test]
8265    fn the_two_counters_count_the_reads_and_nothing_else() {
8266        let mut f = Fixture::new();
8267        f.run(&[b"SET", b"k", b"v"]);
8268        f.run(&[b"GET", b"k"]);
8269        f.run(&[b"GET", b"nope"]);
8270        f.run(&[b"EXISTS", b"k", b"nope"]);
8271        // The write at the top is not in either number, and the three reads
8272        // under it are, once for each key each of them names.
8273        let info = f.run(&[b"INFO", b"stats"]);
8274        assert!(info.contains("keyspace_hits:2"), "{info}");
8275        assert!(info.contains("keyspace_misses:2"), "{info}");
8276
8277        f.run(&[b"CONFIG", b"RESETSTAT"]);
8278        let info = f.run(&[b"INFO", b"stats"]);
8279        assert!(info.contains("keyspace_hits:0"), "{info}");
8280        assert!(info.contains("keyspace_misses:0"), "{info}");
8281    }
8282
8283    /// The shapes that look one key up more than once, which a real server
8284    /// counts once because it only looks once. See `misses::reading`.
8285    #[test]
8286    fn a_read_that_visits_its_key_twice_is_counted_once() {
8287        let mut f = Fixture::new();
8288        f.run(&[b"ZADD", b"z", b"1", b"m"]);
8289        f.run(&[b"ZRANGE", b"z", b"0", b"-1"]);
8290        f.run(&[b"ZMSCORE", b"z", b"m", b"gone", b"also gone"]);
8291        f.run(&[b"OBJECT", b"ENCODING", b"z"]);
8292        f.run(&[b"DUMP", b"z"]);
8293        let info = f.run(&[b"INFO", b"stats"]);
8294        assert!(info.contains("keyspace_hits:4"), "{info}");
8295        // A member that is not in the sorted set is not a miss. Only a key that
8296        // is not there is one.
8297        assert!(info.contains("keyspace_misses:0"), "{info}");
8298    }
8299
8300    /// A lookup on the way to a write is not a read, which is the other half of
8301    /// what `lookups::quiet` is for.
8302    #[test]
8303    fn the_key_a_read_writes_afterwards_is_not_counted() {
8304        let mut f = Fixture::new();
8305        f.run(&[b"SET", b"s", b"v"]);
8306        f.run(&[b"COPY", b"s", b"dst"]);
8307        f.run(&[b"GETEX", b"s", b"EX", b"100"]);
8308        f.run(&[b"BITOP", b"AND", b"into", b"s", b"nope"]);
8309        let info = f.run(&[b"INFO", b"stats"]);
8310        // The source of the copy, the key `GETEX` answers with, and one of the
8311        // two sources of the operation. The three destinations are written and
8312        // never read, so none of them is in here.
8313        assert!(info.contains("keyspace_hits:3"), "{info}");
8314        assert!(info.contains("keyspace_misses:1"), "{info}");
8315    }
8316
8317    #[test]
8318    fn the_object_subcommands_follow_the_policy() {
8319        let mut f = Fixture::new();
8320        f.run(&[b"SET", b"s", b"v"]);
8321        // Under the default the clock is kept and the counter is not, and under
8322        // an LFU policy it is the other way round. Each subcommand refuses on
8323        // the side where its reading of the three bytes means nothing.
8324        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
8325        assert!(
8326            f.run(&[b"OBJECT", b"FREQ", b"s"])
8327                .starts_with("-ERR An LFU maxmemory policy is not selected"),
8328        );
8329
8330        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
8331        assert!(
8332            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
8333                .starts_with("-ERR An LFU maxmemory policy is selected"),
8334        );
8335        // The key was written under a clock policy, so what comes back is that
8336        // clock read as a counter. It is a number and not an error, which is the
8337        // point: switching at runtime does not invalidate anything, it only makes
8338        // the old field mean something else until the key is used again.
8339        assert!(
8340            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
8341            "FREQ should answer under an LFU policy"
8342        );
8343    }
8344
8345    #[test]
8346    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
8347        let mut f = Fixture::new();
8348        f.run(&[b"SET", b"s", b"hello"]);
8349        f.run(&[b"SET", b"n", b"123"]);
8350        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
8351        f.run(&[b"SADD", b"ss", b"a", b"b"]);
8352        f.run(&[b"HSET", b"h", b"f", b"v"]);
8353        for (key, want) in [
8354            (b"s".as_slice(), "embstr"),
8355            (b"n", "int"),
8356            (b"si", "intset"),
8357            (b"ss", "listpack"),
8358            (b"h", "listpack"),
8359        ] {
8360            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
8361            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
8362        }
8363
8364        // A field deadline widens the blob rather than promoting it, and this
8365        // is the only place a client can see that happen.
8366        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
8367        assert_eq!(
8368            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
8369            "$10\r\nlistpackex\r\n"
8370        );
8371
8372        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
8373        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
8374        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
8375    }
8376
8377    #[test]
8378    fn object_answers_nil_for_a_key_that_is_not_there() {
8379        let mut f = Fixture::new();
8380        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
8381            assert_eq!(
8382                f.run(&[b"OBJECT", sub, b"nokey"]),
8383                "$-1\r\n",
8384                "a nil and not an error, which is what 8.10.1 does"
8385            );
8386        }
8387        // And the key is looked up before FREQ has its complaint, so the
8388        // complaint only reaches a key that exists.
8389        f.run(&[b"SET", b"s", b"v"]);
8390        assert!(
8391            f.run(&[b"OBJECT", b"FREQ", b"s"])
8392                .starts_with("-ERR An LFU maxmemory policy is not"),
8393        );
8394        assert_eq!(
8395            f.run(&[b"OBJECT", b"NOPE", b"s"]),
8396            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
8397        );
8398        assert_eq!(
8399            f.run(&[b"OBJECT", b"ENCODING"]),
8400            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
8401        );
8402        assert_eq!(
8403            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
8404            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
8405        );
8406        assert_eq!(
8407            f.run(&[b"OBJECT"]),
8408            "-ERR wrong number of arguments for 'object' command\r\n"
8409        );
8410    }
8411
8412    #[test]
8413    fn memory_usage_counts_the_record_the_body_and_a_share_of_the_index() {
8414        let mut f = Fixture::new();
8415        assert_eq!(
8416            f.run(&[b"MEMORY", b"USAGE", b"nokey"]),
8417            "$-1\r\n",
8418            "a null and not an error, the same as OBJECT"
8419        );
8420        f.run(&[b"SET", b"s", b"hello"]);
8421        let small = int_of(&f.run(&[b"MEMORY", b"USAGE", b"s"]));
8422        assert!(
8423            small > 5,
8424            "the value is in there and so are the name and the header"
8425        );
8426        // A longer value under the same name costs more, and by about what the
8427        // extra bytes are, since a string lives in its own record.
8428        f.run(&[b"SET", b"s", &[b'x'; 1000]]);
8429        let big = int_of(&f.run(&[b"MEMORY", b"USAGE", b"s"]));
8430        assert!(
8431            big - small >= 995 && big - small <= 1005,
8432            "{small} then {big}"
8433        );
8434        // A collection costs its body, so a set of a hundred members is worth
8435        // far more than a set of one.
8436        f.run(&[b"SADD", b"one", b"a"]);
8437        f.run(&[b"SADD", b"many", b"a"]);
8438        for i in 0..100u32 {
8439            f.run(&[b"SADD", b"many", format!("member:{i}").as_bytes()]);
8440        }
8441        assert!(
8442            int_of(&f.run(&[b"MEMORY", b"USAGE", b"many"]))
8443                > int_of(&f.run(&[b"MEMORY", b"USAGE", b"one"]))
8444        );
8445        // Asking twice gives the same answer, which is the property a sampled
8446        // estimate does not have.
8447        assert_eq!(
8448            f.run(&[b"MEMORY", b"USAGE", b"many"]),
8449            f.run(&[b"MEMORY", b"USAGE", b"many"])
8450        );
8451    }
8452
8453    #[test]
8454    fn memory_usage_reads_samples_and_does_not_use_it() {
8455        let mut f = Fixture::new();
8456        f.run(&[b"SET", b"s", b"v"]);
8457        let plain = f.run(&[b"MEMORY", b"USAGE", b"s"]);
8458        for count in [b"0".as_slice(), b"1", b"5", b"1000"] {
8459            assert_eq!(
8460                f.run(&[b"MEMORY", b"USAGE", b"s", b"SAMPLES", count]),
8461                plain
8462            );
8463        }
8464        // The last one wins, which is what the reference's loop does rather
8465        // than something it decided to do.
8466        assert_eq!(
8467            f.run(&[
8468                b"MEMORY", b"USAGE", b"s", b"SAMPLES", b"1", b"SAMPLES", b"2"
8469            ]),
8470            plain
8471        );
8472        assert_eq!(
8473            f.run(&[b"MEMORY", b"USAGE", b"s", b"SAMPLES"]),
8474            "-ERR syntax error\r\n"
8475        );
8476        assert_eq!(
8477            f.run(&[b"MEMORY", b"USAGE", b"s", b"SAMPLES", b"-1"]),
8478            "-ERR syntax error\r\n"
8479        );
8480        assert_eq!(
8481            f.run(&[b"MEMORY", b"USAGE", b"s", b"SAMPLES", b"nine"]),
8482            "-ERR value is not an integer or out of range\r\n"
8483        );
8484        assert_eq!(
8485            f.run(&[b"MEMORY", b"USAGE", b"s", b"BAD", b"1"]),
8486            "-ERR syntax error\r\n"
8487        );
8488        assert_eq!(
8489            f.run(&[b"MEMORY", b"USAGE"]),
8490            "-ERR wrong number of arguments for 'memory|usage' command\r\n"
8491        );
8492    }
8493
8494    #[test]
8495    fn memory_stats_grows_a_field_for_every_database_holding_a_key() {
8496        let mut f = Fixture::new();
8497        assert!(
8498            f.run(&[b"MEMORY", b"STATS"]).starts_with("*72\r\n"),
8499            "thirty six pairs on a server nobody has written to"
8500        );
8501        f.run(&[b"SET", b"a", b"1"]);
8502        assert!(f.run(&[b"MEMORY", b"STATS"]).starts_with("*74\r\n"));
8503        f.run(&[b"SELECT", b"7"]);
8504        f.run(&[b"SET", b"b", b"2"]);
8505        let reply = f.run(&[b"MEMORY", b"STATS"]);
8506        assert!(reply.starts_with("*76\r\n"));
8507        assert!(reply.contains("\r\n$4\r\ndb.0\r\n"));
8508        assert!(reply.contains("\r\n$4\r\ndb.7\r\n"));
8509        // And the row for a database is the pair a real server puts there.
8510        assert!(reply.contains("overhead.hashtable.main"));
8511        assert!(reply.contains("overhead.hashtable.expires"));
8512        assert!(reply.contains("fragmentation.bytes"));
8513    }
8514
8515    #[test]
8516    fn memory_answers_the_four_that_only_look() {
8517        let mut f = Fixture::new();
8518        assert!(f.run(&[b"MEMORY", b"HELP"]).starts_with("*14\r\n+MEMORY "));
8519        assert_eq!(f.run(&[b"MEMORY", b"PURGE"]), "+OK\r\n");
8520        assert_eq!(
8521            f.run(&[b"MEMORY", b"MALLOC-STATS"]),
8522            "$45\r\nStats not supported for the current allocator\r\n"
8523        );
8524        // An empty server is one the doctor will not form an opinion about, and
8525        // it says so in Sam's own words.
8526        assert!(
8527            f.run(&[b"MEMORY", b"DOCTOR"])
8528                .contains("my issues detector can't be used in these conditions")
8529        );
8530        assert_eq!(
8531            f.run(&[b"MEMORY", b"NOPE"]),
8532            "-ERR unknown subcommand 'NOPE'. Try MEMORY HELP.\r\n"
8533        );
8534        for sub in [
8535            b"STATS".as_slice(),
8536            b"DOCTOR",
8537            b"PURGE",
8538            b"MALLOC-STATS",
8539            b"HELP",
8540        ] {
8541            let name = String::from_utf8_lossy(sub).to_lowercase();
8542            assert_eq!(
8543                f.run(&[b"MEMORY", sub, b"extra"]),
8544                format!("-ERR wrong number of arguments for 'memory|{name}' command\r\n"),
8545                "the subcommand is named and not the container"
8546            );
8547        }
8548        assert_eq!(
8549            f.run(&[b"MEMORY"]),
8550            "-ERR wrong number of arguments for 'memory' command\r\n"
8551        );
8552    }
8553
8554    #[test]
8555    fn command_getkeys_finds_the_key_memory_usage_names() {
8556        let mut f = Fixture::new();
8557        assert_eq!(
8558            f.run(&[b"COMMAND", b"GETKEYS", b"MEMORY", b"USAGE", b"k"]),
8559            "*1\r\n$1\r\nk\r\n"
8560        );
8561        // And the subcommands that name none say so rather than answering an
8562        // empty list.
8563        assert!(
8564            f.run(&[b"COMMAND", b"GETKEYS", b"MEMORY", b"DOCTOR"])
8565                .starts_with("-ERR ")
8566        );
8567    }
8568
8569    #[test]
8570    fn config_moves_the_ladder_and_object_encoding_agrees() {
8571        let mut f = Fixture::new();
8572        assert_eq!(
8573            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
8574            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
8575            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
8576        );
8577        // The old spelling is the same number under a different name, and a
8578        // glob that catches both sends both.
8579        assert_eq!(
8580            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
8581            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
8582        );
8583        assert!(
8584            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
8585                .starts_with("*8\r\n")
8586        );
8587        assert!(
8588            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
8589                .starts_with("*6\r\n")
8590        );
8591
8592        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
8593        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
8594
8595        assert_eq!(
8596            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
8597            "+OK\r\n",
8598            "written under the old name and read back under the new one"
8599        );
8600        assert_eq!(
8601            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
8602            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
8603        );
8604        assert_eq!(
8605            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
8606            "$8\r\nlistpack\r\n",
8607            "the hash that already exists is left exactly where it was"
8608        );
8609        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
8610        assert_eq!(
8611            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
8612            "$9\r\nhashtable\r\n",
8613            "and the next one built goes straight to a table"
8614        );
8615
8616        // The set has three of these and all three move.
8617        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
8618        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
8619        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
8620        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
8621        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
8622        assert_eq!(
8623            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
8624            "$9\r\nhashtable\r\n"
8625        );
8626    }
8627
8628    #[test]
8629    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
8630        let mut f = Fixture::new();
8631        assert_eq!(
8632            f.run(&[
8633                b"CONFIG",
8634                b"SET",
8635                b"hash-max-listpack-entries",
8636                b"7",
8637                b"set-max-listpack-entries",
8638                b"abc"
8639            ]),
8640            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
8641        );
8642        assert_eq!(
8643            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
8644            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
8645            "the pair in front of the bad one did not go in"
8646        );
8647        // The name in the complaint is the one that was typed, so the old
8648        // spelling comes back as the old spelling.
8649        assert_eq!(
8650            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
8651            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
8652        );
8653        assert_eq!(
8654            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
8655            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
8656        );
8657        // A number past what an i64 holds is the parse complaint and not the
8658        // range one, which is upstream reading it before it checks it.
8659        assert_eq!(
8660            f.run(&[
8661                b"CONFIG",
8662                b"SET",
8663                b"set-max-intset-entries",
8664                b"99999999999999999999"
8665            ]),
8666            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
8667        );
8668        assert_eq!(
8669            f.run(&[
8670                b"CONFIG",
8671                b"SET",
8672                b"set-max-intset-entries",
8673                b"9223372036854775807"
8674            ]),
8675            "+OK\r\n"
8676        );
8677    }
8678
8679    #[test]
8680    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
8681        let mut f = Fixture::new();
8682        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
8683        f.run(&[b"SELECT", b"3"]);
8684        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
8685        assert_eq!(
8686            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
8687            "$9\r\nhashtable\r\n",
8688            "these are one server wide number in Redis, whatever a Keyspace carries"
8689        );
8690    }
8691
8692    #[test]
8693    fn info_reports_the_numbers_it_can_stand_behind() {
8694        let mut f = Fixture::new();
8695        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
8696        let all = f.run(&[b"INFO"]);
8697        assert!(all.contains("redis_version:8.8.0"), "{all}");
8698        assert!(
8699            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
8700            "{all}"
8701        );
8702        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
8703        assert!(all.contains("role:master"), "{all}");
8704        // One section is one section.
8705        let clients = f.run(&[b"INFO", b"clients"]);
8706        assert!(clients.contains("connected_clients:0"), "{clients}");
8707        assert!(!clients.contains("redis_version"), "{clients}");
8708        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
8709    }
8710
8711    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
8712    ///
8713    /// This is Redis's `unit/info-command` written against the fixture. Every
8714    /// assertion in it is one of theirs, in their order, and the two fields it
8715    /// turns on are the two that suite was failing on: `master_repl_offset`,
8716    /// which is in the default set, and `rejected_calls`, which is not.
8717    #[test]
8718    fn commandstats_is_asked_for_and_replication_is_not() {
8719        let mut f = Fixture::new();
8720        for arg in ["", "all", "default", "everything"] {
8721            let info = if arg.is_empty() {
8722                f.run(&[b"INFO"])
8723            } else {
8724                f.run(&[b"INFO", arg.as_bytes()])
8725            };
8726            assert!(info.contains("redis_version"), "{arg}: {info}");
8727            assert!(info.contains("used_cpu_user"), "{arg}: {info}");
8728            assert!(info.contains("used_memory"), "{arg}: {info}");
8729            assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
8730            let asked = arg == "all" || arg == "everything";
8731            assert_eq!(
8732                info.contains("rejected_calls"),
8733                asked,
8734                "{arg} should{} carry the command counters: {info}",
8735                if asked { "" } else { " not" }
8736            );
8737        }
8738
8739        let cpu = f.run(&[b"INFO", b"cpu"]);
8740        assert!(cpu.contains("used_cpu_user"), "{cpu}");
8741        assert!(!cpu.contains("used_memory"), "{cpu}");
8742
8743        // Their case, to make the point that a section name is not case
8744        // sensitive any more than a command name is.
8745        let stats = f.run(&[b"INFO", b"commandSTATS"]);
8746        assert!(!stats.contains("used_memory"), "{stats}");
8747        assert!(stats.contains("rejected_calls"), "{stats}");
8748
8749        // Two sections named, and neither of them pulls in a third.
8750        let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
8751        assert!(pair.contains("used_cpu_user"), "{pair}");
8752        assert!(!pair.contains("master_repl_offset"), "{pair}");
8753
8754        let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
8755        assert!(with_all.contains("used_memory"), "{with_all}");
8756        assert!(with_all.contains("master_repl_offset"), "{with_all}");
8757        assert!(with_all.contains("rejected_calls"), "{with_all}");
8758        // A section named twice is still written once.
8759        assert_eq!(
8760            with_all.matches("used_cpu_user_children").count(),
8761            1,
8762            "{with_all}"
8763        );
8764
8765        let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
8766        assert!(with_default.contains("used_memory"), "{with_default}");
8767        assert!(
8768            with_default.contains("master_repl_offset"),
8769            "{with_default}"
8770        );
8771        assert!(!with_default.contains("rejected_calls"), "{with_default}");
8772        assert_eq!(
8773            with_default.matches("used_cpu_user_children").count(),
8774            1,
8775            "{with_default}"
8776        );
8777    }
8778
8779    /// The threads section is the sum taken apart again.
8780    ///
8781    /// A connection belongs to the thread that accepted it for as long as it is
8782    /// open, so how the connections landed decides who does the work, and every
8783    /// other number in `INFO` adds the threads up before anybody sees it. This
8784    /// is the one place the split itself is visible. The test runs on one
8785    /// thread, so what it can show is that the section has a row per thread, and
8786    /// that the work it did all landed in one of them and adds back up to the
8787    /// total.
8788    #[test]
8789    fn the_threads_section_says_where_the_work_landed() {
8790        let mut server = Server::new();
8791        server.set_threads(4);
8792        let mut f = Fixture::on(server);
8793        for _ in 0..3 {
8794            f.run(&[b"PING"]);
8795        }
8796
8797        assert!(!f.run(&[b"INFO"]).contains("# Threads"));
8798        assert!(f.run(&[b"INFO", b"all"]).contains("# Threads"));
8799
8800        let info = f.run(&[b"INFO", b"threads"]);
8801        assert!(info.contains("io_threads:4"), "{info}");
8802        for at in 0..4 {
8803            assert!(info.contains(&format!("thread_{at}:clients=")), "{info}");
8804        }
8805        assert!(!info.contains("thread_4:"), "{info}");
8806
8807        let per = f.server.per_thread();
8808        assert_eq!(per.len(), 4);
8809        assert_eq!(
8810            per.iter().map(|t| t.commands).sum::<u64>(),
8811            f.server.totals().commands
8812        );
8813        assert_eq!(per.iter().filter(|t| t.commands > 0).count(), 1, "{per:?}");
8814    }
8815
8816    /// The memory section says what this process may use, not what the machine
8817    /// has.
8818    ///
8819    /// The distinction is the whole point of it. A server inside a container
8820    /// that reports the host's memory is a server whose operator sizes it for
8821    /// memory it will be killed for touching, so all three numbers are there:
8822    /// what the machine has, what the cgroup allows, and the quarter of the
8823    /// tighter one that pools are sized from.
8824    #[test]
8825    fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
8826        let mut f = Fixture::new();
8827        let info = f.run(&[b"INFO", b"memory"]);
8828        for field in [
8829            "total_system_memory:",
8830            "mem_cgroup_limit:",
8831            "mem_limit:",
8832            "mem_budget:",
8833        ] {
8834            assert!(info.contains(field), "no {field} in {info}");
8835        }
8836
8837        let field = |name: &str| -> u64 {
8838            info.lines()
8839                .find_map(|l| l.strip_prefix(name))
8840                .unwrap_or_else(|| panic!("no {name} in {info}"))
8841                .trim()
8842                .parse()
8843                .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
8844        };
8845        let limit = field("mem_limit:");
8846        assert_eq!(field("mem_budget:"), limit / 4, "{info}");
8847        // Zero means there is no limit to report, which is a real answer on a
8848        // machine with no cgroups and no way to ask how big it is.
8849        if limit != 0 {
8850            let host = field("total_system_memory:");
8851            let cgroup = field("mem_cgroup_limit:");
8852            assert!(
8853                limit == host || limit == cgroup,
8854                "the limit came from neither number: {info}"
8855            );
8856        }
8857    }
8858
8859    /// The three counters, each on the path that raises it.
8860    ///
8861    /// `calls` on a command that worked, `failed_calls` on one that ran and
8862    /// answered with an error, and `rejected_calls` on one that never ran at
8863    /// all. The last two are the pair that is easy to collapse into one number
8864    /// and that Redis keeps apart, because a client sending the wrong number of
8865    /// arguments and a client asking for a list element that is not there are
8866    /// not the same problem.
8867    #[test]
8868    fn a_command_counts_what_it_did_separately_from_what_it_refused() {
8869        let mut f = Fixture::new();
8870        f.run(&[b"SET", b"k", b"v"]);
8871        f.run(&[b"SET", b"k", b"w"]);
8872        // Ran, and answered with an error, because `k` is not a list.
8873        f.run(&[b"LPUSH", b"k", b"x"]);
8874        // Never ran: `LPUSH` takes at least three arguments.
8875        f.run(&[b"LPUSH", b"k"]);
8876
8877        let stats = f.run(&[b"INFO", b"commandstats"]);
8878        assert!(
8879            stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
8880            "{stats}"
8881        );
8882        assert!(
8883            stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
8884            "{stats}"
8885        );
8886        assert!(
8887            !stats.contains("cmdstat_zadd"),
8888            "a command nobody has sent has no row: {stats}"
8889        );
8890    }
8891
8892    /// A cache that writes with a deadline and never reads back used to hold
8893    /// every key it had ever written, because lazy expiry needs somebody to walk
8894    /// past a key before it can reclaim it and nobody ever did.
8895    #[test]
8896    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
8897        // Four thousand keys is four thousand trips through dispatch, and what
8898        // Miri charges for is trips rather than keys, so this was over five
8899        // minutes there. An eighth of each keeps everything the test is about,
8900        // which is three keys with a deadline for every one without and a
8901        // sweep that has to reclaim all of the first kind and none of the
8902        // second.
8903        let (dead, live) = if cfg!(miri) {
8904            (375, 125)
8905        } else {
8906            (3_000, 1_000)
8907        };
8908        let mut f = Fixture::new();
8909        for i in 0..dead {
8910            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
8911        }
8912        for i in 0..live {
8913            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
8914        }
8915        let all = format!(":{}\r\n", dead + live);
8916        assert_eq!(f.run(&[b"DBSIZE"]), all);
8917        f.advance(100);
8918        assert_eq!(
8919            f.run(&[b"DBSIZE"]),
8920            all,
8921            "DBSIZE counts records and nothing has read past the dead ones yet"
8922        );
8923
8924        // What the shard loop does, one slice at a time.
8925        let rest = format!(":{live}\r\n");
8926        let mut spent = 0;
8927        for _ in 0..2_000 {
8928            spent += f.server.expire_step(4096);
8929            if f.run(&[b"DBSIZE"]) == rest {
8930                break;
8931            }
8932        }
8933        assert_eq!(f.run(&[b"DBSIZE"]), rest, "spent {spent} looks");
8934        assert!(
8935            f.run(&[b"INFO", b"stats"])
8936                .contains(&format!("expired_keys:{dead}"))
8937        );
8938        for i in 0..live {
8939            assert_eq!(
8940                f.run(&[b"GET", format!("k{i}").as_bytes()]),
8941                "$1\r\nv\r\n",
8942                "it took a key that had no deadline"
8943            );
8944        }
8945    }
8946
8947    #[test]
8948    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
8949        // The keys are only here so that the database the sweep walks is not an
8950        // empty one. Two hundred of them fills as many slots as a sweep looks
8951        // at and is a tenth of the interpreted work.
8952        let n = if cfg!(miri) { 200 } else { 2_000 };
8953        let mut f = Fixture::new();
8954        for i in 0..n {
8955            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
8956        }
8957        assert_eq!(f.server.expire_step(4096), 0);
8958        // And one database having them does not make the other fifteen pay.
8959        f.run(&[b"SELECT", b"3"]);
8960        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
8961        f.advance(100);
8962        for _ in 0..64 {
8963            f.server.expire_step(4096);
8964        }
8965        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8966        f.run(&[b"SELECT", b"0"]);
8967        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{n}\r\n"));
8968        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
8969    }
8970
8971    /// The gate, which is what stops a maintenance slice that runs every hundred
8972    /// nanoseconds from drawing a sample every hundred nanoseconds.
8973    #[test]
8974    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
8975        let mut f = Fixture::new();
8976        for i in 0..500u32 {
8977            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
8978        }
8979        f.advance(100);
8980        let at = f.server.striped(0).now_ms();
8981        f.server.set_clock_ms(at);
8982        // A small budget, so that one slice cannot finish the job and a second
8983        // one having nothing to do would mean the gate and not an empty
8984        // database.
8985        assert!(f.server.expire_slice(8) > 0, "the first one works");
8986        for _ in 0..1_000 {
8987            assert_eq!(
8988                f.server.expire_slice(8),
8989                0,
8990                "the millisecond has not moved and neither should this"
8991            );
8992        }
8993        assert!(
8994            f.server.striped(0).expires() > 400,
8995            "there is plenty left to take"
8996        );
8997        f.server.set_clock_ms(at + 1);
8998        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
8999    }
9000
9001    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
9002    /// how much of a cache is volatile was reading a constant.
9003    #[test]
9004    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
9005        let mut f = Fixture::new();
9006        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
9007        assert!(
9008            f.run(&[b"INFO", b"keyspace"])
9009                .contains("db0:keys=3,expires=0"),
9010            "none of them has one yet"
9011        );
9012        f.run(&[b"EXPIRE", b"a", b"1000"]);
9013        f.run(&[b"EXPIRE", b"b", b"1000"]);
9014        let two = f.run(&[b"INFO", b"keyspace"]);
9015        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
9016        f.run(&[b"PERSIST", b"a"]);
9017        f.run(&[b"DEL", b"b"]);
9018        let none = f.run(&[b"INFO", b"keyspace"]);
9019        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
9020
9021        // Each database answers for itself, the way Redis reports it.
9022        f.run(&[b"SELECT", b"1"]);
9023        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
9024        let both = f.run(&[b"INFO", b"keyspace"]);
9025        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
9026        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
9027    }
9028
9029    /// Not under Miri, which reads a zero on purpose because it has no
9030    /// `getrusage` to call, so the second half of this would burn a billion
9031    /// interpreted multiplications waiting for a number that is never going to
9032    /// move. The first half, that the section is there and has the fields Redis
9033    /// clients look for, is checked by the `INFO` tests above as well, and
9034    /// those do run there.
9035    #[cfg(unix)]
9036    #[cfg_attr(miri, ignore = "no getrusage under Miri, so the number is fixed")]
9037    #[test]
9038    fn info_cpu_reports_processor_time_that_was_really_measured() {
9039        let mut f = Fixture::new();
9040        let cpu = f.run(&[b"INFO", b"cpu"]);
9041        assert!(cpu.contains("# CPU"), "{cpu}");
9042        // Redis's unit/info-command asks for this one by name in three tests.
9043        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
9044        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
9045        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
9046        assert!(!cpu.contains("redis_version"), "{cpu}");
9047
9048        // It is a measurement and not a constant, so it goes up when work
9049        // happens. A tight loop rather than a sleep, because sleeping is the
9050        // one thing that does not move this number.
9051        let before = used_cpu_user(&cpu);
9052        let mut n = 0u64;
9053        let mut rounds = 0;
9054        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
9055            for i in 0..1_000_000u64 {
9056                n = n.wrapping_add(i.wrapping_mul(i));
9057            }
9058            rounds += 1;
9059            // A bound rather than a spin, so a platform where this number does
9060            // not move fails here instead of hanging. Even a clock with whole
9061            // millisecond granularity gets there in the first round or two.
9062            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
9063        }
9064    }
9065
9066    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
9067    #[cfg(unix)]
9068    fn used_cpu_user(info: &str) -> f64 {
9069        info.lines()
9070            .find_map(|l| l.strip_prefix("used_cpu_user:"))
9071            .expect("no used_cpu_user in the reply")
9072            .trim()
9073            .parse()
9074            .expect("used_cpu_user is not a number")
9075    }
9076
9077    /// The safety net under the rule that a body checks its arguments before
9078    /// it writes anything. `MGET` writes its array header first and then reads
9079    /// each key, so if a later argument could fail the header would already be
9080    /// out. Nothing in the string group does that today and this is what would
9081    /// catch the first one that did.
9082    #[test]
9083    fn a_command_that_fails_leaves_nothing_half_written() {
9084        let mut f = Fixture::new();
9085        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
9086        assert_eq!(reply, "-ERR offset is out of range\r\n");
9087        assert!(!reply.contains(':'), "no integer went out in front of it");
9088    }
9089
9090    #[test]
9091    fn quit_answers_first_and_closes_after() {
9092        let mut f = Fixture::new();
9093        let (flow, reply) = f.flow(&[b"QUIT"]);
9094        assert_eq!(reply, "+OK\r\n");
9095        assert_eq!(flow, Flow::Close);
9096    }
9097
9098    /// A server that has not been asked to stop is not stopping, and one that
9099    /// has says so without writing anything back.
9100    ///
9101    /// The empty reply is the point. Redis answers nothing at all here and the
9102    /// client sees the socket close, and an `OK` would be a promise from a
9103    /// process that is about to not exist.
9104    #[test]
9105    fn shutdown_writes_nothing_and_sets_the_flag() {
9106        let mut f = Fixture::new();
9107        assert!(!f.server.stopping(), "nobody has asked yet");
9108
9109        let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
9110        assert_eq!(reply, "");
9111        assert_eq!(flow, Flow::Close);
9112        assert!(f.server.stopping());
9113    }
9114
9115    /// Every flag combination 8.10.1 takes, and every one it refuses.
9116    ///
9117    /// The refusals are the half worth pinning down. `SAVE` and `NOSAVE`
9118    /// contradict each other, `ABORT` says to do nothing so it cannot be
9119    /// combined with a word about how to do it, and repeating any one of them
9120    /// is fine. All of it was read off a running 8.10.1 rather than worked out
9121    /// from the documentation, which does not say.
9122    ///
9123    /// The fixtures here save into a directory of their own because two of the
9124    /// combinations carry `SAVE`, and a test that writes a file into whatever
9125    /// directory the test runner happened to start in leaves it there.
9126    #[test]
9127    fn shutdown_takes_the_flags_redis_takes() {
9128        let s = Saves::new("shutdown-flags");
9129        for flags in [
9130            &[b"NOSAVE".as_slice()][..],
9131            &[b"SAVE"],
9132            &[b"NOW"],
9133            &[b"FORCE"],
9134            &[b"nosave"],
9135            &[b"NOW", b"NOW"],
9136            &[b"SAVE", b"SAVE"],
9137            &[b"NOSAVE", b"NOW", b"FORCE"],
9138        ] {
9139            let mut f = Fixture::new();
9140            f.server.set_dir(s.dir.clone());
9141            let mut parts = vec![b"SHUTDOWN".as_slice()];
9142            parts.extend_from_slice(flags);
9143            let (flow, reply) = f.flow(&parts);
9144            assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
9145            assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
9146            assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
9147        }
9148
9149        for flags in [
9150            &[b"BOGUS".as_slice()][..],
9151            &[b"SAVE", b"NOSAVE"],
9152            &[b"NOSAVE", b"SAVE"],
9153            &[b"ABORT", b"NOW"],
9154            &[b"NOSAVE", b"ABORT"],
9155            &[b"NOW", b"FORCE", b"ABORT"],
9156        ] {
9157            let mut f = Fixture::new();
9158            let mut parts = vec![b"SHUTDOWN".as_slice()];
9159            parts.extend_from_slice(flags);
9160            assert_eq!(
9161                f.run(&parts),
9162                "-ERR syntax error\r\n",
9163                "SHUTDOWN {flags:?} was accepted"
9164            );
9165            assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
9166        }
9167    }
9168
9169    /// `ABORT` has nothing to call off, ever.
9170    ///
9171    /// A shutdown here is decided and done inside one turn of the loop, so
9172    /// there is no window in which one is in progress. That makes Redis's
9173    /// message for a cancel with nothing to cancel the right answer every time
9174    /// rather than only when nothing happens to be pending. Two `ABORT`s is
9175    /// still one `ABORT`, which is what 8.10.1 does.
9176    #[test]
9177    fn shutdown_abort_never_has_anything_to_abort() {
9178        let mut f = Fixture::new();
9179        for parts in [
9180            &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
9181            &[b"SHUTDOWN", b"ABORT", b"ABORT"],
9182        ] {
9183            assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
9184            assert!(!f.server.stopping(), "an abort stopped the server");
9185        }
9186    }
9187
9188    /// A fixture whose server writes into a directory of its own.
9189    ///
9190    /// Every test here really writes files, because the whole point of the
9191    /// command is the files and a backup that is only a state machine would
9192    /// pass a test suite and fail the first person who tried to restore one.
9193    /// The directory carries the test's name so that the suite can run its
9194    /// tests in parallel the way it always does.
9195    struct Backups {
9196        f: Fixture,
9197        dir: PathBuf,
9198    }
9199
9200    impl Backups {
9201        fn new(name: &str) -> Backups {
9202            let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
9203            let _ = std::fs::remove_dir_all(&dir);
9204            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
9205            let mut f = Fixture::new();
9206            f.server.set_dir(dir.clone());
9207            Backups { f, dir }
9208        }
9209
9210        fn run(&mut self, parts: &[&[u8]]) -> String {
9211            self.f.run(parts)
9212        }
9213
9214        /// The names in `backupdir`, sorted, so a test can say what is on disk.
9215        fn files(&self) -> Vec<String> {
9216            let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
9217                Ok(entries) => entries
9218                    .filter_map(|e| e.ok())
9219                    .map(|e| e.file_name().to_string_lossy().into_owned())
9220                    .collect(),
9221                Err(_) => Vec::new(),
9222            };
9223            names.sort();
9224            names
9225        }
9226
9227        fn read(&self, name: &str) -> Vec<u8> {
9228            std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
9229        }
9230    }
9231
9232    impl Drop for Backups {
9233        fn drop(&mut self) {
9234            let _ = std::fs::remove_dir_all(&self.dir);
9235        }
9236    }
9237
9238    /// The four states and the moves between them, in the order a client walks
9239    /// them, with the files checked at every step.
9240    #[test]
9241    fn backup_walks_the_states_the_reference_walks() {
9242        let mut b = Backups::new("states");
9243        let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
9244
9245        assert!(status(&mut b).contains("idle"));
9246        assert!(b.files().is_empty(), "an idle server has written a backup");
9247
9248        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
9249        assert!(status(&mut b).contains("incrementing"));
9250        assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
9251
9252        assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
9253        assert!(status(&mut b).contains("sealed"));
9254        assert_eq!(
9255            b.files(),
9256            [
9257                "appendonly.aof.1.base.rdb",
9258                "appendonly.aof.1.incr.aof",
9259                "appendonly.aof.manifest",
9260            ]
9261        );
9262
9263        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
9264        assert!(status(&mut b).contains("idle"));
9265        assert!(b.files().is_empty(), "cleanup left something behind");
9266    }
9267
9268    /// Every move that is refused, in the reference's words.
9269    #[test]
9270    fn backup_refuses_the_moves_the_reference_refuses() {
9271        let mut b = Backups::new("refusals");
9272
9273        assert_eq!(
9274            b.run(&[b"BACKUP", b"SEAL"]),
9275            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
9276        );
9277        assert_eq!(
9278            b.run(&[b"BACKUP", b"ABORT"]),
9279            "-ERR No backup in progress\r\n"
9280        );
9281        // Cleanup from idle is not an error, it is a way of saying there was
9282        // nothing to clean up.
9283        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
9284
9285        b.run(&[b"BACKUP", b"START"]);
9286        assert_eq!(
9287            b.run(&[b"BACKUP", b"START"]),
9288            "-ERR A backup is already in progress, ABORT it first\r\n"
9289        );
9290        assert_eq!(
9291            b.run(&[b"BACKUP", b"CLEANUP"]),
9292            "-ERR Backup is in progress\r\n"
9293        );
9294
9295        b.run(&[b"BACKUP", b"SEAL"]);
9296        assert_eq!(
9297            b.run(&[b"BACKUP", b"START"]),
9298            "-ERR A sealed backup exists, CLEANUP it first\r\n"
9299        );
9300        assert_eq!(
9301            b.run(&[b"BACKUP", b"SEAL"]),
9302            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
9303        );
9304        assert_eq!(
9305            b.run(&[b"BACKUP", b"ABORT"]),
9306            "-ERR No backup in progress\r\n"
9307        );
9308    }
9309
9310    /// An abort takes the base file away and leaves a state saying who did it.
9311    ///
9312    /// The next backup takes the next sequence number rather than reusing the
9313    /// one whose files were just thrown away, so a directory somebody copied a
9314    /// half finished backup out of cannot end up with two different files under
9315    /// one name.
9316    #[test]
9317    fn backup_abort_removes_the_file_and_says_who_did_it() {
9318        let mut b = Backups::new("abort");
9319        b.run(&[b"BACKUP", b"START"]);
9320        assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
9321
9322        let status = b.run(&[b"BACKUP", b"STATUS"]);
9323        assert!(status.contains("failed"), "{status}");
9324        assert!(status.contains("aborted by user"), "{status}");
9325        assert!(b.files().is_empty(), "abort left the base file behind");
9326        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
9327
9328        // A start from failed works, and is the second backup.
9329        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
9330        assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
9331        let status = b.run(&[b"BACKUP", b"STATUS"]);
9332        assert!(status.contains("incrementing"), "{status}");
9333        assert!(!status.contains("aborted"), "the old error was kept");
9334    }
9335
9336    /// `LIST` names nothing, then one file, then three, and they are absolute.
9337    #[test]
9338    fn backup_list_names_the_files_that_are_pinned_so_far() {
9339        let mut b = Backups::new("list");
9340        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
9341
9342        b.run(&[b"BACKUP", b"START"]);
9343        let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
9344        let base = base.to_string_lossy().into_owned();
9345        assert_eq!(
9346            b.run(&[b"BACKUP", b"LIST"]),
9347            format!("*1\r\n${}\r\n{base}\r\n", base.len())
9348        );
9349
9350        b.run(&[b"BACKUP", b"SEAL"]);
9351        let listed = b.run(&[b"BACKUP", b"LIST"]);
9352        assert!(listed.starts_with("*3\r\n"), "{listed}");
9353        // The order is the manifest's order, base then incremental then the
9354        // manifest itself, which is the order a restore needs them in.
9355        let names: Vec<&str> = listed
9356            .lines()
9357            .filter(|l| l.starts_with('/') || l.contains(":\\"))
9358            .collect();
9359        assert_eq!(names.len(), 3, "{listed}");
9360        assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
9361        assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
9362        assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
9363    }
9364
9365    /// The base file is the dataset as it was at `START` and not at `SEAL`.
9366    ///
9367    /// That is D-46 and it is the one thing about this a client can notice, so
9368    /// it is pinned here rather than left to be discovered by whoever restores
9369    /// one. The incremental file is empty for the same reason: there is no
9370    /// append only log underneath this server to copy the writes in between out
9371    /// of.
9372    #[test]
9373    fn a_backup_holds_the_dataset_as_it_was_at_start() {
9374        let mut b = Backups::new("contents");
9375        b.run(&[b"SET", b"bk", b"v1"]);
9376        b.run(&[b"BACKUP", b"START"]);
9377        b.run(&[b"SET", b"bk", b"v2"]);
9378        b.run(&[b"BACKUP", b"SEAL"]);
9379
9380        let base = b.read("appendonly.aof.1.base.rdb");
9381        assert!(base.starts_with(b"REDIS"), "not an RDB file");
9382        assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
9383        assert!(
9384            !base.windows(2).any(|w| w == b"v2"),
9385            "the base file moved on after START"
9386        );
9387        // The aux field a loader acts on, and the one that says this file is
9388        // the base of an append only file rather than a standalone dump. Its
9389        // value is the one byte string 1, which the encoder writes as an
9390        // integer the way a real server writes it.
9391        let at = base
9392            .windows(8)
9393            .position(|w| w == b"aof-base")
9394            .expect("no aof-base aux field");
9395        assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
9396
9397        assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
9398        assert_eq!(
9399            String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
9400            "file appendonly.aof.1.base.rdb seq 1 type b\n\
9401             file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
9402        );
9403    }
9404
9405    /// `STATUS` is a map of four pairs on RESP3 and the same pairs flat on
9406    /// RESP2, which is what every other map shaped reply in this server does.
9407    #[test]
9408    fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
9409        let mut b = Backups::new("status");
9410        b.f.server.set_clock_ms(1_700_000_000_000);
9411
9412        assert_eq!(
9413            b.run(&[b"BACKUP", b"STATUS"]),
9414            "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
9415             $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
9416        );
9417
9418        b.f.out = Out::new(Proto::Resp3);
9419        b.run(&[b"BACKUP", b"START"]);
9420        assert_eq!(
9421            b.run(&[b"BACKUP", b"STATUS"]),
9422            "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
9423             $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
9424        );
9425
9426        b.run(&[b"BACKUP", b"SEAL"]);
9427        let sealed = b.run(&[b"BACKUP", b"STATUS"]);
9428        assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
9429    }
9430
9431    /// A sealed backup that nobody cleans up goes away on its own once
9432    /// `backup-sealed-ttl` seconds have passed since the seal.
9433    #[test]
9434    fn a_sealed_backup_is_swept_away_after_the_timeout() {
9435        let mut b = Backups::new("ttl");
9436        b.f.server.set_clock_ms(1_000_000);
9437        assert_eq!(
9438            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
9439            "+OK\r\n"
9440        );
9441        b.run(&[b"BACKUP", b"START"]);
9442        b.run(&[b"BACKUP", b"SEAL"]);
9443
9444        // A minute short of the deadline, nothing happens.
9445        b.f.server.set_clock_ms(1_000_000 + 59_000);
9446        b.f.server.backup_expire();
9447        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
9448        assert_eq!(b.files().len(), 3);
9449
9450        b.f.server.set_clock_ms(1_000_000 + 60_000);
9451        b.f.server.backup_expire();
9452        let status = b.run(&[b"BACKUP", b"STATUS"]);
9453        assert!(status.contains("idle"), "{status}");
9454        assert!(b.files().is_empty(), "the timeout left the files behind");
9455
9456        // Zero is the default and means a sealed backup is kept for ever.
9457        b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
9458        b.run(&[b"BACKUP", b"START"]);
9459        b.run(&[b"BACKUP", b"SEAL"]);
9460        b.f.server.set_clock_ms(9_000_000_000);
9461        b.f.server.backup_expire();
9462        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
9463    }
9464
9465    /// The three settings around the command, read and written the way 8.10.1
9466    /// reads and writes them.
9467    #[test]
9468    fn the_backup_settings_behave_the_way_the_reference_does() {
9469        let mut b = Backups::new("config");
9470        let dir = b.dir.to_string_lossy().into_owned();
9471
9472        assert_eq!(
9473            b.run(&[b"CONFIG", b"GET", b"dir"]),
9474            format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
9475        );
9476        assert_eq!(
9477            b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
9478            "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
9479        );
9480        assert_eq!(
9481            b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
9482            "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
9483        );
9484
9485        // `dir` is a protected config, so it is refused even for the value it
9486        // already holds, and `backupdirname` is immutable.
9487        assert_eq!(
9488            b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
9489            "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
9490        );
9491        assert_eq!(
9492            b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
9493            "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
9494        );
9495        assert!(
9496            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
9497                .contains("argument couldn't be parsed into an integer")
9498        );
9499        assert!(
9500            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
9501                .contains("argument must be between 0 and 9223372036854775807 inclusive")
9502        );
9503    }
9504
9505    /// The help text, which has `HELP` in it twice because the reference's does.
9506    #[test]
9507    fn backup_help_is_the_text_the_reference_sends() {
9508        let mut f = Fixture::new();
9509        let help = f.run(&[b"BACKUP", b"HELP"]);
9510        assert!(help.starts_with("*17\r\n"), "{help}");
9511        assert!(
9512            help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
9513        );
9514        assert!(help.contains("+    Start a new backup into the configured 'backupdirname'.\r\n"));
9515        assert!(help.contains("+    Freeze the current backup (BASE + INCR + manifest).\r\n"));
9516        assert!(help.contains("+    Return this help.\r\n+HELP\r\n+    Print this help.\r\n"));
9517    }
9518
9519    /// What a mistyped `BACKUP` gets told.
9520    ///
9521    /// The arity error names `backup` where the reference names `backup|start`,
9522    /// which is D-46: the table reports one arity for the container the way the
9523    /// reference does, and the per subcommand table that would carry the better
9524    /// name is not built yet. Every subcommand is exactly two words, so nothing
9525    /// legal is refused by it.
9526    #[test]
9527    fn backup_refuses_what_it_cannot_read() {
9528        let mut f = Fixture::new();
9529        assert_eq!(
9530            f.run(&[b"BACKUP"]),
9531            "-ERR wrong number of arguments for 'backup' command\r\n"
9532        );
9533        assert_eq!(
9534            f.run(&[b"BACKUP", b"START", b"x"]),
9535            "-ERR wrong number of arguments for 'backup' command\r\n"
9536        );
9537        assert_eq!(
9538            f.run(&[b"BACKUP", b"NOPE"]),
9539            "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
9540        );
9541    }
9542
9543    /// A fixture whose server saves into a directory of its own.
9544    ///
9545    /// The same shape and the same reason as [`Backups`]: these tests write real
9546    /// files, because a save that only moved a counter would pass a test suite
9547    /// and hand somebody an empty file.
9548    struct Saves {
9549        f: Fixture,
9550        dir: PathBuf,
9551    }
9552
9553    impl Saves {
9554        fn new(name: &str) -> Saves {
9555            let dir = std::env::temp_dir().join(format!("yo-save-{name}-{}", std::process::id()));
9556            let _ = std::fs::remove_dir_all(&dir);
9557            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
9558            let mut f = Fixture::new();
9559            f.server.set_dir(dir.clone());
9560            Saves { f, dir }
9561        }
9562
9563        fn run(&mut self, parts: &[&[u8]]) -> String {
9564            self.f.run(parts)
9565        }
9566
9567        /// The names in the directory, sorted.
9568        fn files(&self) -> Vec<String> {
9569            let mut names: Vec<String> = match std::fs::read_dir(&self.dir) {
9570                Ok(entries) => entries
9571                    .filter_map(|e| e.ok())
9572                    .map(|e| e.file_name().to_string_lossy().into_owned())
9573                    .collect(),
9574                Err(_) => Vec::new(),
9575            };
9576            names.sort();
9577            names
9578        }
9579
9580        fn image(&self) -> Vec<u8> {
9581            std::fs::read(self.dir.join("dump.rdb")).expect("could not read the file")
9582        }
9583
9584        /// One field out of `INFO persistence`.
9585        fn field(&mut self, name: &str) -> String {
9586            let text = self.run(&[b"INFO", b"persistence"]);
9587            let head = format!("\r\n{name}:");
9588            let at = text.find(&head).expect("the field is not in the section");
9589            let rest = &text[at + head.len()..];
9590            rest[..rest.find("\r\n").expect("the field has no end")].to_owned()
9591        }
9592    }
9593
9594    impl Drop for Saves {
9595        fn drop(&mut self) {
9596            let _ = std::fs::remove_dir_all(&self.dir);
9597        }
9598    }
9599
9600    #[test]
9601    fn save_writes_a_file_that_carries_the_dataset() {
9602        let mut s = Saves::new("writes");
9603        s.run(&[b"SET", b"k", b"v"]);
9604        s.run(&[b"RPUSH", b"l", b"a", b"b"]);
9605        assert!(
9606            s.files().is_empty(),
9607            "a server has saved without being asked"
9608        );
9609
9610        assert_eq!(s.run(&[b"SAVE"]), "+OK\r\n");
9611        assert_eq!(s.files(), ["dump.rdb"]);
9612
9613        // The header, the two databases the keys are in and the end marker,
9614        // which is as far as this test goes: what is between them is the
9615        // snapshot writer's own test, and a real server starting on one of
9616        // these files is what the harness checks.
9617        let image = s.image();
9618        assert!(
9619            image.starts_with(b"REDIS00"),
9620            "the header is not an RDB one"
9621        );
9622        assert!(
9623            image.windows(1).any(|w| w == [0xFF]),
9624            "there is no end marker"
9625        );
9626        assert!(image.len() > 40, "the file is too small to hold anything");
9627    }
9628
9629    #[test]
9630    fn a_save_leaves_no_temporary_file_behind() {
9631        let mut s = Saves::new("temp");
9632        s.run(&[b"SET", b"k", b"v"]);
9633        s.run(&[b"SAVE"]);
9634        s.run(&[b"BGSAVE"]);
9635        assert_eq!(s.files(), ["dump.rdb"]);
9636    }
9637
9638    #[test]
9639    fn a_save_that_cannot_write_says_so_in_one_word() {
9640        let mut s = Saves::new("nowhere");
9641        // A directory that is not there, which is the failure a real server
9642        // answers `-ERR` to with nothing after it.
9643        s.f.server.set_dir(s.dir.join("gone"));
9644        assert_eq!(s.run(&[b"SAVE"]), "-ERR\r\n");
9645        assert_eq!(s.field("rdb_last_bgsave_status"), "err");
9646        // And the count of attempts moved, because the attempt happened.
9647        assert_eq!(s.field("rdb_saves"), "1");
9648    }
9649
9650    #[test]
9651    fn lastsave_starts_at_the_time_the_server_did_and_moves_on_a_save() {
9652        let mut s = Saves::new("lastsave");
9653        let started = s.run(&[b"LASTSAVE"]);
9654        assert_eq!(started, format!(":{}\r\n", s.f.server.started_ms / 1_000));
9655
9656        s.f.server.set_clock_ms(s.f.server.started_ms + 5_000);
9657        s.run(&[b"SAVE"]);
9658        let after = s.run(&[b"LASTSAVE"]);
9659        assert_eq!(after, format!(":{}\r\n", s.f.server.started_ms / 1_000 + 5));
9660
9661        // A write does not move it. Only a save does.
9662        s.run(&[b"SET", b"k", b"v"]);
9663        assert_eq!(s.run(&[b"LASTSAVE"]), after);
9664    }
9665
9666    #[test]
9667    fn bgsave_takes_the_one_word_it_takes_and_nothing_else() {
9668        let mut s = Saves::new("bgsave");
9669        for parts in [
9670            &[b"BGSAVE".as_slice()][..],
9671            &[b"BGSAVE", b"SCHEDULE"],
9672            &[b"BGSAVE", b"schedule"],
9673        ] {
9674            assert_eq!(s.run(parts), "+Background saving started\r\n");
9675        }
9676        for parts in [
9677            &[b"BGSAVE".as_slice(), b"x"][..],
9678            &[b"BGSAVE", b"SCHEDULE", b"x"],
9679            &[b"BGSAVE", b"SCHEDULE", b"SCHEDULE"],
9680        ] {
9681            assert_eq!(s.run(parts), "-ERR syntax error\r\n");
9682        }
9683    }
9684
9685    #[test]
9686    fn a_save_inside_a_transaction_says_it_was_scheduled() {
9687        let mut s = Saves::new("queued");
9688        // `SAVE` never gets there, because it carries `no_multi`.
9689        assert_eq!(s.run(&[b"MULTI"]), "+OK\r\n");
9690        assert_eq!(
9691            s.run(&[b"SAVE"]),
9692            "-ERR Command not allowed inside a transaction\r\n"
9693        );
9694        assert_eq!(
9695            s.run(&[b"EXEC"]),
9696            "-EXECABORT Transaction discarded because of previous errors.\r\n"
9697        );
9698
9699        assert_eq!(s.run(&[b"MULTI"]), "+OK\r\n");
9700        assert_eq!(s.run(&[b"BGSAVE"]), "+QUEUED\r\n");
9701        assert_eq!(s.run(&[b"BGREWRITEAOF"]), "+QUEUED\r\n");
9702        assert_eq!(
9703            s.run(&[b"EXEC"]),
9704            "*2\r\n+Background saving scheduled\r\n\
9705             +Background append only file rewriting scheduled\r\n"
9706        );
9707        // And the file is there, which is the half of it that is not the words.
9708        assert_eq!(s.files(), ["dump.rdb"]);
9709    }
9710
9711    #[test]
9712    fn a_rewrite_counts_itself_and_writes_nothing() {
9713        let mut s = Saves::new("rewrite");
9714        assert_eq!(
9715            s.run(&[b"BGREWRITEAOF"]),
9716            "+Background append only file rewriting started\r\n"
9717        );
9718        assert_eq!(s.field("aof_rewrites"), "1");
9719        assert_eq!(s.field("aof_enabled"), "0");
9720        assert!(s.files().is_empty(), "a rewrite has written a file");
9721    }
9722
9723    #[test]
9724    fn role_says_master_with_nothing_following_it() {
9725        let mut f = Fixture::new();
9726        assert_eq!(f.run(&[b"ROLE"]), "*3\r\n$6\r\nmaster\r\n:0\r\n*0\r\n");
9727        assert_eq!(
9728            f.run(&[b"ROLE", b"x"]),
9729            "-ERR wrong number of arguments for 'role' command\r\n"
9730        );
9731    }
9732
9733    #[test]
9734    fn the_persistence_section_counts_the_saves_that_were_asked_for() {
9735        let mut s = Saves::new("counts");
9736        assert_eq!(s.field("rdb_saves"), "0");
9737        assert_eq!(s.field("rdb_last_bgsave_status"), "ok");
9738        s.run(&[b"SAVE"]);
9739        s.run(&[b"BGSAVE"]);
9740        s.run(&[b"BGSAVE", b"SCHEDULE"]);
9741        assert_eq!(s.field("rdb_saves"), "3");
9742        assert_eq!(s.field("rdb_bgsave_in_progress"), "0");
9743        assert_eq!(s.field("loading"), "0");
9744    }
9745
9746    #[test]
9747    fn the_persistence_section_is_in_a_bare_info_and_not_in_another_one() {
9748        let mut f = Fixture::new();
9749        assert!(f.run(&[b"INFO"]).contains("# Persistence"));
9750        assert!(f.run(&[b"INFO", b"persistence"]).contains("# Persistence"));
9751        assert!(f.run(&[b"INFO", b"all"]).contains("# Persistence"));
9752        assert!(!f.run(&[b"INFO", b"clients"]).contains("# Persistence"));
9753    }
9754
9755    #[test]
9756    fn the_file_name_reads_back_and_cannot_be_written() {
9757        let mut f = Fixture::new();
9758        assert_eq!(
9759            f.run(&[b"CONFIG", b"GET", b"dbfilename"]),
9760            "*2\r\n$10\r\ndbfilename\r\n$8\r\ndump.rdb\r\n"
9761        );
9762        assert_eq!(
9763            f.run(&[b"CONFIG", b"SET", b"dbfilename", b"other.rdb"]),
9764            "-ERR CONFIG SET failed (possibly related to argument 'dbfilename') - can't set protected config\r\n"
9765        );
9766        // Refused even when it is set to what it already is, which is what
9767        // being protected means and is not what being immutable means.
9768        assert_eq!(
9769            f.run(&[b"CONFIG", b"SET", b"dbfilename", b"dump.rdb"]),
9770            "-ERR CONFIG SET failed (possibly related to argument 'dbfilename') - can't set protected config\r\n"
9771        );
9772    }
9773
9774    #[test]
9775    fn shutdown_save_writes_the_file_and_shutdown_on_its_own_does_not() {
9776        let mut s = Saves::new("shutdown");
9777        s.run(&[b"SET", b"k", b"v"]);
9778        s.run(&[b"SHUTDOWN", b"NOSAVE"]);
9779        assert!(s.files().is_empty(), "a nosave shutdown wrote a file");
9780
9781        let mut s = Saves::new("shutdown-save");
9782        s.run(&[b"SET", b"k", b"v"]);
9783        s.run(&[b"SHUTDOWN", b"SAVE"]);
9784        assert_eq!(s.files(), ["dump.rdb"]);
9785    }
9786
9787    /// Every type survives the trip out to the file and back.
9788    ///
9789    /// This is the test the Redis suite is really running when it calls `DEBUG
9790    /// RELOAD` after a case: not that the command answers, but that what was in
9791    /// memory before it is what is in memory after it.
9792    #[test]
9793    fn debug_reload_brings_every_type_back_the_way_it_went_in() {
9794        let mut s = Saves::new("reload-types");
9795        s.run(&[b"SET", b"str", b"hello"]);
9796        s.run(&[b"SET", b"num", b"1234"]);
9797        s.run(&[b"RPUSH", b"list", b"a", b"b", b"c"]);
9798        s.run(&[b"SADD", b"set", b"x", b"y"]);
9799        s.run(&[b"SADD", b"ints", b"1", b"2", b"3"]);
9800        s.run(&[b"HSET", b"hash", b"f", b"v", b"g", b"w"]);
9801        s.run(&[b"ZADD", b"zset", b"1.5", b"m", b"2", b"n"]);
9802        s.run(&[b"XADD", b"stream", b"1-1", b"f", b"v"]);
9803        s.run(&[b"PEXPIREAT", b"str", b"4102444800000"]);
9804        let before = s.run(&[b"DBSIZE"]);
9805
9806        assert_eq!(s.run(&[b"DEBUG", b"RELOAD"]), "+OK\r\n");
9807
9808        assert_eq!(s.run(&[b"DBSIZE"]), before);
9809        assert_eq!(s.run(&[b"GET", b"str"]), "$5\r\nhello\r\n");
9810        assert_eq!(s.run(&[b"GET", b"num"]), "$4\r\n1234\r\n");
9811        assert_eq!(
9812            s.run(&[b"LRANGE", b"list", b"0", b"-1"]),
9813            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
9814        );
9815        assert_eq!(s.run(&[b"SCARD", b"set"]), ":2\r\n");
9816        assert_eq!(s.run(&[b"SISMEMBER", b"set", b"y"]), ":1\r\n");
9817        assert_eq!(s.run(&[b"SCARD", b"ints"]), ":3\r\n");
9818        assert_eq!(s.run(&[b"HGET", b"hash", b"g"]), "$1\r\nw\r\n");
9819        assert_eq!(s.run(&[b"ZSCORE", b"zset", b"m"]), "$3\r\n1.5\r\n");
9820        assert_eq!(s.run(&[b"XLEN", b"stream"]), ":1\r\n");
9821        // The deadline travels with the key, and it is the same deadline and not
9822        // one worked out again from a remaining time.
9823        assert_eq!(s.run(&[b"PEXPIRETIME", b"str"]), ":4102444800000\r\n");
9824        assert_eq!(s.run(&[b"PEXPIRETIME", b"num"]), ":-1\r\n");
9825    }
9826
9827    /// A key goes back into the database it came out of.
9828    #[test]
9829    fn debug_reload_puts_every_key_back_in_its_own_database() {
9830        let mut s = Saves::new("reload-dbs");
9831        s.run(&[b"SET", b"home", b"zero"]);
9832        s.run(&[b"SELECT", b"9"]);
9833        s.run(&[b"SET", b"away", b"nine"]);
9834        s.run(&[b"SELECT", b"0"]);
9835
9836        assert_eq!(s.run(&[b"DEBUG", b"RELOAD"]), "+OK\r\n");
9837
9838        assert_eq!(s.run(&[b"GET", b"home"]), "$4\r\nzero\r\n");
9839        assert_eq!(s.run(&[b"EXISTS", b"away"]), ":0\r\n");
9840        s.run(&[b"SELECT", b"9"]);
9841        assert_eq!(s.run(&[b"GET", b"away"]), "$4\r\nnine\r\n");
9842        assert_eq!(s.run(&[b"EXISTS", b"home"]), ":0\r\n");
9843    }
9844
9845    /// `NOSAVE` reads the file that is there rather than writing a new one.
9846    #[test]
9847    fn debug_reload_nosave_reads_the_file_that_is_already_there() {
9848        let mut s = Saves::new("reload-nosave");
9849        s.run(&[b"SET", b"k", b"first"]);
9850        s.run(&[b"SAVE"]);
9851        s.run(&[b"SET", b"k", b"second"]);
9852        s.run(&[b"SET", b"later", b"x"]);
9853
9854        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE"]), "+OK\r\n");
9855
9856        // Both changes are gone, because the file knows nothing about either.
9857        assert_eq!(s.run(&[b"GET", b"k"]), "$5\r\nfirst\r\n");
9858        assert_eq!(s.run(&[b"EXISTS", b"later"]), ":0\r\n");
9859    }
9860
9861    /// `NOFLUSH` lets the file land on what is already in memory.
9862    #[test]
9863    fn debug_reload_noflush_keeps_what_the_file_does_not_mention() {
9864        let mut s = Saves::new("reload-noflush");
9865        s.run(&[b"SET", b"k", b"first"]);
9866        s.run(&[b"SAVE"]);
9867        s.run(&[b"SET", b"k", b"second"]);
9868        s.run(&[b"SET", b"later", b"x"]);
9869
9870        assert_eq!(
9871            s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE", b"NOFLUSH"]),
9872            "+OK\r\n"
9873        );
9874
9875        // The file wins where the two disagree and memory keeps the rest, which
9876        // is what `MERGE` buys on a real server and is what happens here whether
9877        // the word was sent or not.
9878        assert_eq!(s.run(&[b"GET", b"k"]), "$5\r\nfirst\r\n");
9879        assert_eq!(s.run(&[b"GET", b"later"]), "$1\r\nx\r\n");
9880    }
9881
9882    /// The three words it takes, in any case, and one sentence for anything else.
9883    #[test]
9884    fn debug_reload_takes_its_three_words_and_no_others() {
9885        let mut s = Saves::new("reload-words");
9886        s.run(&[b"SET", b"k", b"v"]);
9887        for parts in [
9888            &[b"DEBUG".as_slice(), b"RELOAD"][..],
9889            &[b"DEBUG", b"RELOAD", b"NOSAVE"],
9890            &[b"DEBUG", b"RELOAD", b"nosave"],
9891            &[b"DEBUG", b"RELOAD", b"MERGE"],
9892            &[b"DEBUG", b"RELOAD", b"NOFLUSH"],
9893            &[b"DEBUG", b"RELOAD", b"MERGE", b"NOFLUSH", b"NOSAVE"],
9894            // Repeated is not an error on a real server either.
9895            &[b"DEBUG", b"RELOAD", b"NOSAVE", b"NOSAVE"],
9896        ] {
9897            assert_eq!(s.run(parts), "+OK\r\n", "{parts:?}");
9898        }
9899        for parts in [
9900            &[b"DEBUG".as_slice(), b"RELOAD", b"BOGUS"][..],
9901            &[b"DEBUG", b"RELOAD", b"NOSAVE", b"BOGUS"],
9902            &[b"DEBUG", b"RELOAD", b""],
9903        ] {
9904            assert_eq!(
9905                s.run(parts),
9906                "-ERR DEBUG RELOAD only supports the MERGE, NOFLUSH and NOSAVE options.\r\n",
9907                "{parts:?}"
9908            );
9909        }
9910        // And the dataset is still there after all of that.
9911        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
9912    }
9913
9914    /// A reload that cannot write its file says what a save says.
9915    #[test]
9916    fn debug_reload_that_cannot_write_the_file_says_so_in_one_word() {
9917        let mut s = Saves::new("reload-nowhere");
9918        s.run(&[b"SET", b"k", b"v"]);
9919        s.f.server.set_dir(s.dir.join("gone"));
9920        assert_eq!(s.run(&[b"DEBUG", b"RELOAD"]), "-ERR\r\n");
9921        // Nothing was thrown away, because nothing was read.
9922        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
9923    }
9924
9925    /// A reload that cannot read its file says to look in the log.
9926    ///
9927    /// Two ways to get there, a file that is not there and a file that is not
9928    /// one, and the reply is the same sentence for both because a client can do
9929    /// nothing with the difference.
9930    #[test]
9931    fn debug_reload_that_cannot_read_the_file_says_to_check_the_log() {
9932        let mut s = Saves::new("reload-unreadable");
9933        s.run(&[b"SET", b"k", b"v"]);
9934        let failed = "-ERR Error trying to load the RDB dump, check server logs.\r\n";
9935        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE"]), failed);
9936        // Refused before the flush, so the dataset is still here.
9937        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
9938
9939        s.run(&[b"SAVE"]);
9940        std::fs::write(s.dir.join("dump.rdb"), b"not an RDB file at all")
9941            .expect("could not write over the file");
9942        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE"]), failed);
9943        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
9944    }
9945
9946    /// A reload says what it would lose rather than losing it.
9947    ///
9948    /// A time series has no RDB type byte, so it is not in the file the save
9949    /// wrote, and flushing would make the round trip a delete. `NOFLUSH` is the
9950    /// way through: everything in the file lands on top of what is there and the
9951    /// key that could not be written stays where it is.
9952    #[test]
9953    fn debug_reload_refuses_to_drop_a_key_with_no_rdb_form() {
9954        let mut s = Saves::new("reload-foreign");
9955        s.run(&[b"SET", b"k", b"v"]);
9956        s.run(&[b"TS.CREATE", b"ts"]);
9957        s.run(&[b"TS.ADD", b"ts", b"1000", b"1.5"]);
9958
9959        assert_eq!(
9960            s.run(&[b"DEBUG", b"RELOAD"]),
9961            "-ERR DEBUG RELOAD would drop 1 key with no RDB form, use NOFLUSH to keep it\r\n"
9962        );
9963        assert_eq!(s.run(&[b"EXISTS", b"ts"]), ":1\r\n");
9964
9965        s.run(&[b"TS.CREATE", b"ts2"]);
9966        assert_eq!(
9967            s.run(&[b"DEBUG", b"RELOAD"]),
9968            "-ERR DEBUG RELOAD would drop 2 keys with no RDB form, use NOFLUSH to keep them\r\n"
9969        );
9970
9971        // And the way through keeps everything.
9972        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOFLUSH"]), "+OK\r\n");
9973        assert_eq!(s.run(&[b"EXISTS", b"ts"]), ":1\r\n");
9974        assert_eq!(s.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
9975        assert_eq!(s.run(&[b"TS.GET", b"ts"]), "*2\r\n:1000\r\n+1.5\r\n");
9976    }
9977
9978    /// A key that died while the file was on disk does not come back.
9979    #[test]
9980    fn debug_reload_drops_a_key_whose_deadline_went_by() {
9981        let mut s = Saves::new("reload-expired");
9982        s.run(&[b"SET", b"gone", b"v"]);
9983        s.run(&[b"SET", b"stays", b"v"]);
9984        s.run(&[b"PEXPIREAT", b"gone", b"4102444800000"]);
9985        s.run(&[b"SAVE"]);
9986        s.f.server.set_clock_ms(4_102_444_800_001);
9987
9988        assert_eq!(s.run(&[b"DEBUG", b"RELOAD", b"NOSAVE"]), "+OK\r\n");
9989
9990        assert_eq!(s.run(&[b"EXISTS", b"gone"]), ":0\r\n");
9991        assert_eq!(s.run(&[b"GET", b"stays"]), "$1\r\nv\r\n");
9992    }
9993
9994    /// The other caller of the same walk, which is `yodb serve --restore` and
9995    /// `yodb restore`: a file one server wrote, read into a server that has never
9996    /// seen it.
9997    ///
9998    /// The interesting half is that the second server is a different one. A
9999    /// reload reads a file its own writer produced a moment ago into a keyspace
10000    /// whose thresholds have not moved, and a restore does not, so this is the
10001    /// shape the migration story actually has.
10002    #[test]
10003    fn a_file_one_server_wrote_loads_into_a_server_that_has_never_seen_it() {
10004        let mut wrote = Saves::new("restore-across");
10005        wrote.run(&[b"SET", b"s", b"hello"]);
10006        wrote.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
10007        wrote.run(&[b"HSET", b"h", b"f", b"v"]);
10008        wrote.run(&[b"ZADD", b"z", b"1.5", b"m"]);
10009        wrote.run(&[b"SELECT", b"7"]);
10010        wrote.run(&[b"SADD", b"far", b"x"]);
10011        assert_eq!(wrote.run(&[b"SAVE"]), "+OK\r\n");
10012
10013        let mut fresh = Fixture::new();
10014        let done = fresh
10015            .server
10016            .load_image(&wrote.image(), true)
10017            .expect("the file one server wrote is a file another can read");
10018        assert_eq!(done.keys[0], 4);
10019        assert_eq!(done.keys[7], 1);
10020        assert_eq!(done.total(), 5);
10021        assert_eq!(done.expired, 0);
10022
10023        assert_eq!(fresh.run(&[b"GET", b"s"]), "$5\r\nhello\r\n");
10024        assert_eq!(
10025            fresh.run(&[b"LRANGE", b"l", b"0", b"-1"]),
10026            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
10027        );
10028        assert_eq!(fresh.run(&[b"HGET", b"h", b"f"]), "$1\r\nv\r\n");
10029        assert_eq!(fresh.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n1.5\r\n");
10030        fresh.run(&[b"SELECT", b"7"]);
10031        assert_eq!(fresh.run(&[b"SMEMBERS", b"far"]), "*1\r\n$1\r\nx\r\n");
10032    }
10033
10034    /// A load says how much of the file landed and how much of it was too old to
10035    /// keep, and `INFO persistence` says the same two numbers afterwards.
10036    #[test]
10037    fn a_load_reports_what_it_kept_and_what_had_already_died() {
10038        let mut wrote = Saves::new("restore-counts");
10039        wrote.run(&[b"SET", b"gone", b"v"]);
10040        wrote.run(&[b"SET", b"stays", b"v"]);
10041        wrote.run(&[b"PEXPIREAT", b"gone", b"4102444800000"]);
10042        assert_eq!(wrote.run(&[b"SAVE"]), "+OK\r\n");
10043
10044        let mut fresh = Saves::new("restore-counts-into");
10045        fresh.f.server.set_clock_ms(4_102_444_800_001);
10046        let done = fresh
10047            .f
10048            .server
10049            .load_image(&wrote.image(), true)
10050            .expect("a file with a dead key in it is still a good file");
10051        assert_eq!(done.total(), 1);
10052        assert_eq!(done.expired, 1);
10053
10054        assert_eq!(fresh.field("rdb_last_load_keys_loaded"), "1");
10055        assert_eq!(fresh.field("rdb_last_load_keys_expired"), "1");
10056        assert_eq!(fresh.run(&[b"EXISTS", b"gone"]), ":0\r\n");
10057        assert_eq!(fresh.run(&[b"GET", b"stays"]), "$1\r\nv\r\n");
10058    }
10059
10060    /// A server that has not loaded anything reports nought for both, which is
10061    /// true rather than a placeholder.
10062    #[test]
10063    fn a_server_that_has_loaded_nothing_says_so() {
10064        let mut s = Saves::new("restore-never");
10065        assert_eq!(s.field("rdb_last_load_keys_loaded"), "0");
10066        assert_eq!(s.field("rdb_last_load_keys_expired"), "0");
10067    }
10068
10069    /// Bytes that are not an RDB at all leave the keyspace exactly as it was.
10070    ///
10071    /// The magic is checked before anything is thrown away, which is the whole
10072    /// reason a restore is safe to point at the wrong file.
10073    #[test]
10074    fn a_file_that_is_not_an_rdb_is_refused_with_the_dataset_still_there() {
10075        let mut f = Fixture::new();
10076        f.run(&[b"SET", b"k", b"v"]);
10077        let refused = f
10078            .server
10079            .load_image(b"this is not a Redis dump at all, not even close", true)
10080            .expect_err("that is not an RDB");
10081        assert_eq!(refused.to_string(), "the file does not start with REDIS");
10082        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
10083    }
10084
10085    /// A file whose last eight bytes do not add up is refused too, and for the
10086    /// same reason it is safe: the checksum is over the whole file and is read
10087    /// before the first key comes out.
10088    #[test]
10089    fn a_damaged_file_is_refused_with_the_dataset_still_there() {
10090        let mut wrote = Saves::new("restore-damaged");
10091        wrote.run(&[b"SET", b"a", b"b"]);
10092        assert_eq!(wrote.run(&[b"SAVE"]), "+OK\r\n");
10093        let mut image = wrote.image();
10094        // One byte in the middle, so that the frame still parses and only the
10095        // checksum knows. Flipping the footer would be a different test.
10096        let middle = image.len() / 2;
10097        image[middle] ^= 0xff;
10098
10099        let mut f = Fixture::new();
10100        f.run(&[b"SET", b"k", b"v"]);
10101        let refused = f
10102            .server
10103            .load_image(&image, true)
10104            .expect_err("the checksum does not match");
10105        assert_eq!(
10106            refused.to_string(),
10107            "the checksum does not match, so the file is damaged"
10108        );
10109        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
10110    }
10111
10112    /// The fields of one `DEBUG OBJECT` line, read off a string.
10113    ///
10114    /// Every number in it is checked somewhere and this is the one that checks
10115    /// the shape: the field order, the spacing and the two that are constant.
10116    #[test]
10117    fn debug_object_describes_how_a_value_is_written_down() {
10118        let mut f = Fixture::new();
10119        f.run(&[b"SET", b"s", b"hello"]);
10120
10121        let line = f.run(&[b"DEBUG", b"OBJECT", b"s"]);
10122        let line = line
10123            .strip_prefix('+')
10124            .and_then(|l| l.strip_suffix("\r\n"))
10125            .expect("a simple string");
10126        let mut fields = line.split(' ');
10127        assert_eq!(fields.next(), Some("Value"));
10128        assert!(
10129            fields.next().expect("an address").starts_with("at:0x"),
10130            "{line}"
10131        );
10132        assert_eq!(fields.next(), Some("refcount:1"));
10133        assert_eq!(fields.next(), Some("encoding:embstr"));
10134        // Five bytes of hello and the one byte header a short string is
10135        // written with, which is the body and neither the type byte in front
10136        // of it nor the footer behind.
10137        assert_eq!(fields.next(), Some("serializedlength:6"));
10138        assert!(
10139            fields.next().expect("a clock").starts_with("lru:"),
10140            "{line}"
10141        );
10142        assert_eq!(fields.next(), Some("lru_seconds_idle:0"));
10143        assert_eq!(fields.next(), None);
10144    }
10145
10146    /// The five extra fields a list that broke into nodes carries.
10147    #[test]
10148    fn debug_object_counts_the_nodes_a_list_broke_into() {
10149        let mut f = Fixture::new();
10150        // Enough long members to be past the eight kilobyte band, so that the
10151        // list is a quicklist rather than one packed run.
10152        let member = vec![b'x'; 200];
10153        for _ in 0..100 {
10154            f.run(&[b"RPUSH", b"l", &member]);
10155        }
10156        assert_eq!(
10157            f.run(&[b"OBJECT", b"ENCODING", b"l"]),
10158            "$9\r\nquicklist\r\n"
10159        );
10160
10161        let line = f.run(&[b"DEBUG", b"OBJECT", b"l"]);
10162        let nodes: usize = field(&line, "ql_nodes:").parse().expect("a count");
10163        assert!(nodes > 1, "{line}");
10164        let avg: f64 = field(&line, "ql_avg_node:").parse().expect("an average");
10165        assert!((avg - 100.0 / nodes as f64).abs() < 0.01, "{line}");
10166        assert_eq!(field(&line, "ql_listpack_max:"), "-2");
10167        assert_eq!(field(&line, "ql_compressed:"), "0");
10168        let bytes: usize = field(&line, "ql_uncompressed_size:")
10169            .parse()
10170            .expect("a size");
10171        assert!(bytes > 100 * 200, "{line}");
10172
10173        // A list small enough to stay packed has none of them.
10174        f.run(&[b"RPUSH", b"small", b"a"]);
10175        let line = f.run(&[b"DEBUG", b"OBJECT", b"small"]);
10176        assert!(!line.contains("ql_nodes"), "{line}");
10177    }
10178
10179    /// Looking is not using, which is the property the whole subcommand rests
10180    /// on: a diagnostic that reset the number it reports would answer nought
10181    /// every time it was asked.
10182    #[test]
10183    fn debug_object_does_not_count_as_using_the_key() {
10184        let mut f = Fixture::new();
10185        f.run(&[b"SET", b"s", b"hello"]);
10186        let was = field(&f.run(&[b"DEBUG", b"OBJECT", b"s"]), "lru:").to_owned();
10187
10188        f.server.set_clock_ms(f.server.clock.now_ms() + 60_000);
10189        let line = f.run(&[b"DEBUG", b"OBJECT", b"s"]);
10190
10191        assert_eq!(field(&line, "lru_seconds_idle:"), "60");
10192        // The clock the idle time counts back from has not moved, because
10193        // nothing has touched the key.
10194        assert_eq!(field(&line, "lru:"), was);
10195    }
10196
10197    /// The two lengths `DEBUG SDSLEN` is read for, and the four numbers about
10198    /// an allocator that is not here, which is D-135.
10199    #[test]
10200    fn debug_sdslen_measures_the_name_and_the_string_under_it() {
10201        let mut f = Fixture::new();
10202        f.run(&[b"SET", b"name", b"hello"]);
10203
10204        assert_eq!(
10205            f.run(&[b"DEBUG", b"SDSLEN", b"name"]),
10206            "+key_sds_len:4, key_sds_avail:0, key_zmalloc: 4, \
10207             val_sds_len:5, val_sds_avail:0, val_zmalloc: 5\r\n"
10208        );
10209    }
10210
10211    /// What each of the four refuses, which is the half a suite branches on.
10212    #[test]
10213    fn the_inspecting_subcommands_refuse_what_they_cannot_describe() {
10214        let mut f = Fixture::new();
10215        f.run(&[b"SET", b"s", b"hello"]);
10216        f.run(&[b"SET", b"n", b"12345"]);
10217        f.run(&[b"RPUSH", b"l", b"a"]);
10218
10219        // A key that is not there is the same sentence from all four, and it is
10220        // an error rather than the nil `OBJECT ENCODING` answers.
10221        for sub in [
10222            b"OBJECT".as_slice(),
10223            b"SDSLEN".as_slice(),
10224            b"LISTPACK".as_slice(),
10225            b"QUICKLIST".as_slice(),
10226        ] {
10227            assert_eq!(
10228                f.run(&[b"DEBUG", sub, b"nosuch"]),
10229                "-ERR no such key\r\n",
10230                "{}",
10231                String::from_utf8_lossy(sub)
10232            );
10233        }
10234
10235        // An integer encoded string has no string in it to measure.
10236        assert_eq!(
10237            f.run(&[b"DEBUG", b"SDSLEN", b"n"]),
10238            "-ERR Not an sds encoded string.\r\n"
10239        );
10240        assert_eq!(
10241            f.run(&[b"DEBUG", b"SDSLEN", b"l"]),
10242            "-ERR Not an sds encoded string.\r\n"
10243        );
10244
10245        // Each structure dump takes the representation it is named after and
10246        // nothing else, whatever type the value is.
10247        assert_eq!(
10248            f.run(&[b"DEBUG", b"LISTPACK", b"l"]),
10249            "+Listpack structure printed on stdout\r\n"
10250        );
10251        assert_eq!(
10252            f.run(&[b"DEBUG", b"QUICKLIST", b"l"]),
10253            "-ERR Not a quicklist encoded object.\r\n"
10254        );
10255        assert_eq!(
10256            f.run(&[b"DEBUG", b"LISTPACK", b"s"]),
10257            "-ERR Not a listpack encoded object.\r\n"
10258        );
10259    }
10260
10261    /// A listpack is a representation and not a type, so the same subcommand
10262    /// answers for four different types and refuses the intset next to them.
10263    #[test]
10264    fn debug_listpack_answers_for_anything_written_as_one() {
10265        let mut f = Fixture::new();
10266        f.run(&[b"RPUSH", b"l", b"a"]);
10267        f.run(&[b"HSET", b"h", b"f", b"v"]);
10268        f.run(&[b"SADD", b"st", b"a"]);
10269        f.run(&[b"ZADD", b"z", b"1", b"m"]);
10270        f.run(&[b"SADD", b"ints", b"1", b"2"]);
10271
10272        for key in [b"l".as_slice(), b"h", b"st", b"z"] {
10273            assert_eq!(
10274                f.run(&[b"DEBUG", b"LISTPACK", key]),
10275                "+Listpack structure printed on stdout\r\n",
10276                "{}",
10277                String::from_utf8_lossy(key)
10278            );
10279        }
10280        assert_eq!(
10281            f.run(&[b"OBJECT", b"ENCODING", b"ints"]),
10282            "$6\r\nintset\r\n"
10283        );
10284        assert_eq!(
10285            f.run(&[b"DEBUG", b"LISTPACK", b"ints"]),
10286            "-ERR Not a listpack encoded object.\r\n"
10287        );
10288    }
10289
10290    /// The level argument on `QUICKLIST`, which is read and dropped, and the
10291    /// wrong argument count on either, which is the container's own sentence.
10292    #[test]
10293    fn debug_quicklist_takes_a_level_it_does_nothing_with() {
10294        let mut f = Fixture::new();
10295        let member = vec![b'x'; 200];
10296        for _ in 0..100 {
10297            f.run(&[b"RPUSH", b"l", &member]);
10298        }
10299
10300        let said = "+Quicklist structure printed on stdout\r\n";
10301        assert_eq!(f.run(&[b"DEBUG", b"QUICKLIST", b"l"]), said);
10302        assert_eq!(f.run(&[b"DEBUG", b"QUICKLIST", b"l", b"1"]), said);
10303        // A word that is not a number is taken rather than refused, which is
10304        // the reference: it reads the argument with atoi and gets nought.
10305        assert_eq!(f.run(&[b"DEBUG", b"QUICKLIST", b"l", b"abc"]), said);
10306
10307        assert_eq!(
10308            f.run(&[b"DEBUG", b"QUICKLIST", b"l", b"1", b"2"]),
10309            "-ERR unknown subcommand or wrong number of arguments for \
10310             'QUICKLIST'. Try DEBUG HELP.\r\n"
10311        );
10312        assert_eq!(
10313            f.run(&[b"DEBUG", b"LISTPACK", b"l", b"0"]),
10314            "-ERR unknown subcommand or wrong number of arguments for \
10315             'LISTPACK'. Try DEBUG HELP.\r\n"
10316        );
10317    }
10318
10319    /// Every one of these is a number read off redis-server 8.10.1 rather than
10320    /// one this build produced, which is the only kind of assertion worth
10321    /// making about a digest: a number computed a different way is not a worse
10322    /// digest, it is a useless one.
10323    #[test]
10324    fn a_value_digest_is_the_number_the_reference_computes() {
10325        let mut f = Fixture::new();
10326        f.run(&[b"SET", b"s", b"hello"]);
10327        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
10328        f.run(&[b"SADD", b"t", b"a", b"b", b"c"]);
10329        f.run(&[b"HSET", b"h", b"f", b"v"]);
10330        f.run(&[b"ZADD", b"z", b"1", b"a", b"2.5", b"b"]);
10331        f.run(&[b"XADD", b"x", b"1-1", b"f", b"v"]);
10332
10333        for (key, want) in [
10334            (&b"s"[..], "36b23a1456b2dce2c3ed252c456761301dba8060"),
10335            (b"l", "8bf72d812571eea9b927f3c11beb0c4165a6ff89"),
10336            (b"t", "593c2414786d75446e97f4ea5d4b731f3313da72"),
10337            (b"h", "90c76e9e9f4c62d642a34fc97c7dad503b51f906"),
10338            (b"z", "c45c5b051acd64070e5ed1a949939d5145f806c5"),
10339            (b"x", "2ed9a7a81688084b1f7eae33456ef7727d357031"),
10340        ] {
10341            assert_eq!(
10342                f.run(&[b"DEBUG", b"DIGEST-VALUE", key]),
10343                format!("*1\r\n+{want}\r\n"),
10344                "{}",
10345                String::from_utf8_lossy(key)
10346            );
10347        }
10348    }
10349
10350    /// The deadline is in the digest and the time left is not, which is what
10351    /// lets two servers that agree about a dataset agree about the number.
10352    #[test]
10353    fn a_deadline_shows_up_without_the_time_left_showing_up() {
10354        let mut f = Fixture::new();
10355        f.run(&[b"SET", b"e", b"value"]);
10356        let bare = "*1\r\n+d59ec93db87f4cd915db3cdf44bb63755bc4a635\r\n";
10357        let dated = "*1\r\n+331b37c26446a68dd4cdd72701d1acee416ae7b6\r\n";
10358        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"e"]), bare);
10359
10360        f.run(&[b"EXPIRE", b"e", b"1000"]);
10361        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"e"]), dated);
10362        // A different deadline on the same value is the same digest.
10363        f.run(&[b"EXPIRE", b"e", b"999999"]);
10364        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"e"]), dated);
10365        f.run(&[b"PERSIST", b"e"]);
10366        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"e"]), bare);
10367
10368        // The same again for a field of a hash, which says so with its own
10369        // word rather than with the key's.
10370        f.run(&[b"HSET", b"he", b"f1", b"v1", b"f2", b"v2"]);
10371        f.run(&[b"HEXPIRE", b"he", b"1000", b"FIELDS", b"1", b"f2"]);
10372        assert_eq!(
10373            f.run(&[b"DEBUG", b"DIGEST-VALUE", b"he"]),
10374            "*1\r\n+8911d6d4d198f5e022f80dfefd5e15d6c0eaabe3\r\n"
10375        );
10376    }
10377
10378    /// The value and not the entry, which is the difference between the two
10379    /// subcommands and is why a copy answers the same forty characters.
10380    #[test]
10381    fn a_value_digest_does_not_know_what_the_key_is_called() {
10382        let mut f = Fixture::new();
10383        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
10384        f.run(&[b"COPY", b"l", b"l2"]);
10385        let want = "*1\r\n+8bf72d812571eea9b927f3c11beb0c4165a6ff89\r\n";
10386        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"l"]), want);
10387        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE", b"l2"]), want);
10388
10389        // Several at once, in the order asked for, with a key that is not
10390        // there answering forty zeros rather than an error.
10391        assert_eq!(
10392            f.run(&[b"DEBUG", b"DIGEST-VALUE", b"l", b"gone", b"l2"]),
10393            format!(
10394                "*3\r\n+8bf72d812571eea9b927f3c11beb0c4165a6ff89\r\n+{0}\r\n\
10395                 +8bf72d812571eea9b927f3c11beb0c4165a6ff89\r\n",
10396                "0".repeat(40)
10397            )
10398        );
10399        assert_eq!(f.run(&[b"DEBUG", b"DIGEST-VALUE"]), "*0\r\n");
10400    }
10401
10402    /// The whole server, where the name is in it and the database number is
10403    /// in it and an empty database is not.
10404    #[test]
10405    fn the_whole_digest_folds_in_the_names_and_the_database_numbers() {
10406        let mut f = Fixture::new();
10407        let empty = format!("+{}\r\n", "0".repeat(40));
10408        assert_eq!(f.run(&[b"DEBUG", b"DIGEST"]), empty);
10409
10410        f.run(&[b"SET", b"k", b"hello"]);
10411        assert_eq!(
10412            f.run(&[b"DEBUG", b"DIGEST"]),
10413            "+d101db227d1e3b31616b18b0b8700f84c3ffa5e9\r\n"
10414        );
10415
10416        f.run(&[b"SELECT", b"3"]);
10417        f.run(&[b"SET", b"k", b"hello"]);
10418        assert_eq!(
10419            f.run(&[b"DEBUG", b"DIGEST"]),
10420            "+a541f66c15932c1014da1569ff27c15bcde7d1dc\r\n"
10421        );
10422
10423        // Emptying the first one leaves the same key in the same place and a
10424        // different number, because the database it is in is folded in.
10425        f.run(&[b"SELECT", b"0"]);
10426        f.run(&[b"FLUSHDB"]);
10427        assert_eq!(
10428            f.run(&[b"DEBUG", b"DIGEST"]),
10429            "+f9b35ab00ad2f456386a2f73d316bf8266013606\r\n"
10430        );
10431
10432        f.run(&[b"FLUSHALL"]);
10433        assert_eq!(f.run(&[b"DEBUG", b"DIGEST"]), empty);
10434    }
10435
10436    /// Digesting is not using, which is what makes it safe for a suite to call
10437    /// between every step of whatever it is measuring.
10438    #[test]
10439    fn digesting_does_not_count_as_using_anything() {
10440        let mut f = Fixture::new();
10441        f.run(&[b"SET", b"k", b"value"]);
10442        f.run(&[b"CONFIG", b"RESETSTAT"]);
10443        f.run(&[b"DEBUG", b"DIGEST"]);
10444        f.run(&[b"DEBUG", b"DIGEST-VALUE", b"k", b"gone"]);
10445        let stats = f.run(&[b"INFO", b"stats"]);
10446        assert!(stats.contains("keyspace_hits:0\r\n"), "{stats}");
10447        assert!(stats.contains("keyspace_misses:0\r\n"), "{stats}");
10448
10449        // And the counters are working, so the nought above is the command
10450        // holding still rather than the statistic never moving.
10451        f.run(&[b"GET", b"k"]);
10452        f.run(&[b"GET", b"gone"]);
10453        let stats = f.run(&[b"INFO", b"stats"]);
10454        assert!(stats.contains("keyspace_hits:1\r\n"), "{stats}");
10455        assert!(stats.contains("keyspace_misses:1\r\n"), "{stats}");
10456    }
10457
10458    /// One field out of a `DEBUG OBJECT` line, named by its label.
10459    fn field<'a>(line: &'a str, label: &str) -> &'a str {
10460        let at = line
10461            .find(label)
10462            .unwrap_or_else(|| panic!("no {label} in {line}"));
10463        let rest = &line[at + label.len()..];
10464        rest.split([' ', '\r']).next().expect("a value")
10465    }
10466
10467    /// A fixture on a server with a password, on a connection that has not met
10468    /// it.
10469    ///
10470    /// The two calls have to be in this order and both have to happen. Setting
10471    /// the password is the server's half and admitting nothing is the
10472    /// connection's, and the connection's half is what a real front does at
10473    /// accept time. A fixture that only set the password would be a connection
10474    /// that was open before it went on, which is the case in
10475    /// [`a_password_set_under_an_open_connection_leaves_it_alone`].
10476    fn guarded(password: &[u8]) -> Fixture {
10477        let mut f = Fixture::new();
10478        f.server.set_password(password);
10479        f.session.admit(false);
10480        f
10481    }
10482
10483    /// Nothing gets through without the password, and the sentence is the one
10484    /// a client branches on.
10485    #[test]
10486    fn a_server_with_a_password_answers_everything_else_with_noauth() {
10487        let mut f = guarded(b"hunter2");
10488        for parts in [
10489            &[b"PING".as_slice()][..],
10490            &[b"GET", b"k"],
10491            &[b"SET", b"k", b"v"],
10492            &[b"COMMAND", b"COUNT"],
10493            &[b"SUBSCRIBE", b"ch"],
10494            &[b"MULTI"],
10495            &[b"INFO"],
10496        ] {
10497            assert_eq!(
10498                f.run(parts),
10499                "-NOAUTH Authentication required.\r\n",
10500                "{parts:?} got through"
10501            );
10502        }
10503    }
10504
10505    /// The four commands a client may send before it has authenticated.
10506    ///
10507    /// `AUTH` because it is the way in, `HELLO` because it carries the option
10508    /// that is the other way in, `RESET` because starting over cannot need a
10509    /// password, and `QUIT` because leaving cannot either. Redis marks all four
10510    /// `no_auth` and the gate reads the flag rather than the names.
10511    #[test]
10512    fn the_four_commands_that_do_not_need_the_password_get_through() {
10513        for name in ["auth", "hello", "reset", "quit"] {
10514            let spec = table::lookup(name.as_bytes()).expect(name);
10515            assert!(spec.flags.contains(&"no_auth"), "{name} is not no_auth");
10516        }
10517        let mut f = guarded(b"hunter2");
10518        assert_eq!(f.run(&[b"QUIT"]), "+OK\r\n");
10519        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
10520        assert_eq!(
10521            f.run(&[b"AUTH", b"wrong"]),
10522            "-WRONGPASS invalid username-password pair or user is disabled.\r\n"
10523        );
10524        assert_eq!(f.run(&[b"AUTH", b"hunter2"]), "+OK\r\n");
10525        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
10526    }
10527
10528    /// Both spellings of `AUTH`, and the one user there is.
10529    #[test]
10530    fn auth_takes_the_password_on_its_own_or_behind_the_user_name() {
10531        let mut f = guarded(b"hunter2");
10532        let wrong = "-WRONGPASS invalid username-password pair or user is disabled.\r\n";
10533        assert_eq!(f.run(&[b"AUTH", b"default", b"hunter2"]), "+OK\r\n");
10534        assert_eq!(f.run(&[b"AUTH", b"default", b"wrong"]), wrong);
10535        // And a failed attempt does not throw out the connection that had
10536        // already got in, which was read off 8.10.1 rather than assumed.
10537        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
10538        assert_eq!(f.run(&[b"AUTH", b"someone", b"hunter2"]), wrong);
10539        assert_eq!(
10540            f.run(&[b"AUTH"]),
10541            "-ERR wrong number of arguments for 'auth' command\r\n"
10542        );
10543        assert_eq!(f.run(&[b"AUTH", b"a", b"b", b"c"]), "-ERR syntax error\r\n");
10544    }
10545
10546    /// On a server with no password the default user is `nopass`, and what that
10547    /// means is not what it sounds like.
10548    ///
10549    /// Any password at all is the right one for it, so the two argument form
10550    /// says `OK`. The one argument form is the exception and gets a sentence
10551    /// about the configuration instead, because a client that sends it has
10552    /// almost certainly reached a server it did not mean to reach.
10553    #[test]
10554    fn auth_on_a_server_with_no_password_says_so_at_length() {
10555        let mut f = Fixture::new();
10556        assert_eq!(
10557            f.run(&[b"AUTH", b"anything"]),
10558            "-ERR AUTH <password> called without any password configured for the \
10559             default user. Are you sure your configuration is correct?\r\n"
10560        );
10561        assert_eq!(f.run(&[b"AUTH", b"default", b"anything"]), "+OK\r\n");
10562        assert_eq!(
10563            f.run(&[b"AUTH", b"nobody", b"anything"]),
10564            "-WRONGPASS invalid username-password pair or user is disabled.\r\n"
10565        );
10566        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
10567    }
10568
10569    /// `HELLO` has a sentence of its own, and the order it decides things in is
10570    /// not the order they are written in.
10571    ///
10572    /// The protocol version first, so a bad one is a `NOPROTO` even from a
10573    /// connection that has not authenticated and would have been let in by the
10574    /// `AUTH` option on the same line. Then the option, so a wrong password is a
10575    /// `WRONGPASS`. Then the password at all, which is what a bare `HELLO` on a
10576    /// guarded server gets. All three read off 8.10.1.
10577    #[test]
10578    fn hello_says_which_option_would_have_worked() {
10579        let long = "-NOAUTH HELLO must be called with the client already authenticated, \
10580                    otherwise the HELLO <proto> AUTH <user> <pass> option can be used to \
10581                    authenticate the client and select the RESP protocol version at the \
10582                    same time\r\n";
10583        let mut f = guarded(b"hunter2");
10584        assert_eq!(f.run(&[b"HELLO"]), long);
10585        assert_eq!(f.run(&[b"HELLO", b"3"]), long);
10586        assert_eq!(
10587            f.run(&[b"HELLO", b"9"]),
10588            "-NOPROTO unsupported protocol version\r\n"
10589        );
10590        // The version is refused before the option is applied, so this leaves
10591        // the connection exactly as unauthenticated as it found it.
10592        assert_eq!(
10593            f.run(&[b"HELLO", b"9", b"AUTH", b"default", b"hunter2"]),
10594            "-NOPROTO unsupported protocol version\r\n"
10595        );
10596        assert_eq!(f.run(&[b"PING"]), "-NOAUTH Authentication required.\r\n");
10597        assert_eq!(
10598            f.run(&[b"HELLO", b"2", b"AUTH", b"default", b"wrong"]),
10599            "-WRONGPASS invalid username-password pair or user is disabled.\r\n"
10600        );
10601        assert!(
10602            f.run(&[b"HELLO", b"3", b"AUTH", b"default", b"hunter2"])
10603                .starts_with("%7\r\n"),
10604            "the option did not let it in"
10605        );
10606        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
10607    }
10608
10609    /// `EXEC` is answered the abort rather than the refusal, with the refusal
10610    /// spliced into it.
10611    ///
10612    /// A client that sent `EXEC` is waiting for the transaction to be over one
10613    /// way or another, so the reference turns every refusal the funnel makes of
10614    /// an `EXEC` into an abort carrying the reason. The code word is in the
10615    /// spliced reason as well as in front of the reply it would have been.
10616    #[test]
10617    fn exec_without_the_password_is_an_abort_and_says_why() {
10618        let mut f = guarded(b"hunter2");
10619        assert_eq!(f.run(&[b"MULTI"]), "-NOAUTH Authentication required.\r\n");
10620        assert_eq!(
10621            f.run(&[b"EXEC"]),
10622            "-EXECABORT Transaction discarded because of: NOAUTH Authentication \
10623             required.\r\n"
10624        );
10625    }
10626
10627    /// `RESET` puts the connection back to how it was accepted, password and
10628    /// all.
10629    #[test]
10630    fn reset_gives_the_password_back_to_the_server_to_ask_for_again() {
10631        let mut f = guarded(b"hunter2");
10632        assert_eq!(f.run(&[b"AUTH", b"hunter2"]), "+OK\r\n");
10633        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
10634        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
10635        assert_eq!(f.run(&[b"PING"]), "-NOAUTH Authentication required.\r\n");
10636
10637        // And on a server with no password it puts back the same nothing.
10638        let mut f = Fixture::new();
10639        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
10640        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
10641    }
10642
10643    /// A password set under a connection that is already open leaves it alone.
10644    ///
10645    /// This is the rule nobody would guess and it is the reference's: the flag
10646    /// is decided when the connection is accepted, so `CONFIG SET requirepass`
10647    /// locks out everybody who connects after it and nobody who is already
10648    /// there, including the connection that sent it.
10649    #[test]
10650    fn a_password_set_under_an_open_connection_leaves_it_alone() {
10651        let mut f = Fixture::new();
10652        f.session.admit(true);
10653        assert_eq!(
10654            f.run(&[b"CONFIG", b"SET", b"requirepass", b"hunter2"]),
10655            "+OK\r\n"
10656        );
10657        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
10658        // And taking it off again lets in a connection that never met it.
10659        let mut later = Fixture::on(Server::new());
10660        later.server.set_password(b"hunter2");
10661        later.session.admit(false);
10662        assert_eq!(
10663            later.run(&[b"PING"]),
10664            "-NOAUTH Authentication required.\r\n"
10665        );
10666        later.server.set_password(b"");
10667        assert_eq!(later.run(&[b"PING"]), "+PONG\r\n");
10668    }
10669
10670    /// The password reads back in the clear and is set and cleared by the same
10671    /// pair of words.
10672    #[test]
10673    fn requirepass_reads_back_what_was_written_and_an_empty_one_clears_it() {
10674        let mut f = Fixture::new();
10675        // Open before the password goes on, so the connection keeps talking
10676        // after it does and can read it back.
10677        f.session.admit(true);
10678        assert_eq!(
10679            f.run(&[b"CONFIG", b"GET", b"requirepass"]),
10680            "*2\r\n$11\r\nrequirepass\r\n$0\r\n\r\n"
10681        );
10682        f.run(&[b"CONFIG", b"SET", b"requirepass", b"hunter2"]);
10683        assert_eq!(
10684            f.run(&[b"CONFIG", b"GET", b"requirepass"]),
10685            "*2\r\n$11\r\nrequirepass\r\n$7\r\nhunter2\r\n"
10686        );
10687        assert!(f.server.guarded());
10688        f.run(&[b"CONFIG", b"SET", b"requirepass", b""]);
10689        assert!(!f.server.guarded(), "an empty password did not clear it");
10690        assert_eq!(
10691            f.run(&[b"CONFIG", b"GET", b"requirepass"]),
10692            "*2\r\n$11\r\nrequirepass\r\n$0\r\n\r\n"
10693        );
10694    }
10695
10696    /// Every `DEBUG PROTOCOL` type, on RESP2, byte for byte off 8.10.1.
10697    #[test]
10698    fn debug_protocol_writes_what_the_reference_writes_on_resp2() {
10699        let mut f = Fixture::new();
10700        for (kind, want) in [
10701            ("string", "$11\r\nHello World\r\n"),
10702            ("integer", ":12345\r\n"),
10703            ("double", "$5\r\n3.141\r\n"),
10704            ("bignum", "$37\r\n1234567999999999999999999999999999999\r\n"),
10705            ("null", "$-1\r\n"),
10706            ("array", "*3\r\n:0\r\n:1\r\n:2\r\n"),
10707            ("set", "*3\r\n:0\r\n:1\r\n:2\r\n"),
10708            ("map", "*6\r\n:0\r\n:0\r\n:1\r\n:1\r\n:2\r\n:0\r\n"),
10709            (
10710                "attrib",
10711                "$39\r\nSome real reply following the attribute\r\n",
10712            ),
10713            ("push", "-ERR RESP2 is not supported by this command\r\n"),
10714            ("verbatim", "$25\r\nThis is a verbatim\nstring\r\n"),
10715            ("true", ":1\r\n"),
10716            ("false", ":0\r\n"),
10717        ] {
10718            assert_eq!(
10719                f.run(&[b"DEBUG", b"PROTOCOL", kind.as_bytes()]),
10720                want,
10721                "{kind}"
10722            );
10723        }
10724    }
10725
10726    /// And on RESP3, where all thirteen are their own type.
10727    #[test]
10728    fn debug_protocol_writes_what_the_reference_writes_on_resp3() {
10729        let mut f = Fixture::new();
10730        f.out = Out::new(Proto::Resp3);
10731        for (kind, want) in [
10732            ("string", "$11\r\nHello World\r\n"),
10733            ("integer", ":12345\r\n"),
10734            ("double", ",3.141\r\n"),
10735            ("bignum", "(1234567999999999999999999999999999999\r\n"),
10736            ("null", "_\r\n"),
10737            ("array", "*3\r\n:0\r\n:1\r\n:2\r\n"),
10738            ("set", "~3\r\n:0\r\n:1\r\n:2\r\n"),
10739            ("map", "%3\r\n:0\r\n#f\r\n:1\r\n#t\r\n:2\r\n#f\r\n"),
10740            (
10741                "attrib",
10742                "|1\r\n$14\r\nkey-popularity\r\n*2\r\n$7\r\nkey:123\r\n:90\r\n\
10743                 $39\r\nSome real reply following the attribute\r\n",
10744            ),
10745            (
10746                "push",
10747                "$40\r\nSome real reply following the push reply\r\n\
10748                 >2\r\n$16\r\nserver-cpu-usage\r\n:42\r\n",
10749            ),
10750            ("verbatim", "=29\r\ntxt:This is a verbatim\nstring\r\n"),
10751            ("true", "#t\r\n"),
10752            ("false", "#f\r\n"),
10753        ] {
10754            assert_eq!(
10755                f.run(&[b"DEBUG", b"PROTOCOL", kind.as_bytes()]),
10756                want,
10757                "{kind}"
10758            );
10759        }
10760    }
10761
10762    /// A type name that is not one of the thirteen lists all thirteen.
10763    #[test]
10764    fn debug_protocol_names_every_type_when_it_is_given_none_of_them() {
10765        let mut f = Fixture::new();
10766        for kind in [&b"bogus"[..], b""] {
10767            assert_eq!(
10768                f.run(&[b"DEBUG", b"PROTOCOL", kind]),
10769                "-ERR Wrong protocol type name. Please use one of the following: \
10770                 string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false\r\n"
10771            );
10772        }
10773    }
10774
10775    /// The one sentence `DEBUG` says about everything it cannot do.
10776    ///
10777    /// A subcommand that does not exist and a subcommand handed the wrong number
10778    /// of arguments are the same case on a real server, because both fall off
10779    /// the end of the same chain of tests, and the name is echoed in the case it
10780    /// arrived in.
10781    #[test]
10782    fn debug_says_the_same_thing_about_a_bad_name_and_a_bad_count() {
10783        let mut f = Fixture::new();
10784        for parts in [
10785            &[b"DEBUG".as_slice(), b"NOSUCH"][..],
10786            &[b"DEBUG", b"PROTOCOL"],
10787            &[b"DEBUG", b"PROTOCOL", b"string", b"extra"],
10788            &[b"DEBUG", b"SLEEP"],
10789            &[b"DEBUG", b"SET-ACTIVE-EXPIRE", b"1", b"2"],
10790            &[b"DEBUG", b"HELP", b"me"],
10791        ] {
10792            let got = f.run(parts);
10793            let name = String::from_utf8_lossy(parts[1]).to_string();
10794            assert_eq!(
10795                got,
10796                format!(
10797                    "-ERR unknown subcommand or wrong number of arguments for '{name}'. \
10798                     Try DEBUG HELP.\r\n"
10799                ),
10800                "{name}"
10801            );
10802        }
10803        assert_eq!(
10804            f.run(&[b"DEBUG"]),
10805            "-ERR wrong number of arguments for 'debug' command\r\n"
10806        );
10807    }
10808
10809    /// `DEBUG ERROR` writes the line it was given and nothing around it.
10810    #[test]
10811    fn debug_error_hands_back_whatever_it_was_given() {
10812        let mut f = Fixture::new();
10813        assert_eq!(f.run(&[b"DEBUG", b"ERROR", b"my error"]), "-my error\r\n");
10814        assert_eq!(f.run(&[b"DEBUG", b"ERROR", b""]), "-\r\n");
10815        // A code the caller made up goes out as the code, which is the whole use
10816        // of this: a client library testing that it branches on one.
10817        assert_eq!(
10818            f.run(&[b"DEBUG", b"ERROR", b"-WEIRD thing"]),
10819            "--WEIRD thing\r\n"
10820        );
10821        // And a newline in the middle cannot become a second reply.
10822        assert_eq!(
10823            f.run(&[b"DEBUG", b"ERROR", b"two\nlines"]),
10824            "-two lines\r\n"
10825        );
10826    }
10827
10828    /// `DEBUG POPULATE` fills a database and leaves what is already there.
10829    #[test]
10830    fn debug_populate_fills_and_skips_what_is_there() {
10831        let mut f = Fixture::new();
10832        assert_eq!(f.run(&[b"SET", b"key:0", b"mine"]), "+OK\r\n");
10833        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"3"]), "+OK\r\n");
10834        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n");
10835        assert_eq!(f.run(&[b"GET", b"key:0"]), "$4\r\nmine\r\n");
10836        assert_eq!(f.run(&[b"GET", b"key:2"]), "$7\r\nvalue:2\r\n");
10837        // A prefix is the whole of the name in front of the colon, so the colon
10838        // in a prefix that has one is not the separator and there are two.
10839        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"1", b"p:"]), "+OK\r\n");
10840        assert_eq!(f.run(&[b"GET", b"p::0"]), "$7\r\nvalue:0\r\n");
10841        // A size pads with zero bytes, and one shorter than the name cuts it.
10842        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"1", b"q", b"9"]), "+OK\r\n");
10843        assert_eq!(f.raw(&[b"GET", b"q:0"]), b"$9\r\nvalue:0\0\0\r\n");
10844        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"1", b"r", b"4"]), "+OK\r\n");
10845        assert_eq!(f.run(&[b"GET", b"r:0"]), "$4\r\nvalu\r\n");
10846        // And nought is not a size of nothing, it is no size at all.
10847        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"1", b"s", b"0"]), "+OK\r\n");
10848        assert_eq!(f.run(&[b"GET", b"s:0"]), "$7\r\nvalue:0\r\n");
10849    }
10850
10851    /// Both of `POPULATE`'s numbers complain about the range and not the digits.
10852    #[test]
10853    fn debug_populate_wants_two_numbers_that_are_not_negative() {
10854        let mut f = Fixture::new();
10855        for parts in [
10856            &[b"DEBUG".as_slice(), b"POPULATE", b"abc"][..],
10857            &[b"DEBUG", b"POPULATE", b"-1"],
10858            &[b"DEBUG", b"POPULATE", b"1.5"],
10859            &[b"DEBUG", b"POPULATE", b"1", b"p", b"-1"],
10860            &[b"DEBUG", b"POPULATE", b"1", b"p", b"x"],
10861        ] {
10862            assert_eq!(
10863                f.run(parts),
10864                "-ERR value is out of range, must be positive\r\n"
10865            );
10866        }
10867        assert_eq!(f.run(&[b"DEBUG", b"POPULATE", b"0"]), "+OK\r\n");
10868        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10869    }
10870
10871    /// The packed threshold takes a memory value up to just under four gigabytes.
10872    ///
10873    /// The error sentence says bigger than one and smaller than 4gb and neither
10874    /// half of that is what is checked, which is why the numbers here were taken
10875    /// off a running server rather than off the sentence.
10876    #[test]
10877    fn debug_quicklist_packed_threshold_takes_what_the_reference_takes() {
10878        let mut f = Fixture::new();
10879        for good in [
10880            &b"1"[..],
10881            b"2",
10882            b"1b",
10883            b"1K",
10884            b"1kb",
10885            b"1G",
10886            b"3gb",
10887            b"0",
10888            b"0b",
10889        ] {
10890            assert_eq!(
10891                f.run(&[b"DEBUG", b"QUICKLIST-PACKED-THRESHOLD", good]),
10892                "+OK\r\n",
10893                "{}",
10894                String::from_utf8_lossy(good)
10895            );
10896        }
10897        for bad in [
10898            &b"4gb"[..],
10899            b"4294967295",
10900            b"4294967296",
10901            b"abc",
10902            b"",
10903            b"+5",
10904            b"1.5",
10905        ] {
10906            assert_eq!(
10907                f.run(&[b"DEBUG", b"QUICKLIST-PACKED-THRESHOLD", bad]),
10908                "-ERR argument must be a memory value bigger than 1 and smaller than 4gb\r\n",
10909                "{}",
10910                String::from_utf8_lossy(bad)
10911            );
10912        }
10913    }
10914
10915    /// The three gates really gate, and they say `OK` to anything.
10916    #[test]
10917    fn the_debug_gates_turn_the_things_they_name_off_and_on_again() {
10918        let mut f = Fixture::new();
10919        for (sub, read) in [
10920            (&b"SET-ACTIVE-EXPIRE"[..], 0),
10921            (b"DICT-RESIZING", 1),
10922            (b"PAUSE-CRON", 2),
10923        ] {
10924            let reads: [fn(&Server) -> bool; 3] =
10925                [Server::expiring, Server::resizing, Server::cron_running];
10926            let on = reads[read];
10927            // `PAUSE-CRON` is the one whose argument means the opposite of the
10928            // gate, since it names the stopping and the gate names the running.
10929            let stop: &[u8] = if read == 2 { b"1" } else { b"0" };
10930            let go: &[u8] = if read == 2 { b"0" } else { b"1" };
10931            assert!(on(&f.server), "{}", String::from_utf8_lossy(sub));
10932            assert_eq!(f.run(&[b"DEBUG", sub, stop]), "+OK\r\n");
10933            assert!(!on(&f.server), "{}", String::from_utf8_lossy(sub));
10934            // A word is nought to `atoi`, so it turns the gate off rather than
10935            // being refused, and on `PAUSE-CRON` that means it starts the cron.
10936            assert_eq!(f.run(&[b"DEBUG", sub, b"nonsense"]), "+OK\r\n");
10937            assert_eq!(on(&f.server), read == 2);
10938            assert_eq!(f.run(&[b"DEBUG", sub, go]), "+OK\r\n");
10939            assert!(on(&f.server), "{}", String::from_utf8_lossy(sub));
10940        }
10941    }
10942
10943    /// A key past its deadline is not swept while the sweep is off.
10944    ///
10945    /// The lazy read still reports it gone, which is the same split a real
10946    /// server has: `SET-ACTIVE-EXPIRE 0` stops the background cycle and does not
10947    /// make an expired key readable.
10948    #[test]
10949    fn the_sweep_stops_when_debug_turns_it_off() {
10950        let mut f = Fixture::new();
10951        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PX", b"10"]), "+OK\r\n");
10952        assert_eq!(f.run(&[b"DEBUG", b"SET-ACTIVE-EXPIRE", b"0"]), "+OK\r\n");
10953        f.advance(50);
10954        assert_eq!(f.server.expire_slice(64), 0);
10955        assert_eq!(f.run(&[b"DEBUG", b"SET-ACTIVE-EXPIRE", b"1"]), "+OK\r\n");
10956        assert_eq!(f.server.expire_slice(64), 1);
10957    }
10958
10959    /// Five of the ten `COMMAND INFO` fields are sets once RESP3 has a set.
10960    ///
10961    /// This is every command and not just `DEBUG`, and it only shows on RESP3,
10962    /// which is why it went unnoticed until a wire compare looked at the bytes
10963    /// rather than at what a client decoded them into.
10964    #[test]
10965    fn command_info_sends_sets_where_the_reference_sends_sets() {
10966        let mut f = Fixture::new();
10967        f.out = Out::new(Proto::Resp3);
10968        assert_eq!(
10969            f.run(&[b"COMMAND", b"INFO", b"get"]),
10970            "*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\
10971             ~3\r\n+@read\r\n+@string\r\n+@fast\r\n~0\r\n~1\r\n%3\r\n\
10972             $5\r\nflags\r\n~2\r\n+RO\r\n+access\r\n\
10973             $12\r\nbegin_search\r\n%2\r\n$4\r\ntype\r\n$5\r\nindex\r\n$4\r\nspec\r\n\
10974             %1\r\n$5\r\nindex\r\n:1\r\n\
10975             $9\r\nfind_keys\r\n%2\r\n$4\r\ntype\r\n$5\r\nrange\r\n$4\r\nspec\r\n\
10976             %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\
10977             ~0\r\n"
10978        );
10979        // And RESP2, where a set is an array and nothing moved.
10980        let mut f = Fixture::new();
10981        assert_eq!(
10982            f.run(&[b"COMMAND", b"INFO", b"get"]),
10983            "*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\
10984             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*1\r\n*6\r\n\
10985             $5\r\nflags\r\n*2\r\n+RO\r\n+access\r\n\
10986             $12\r\nbegin_search\r\n*4\r\n$4\r\ntype\r\n$5\r\nindex\r\n$4\r\nspec\r\n\
10987             *2\r\n$5\r\nindex\r\n:1\r\n\
10988             $9\r\nfind_keys\r\n*4\r\n$4\r\ntype\r\n$5\r\nrange\r\n$4\r\nspec\r\n\
10989             *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\
10990             *0\r\n"
10991        );
10992    }
10993
10994    /// `DEBUG` is admin, so no monitor is ever shown one.
10995    #[test]
10996    fn debug_is_admin_and_stays_off_a_monitor_feed() {
10997        let spec = table::lookup(b"debug").expect("debug is in the table");
10998        assert!(spec.flags.contains(&"admin"));
10999        assert_eq!(spec.arity, -2);
11000        assert_eq!(spec.acl, ["@admin", "@slow", "@dangerous"]);
11001    }
11002
11003    #[test]
11004    fn the_command_counter_counts_every_command_including_the_bad_ones() {
11005        let mut f = Fixture::new();
11006        f.run(&[b"PING"]);
11007        f.run(&[b"NOPE"]);
11008        f.run(&[b"GET"]);
11009        assert_eq!(f.server.totals().commands, 3);
11010    }
11011
11012    #[test]
11013    fn what_a_thread_marked_is_taken_by_the_maintenance_turn() {
11014        let mut server = Server::new();
11015        server.set_threads(2);
11016        // A fresh server has every database on the turn's list, so start from
11017        // nothing to see the one mark arrive.
11018        server.mine().turn.store(0, Relaxed);
11019        server.locals[1].mark(1 << 9);
11020        server.collect_marks();
11021        assert!(server.mine().wanted(9));
11022        // And taken once rather than left to be taken again next turn.
11023        assert_eq!(server.locals[1].dirty.load(Relaxed), 0);
11024    }
11025
11026    #[test]
11027    fn what_two_threads_counted_is_added_up_when_info_asks() {
11028        let mut server = Server::new();
11029        server.set_threads(2);
11030        // Written into the two sets by hand, because what is under test is the
11031        // adding up and not the claiming, and one test thread can only ever
11032        // claim one set.
11033        let ping = lookup(b"PING").expect("PING is a command");
11034        for (at, calls) in [(0, 2), (1, 3)] {
11035            let counters = &server.locals[at];
11036            for _ in 0..calls {
11037                counters.stats.commands.bump();
11038                counters.cmdstats.at(ping).calls.bump();
11039            }
11040            counters.stats.opened();
11041        }
11042        assert_eq!(server.totals().commands, 5);
11043        assert_eq!(server.totals().clients, 2);
11044        assert_eq!(server.totals().connections, 2);
11045        let rows: Vec<_> = server.command_stats().collect();
11046        assert_eq!(rows.len(), 1);
11047        assert_eq!(rows[0].0, "ping");
11048        assert_eq!(rows[0].1.calls, 5);
11049        // A reset takes the totals and leaves the open connections, which are
11050        // still open.
11051        server.reset_stats();
11052        assert_eq!(server.totals().commands, 0);
11053        assert_eq!(server.totals().connections, 0);
11054        assert_eq!(server.totals().clients, 2);
11055    }
11056
11057    #[test]
11058    fn the_parked_count_says_what_the_waiter_list_says() {
11059        let mut f = Fixture::new();
11060        assert_eq!(f.server.parked(), 0);
11061        for client in 1..=3u64 {
11062            f.session = Session::new(client);
11063            assert_eq!(f.flow(&[b"BLPOP", b"q", b"0"]).0, Flow::Block);
11064        }
11065        assert_eq!(f.server.parked(), 3);
11066        assert_eq!(f.server.waiters().len(), 3);
11067
11068        // The three ways the list gets shorter, each of which has to move the
11069        // number with it, because a number left behind is either a walk of the
11070        // list that never happens or one that runs off the end of it.
11071        f.server.forget_waiters(2);
11072        assert_eq!(f.server.parked(), f.server.waiters().len());
11073        f.server.forget_waiters(1);
11074        assert_eq!(f.server.parked(), f.server.waiters().len());
11075        f.run(&[b"RPUSH", b"q", b"v"]);
11076        let mut out = Out::new(Proto::Resp2);
11077        assert!(f.server.serve_waiter(3, 0, &mut out));
11078        f.server.forget_waiters(3);
11079        assert_eq!(f.server.parked(), 0);
11080        assert!(f.server.waiters().is_empty());
11081    }
11082
11083    #[test]
11084    fn a_set_goes_from_bytes_to_bytes() {
11085        let mut f = Fixture::new();
11086        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
11087        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
11088        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
11089        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
11090        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
11091        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
11092        assert_eq!(
11093            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
11094            "*3\r\n:1\r\n:0\r\n:1\r\n"
11095        );
11096        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
11097        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
11098    }
11099
11100    #[test]
11101    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
11102        let mut f = Fixture::new();
11103        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
11104        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
11105        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
11106        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
11107        assert_eq!(
11108            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
11109            "*2\r\n:0\r\n:0\r\n"
11110        );
11111        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
11112    }
11113
11114    #[test]
11115    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
11116        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
11117        // and one that gets a `*` hands it a list, without either of them being
11118        // told which command was sent.
11119        let mut f = Fixture::new();
11120        f.run(&[b"SADD", b"s", b"one"]);
11121        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
11122
11123        f.run(&[b"HELLO", b"3"]);
11124        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
11125    }
11126
11127    #[test]
11128    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
11129        // An intset holds the number, so these digits exist for the first time
11130        // in the reply buffer.
11131        let mut f = Fixture::new();
11132        f.run(&[b"SADD", b"s", b"42"]);
11133        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
11134        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
11135        assert_eq!(
11136            f.run(&[b"SISMEMBER", b"s", b"042"]),
11137            ":0\r\n",
11138            "the member is the bytes and not the number they parse to"
11139        );
11140    }
11141
11142    #[test]
11143    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
11144        let mut f = Fixture::new();
11145        f.run(&[b"SET", b"str", b"v"]);
11146        f.run(&[b"SADD", b"set", b"a"]);
11147
11148        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
11149        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
11150        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
11151        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
11152        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
11153        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
11154        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
11155        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
11156        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
11157
11158        // MGET is the one that does not, because Redis gives nil for the odd
11159        // key out rather than failing the good keys next to it.
11160        assert_eq!(
11161            f.run(&[b"MGET", b"str", b"set", b"nope"]),
11162            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
11163        );
11164        // And plain SET overwrites any type, which takes the body with it.
11165        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
11166        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
11167    }
11168
11169    #[test]
11170    fn a_wrongtype_leaves_nothing_half_written() {
11171        // SMISMEMBER writes an array header and then one reply per member, so
11172        // it is the first command in the server that could get a header out in
11173        // front of an error if it checked its key in the wrong order.
11174        let mut f = Fixture::new();
11175        f.run(&[b"SET", b"k", b"v"]);
11176        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
11177        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
11178        assert!(!reply.contains('*'), "an array header went out in front");
11179    }
11180
11181    #[test]
11182    fn emptying_a_set_takes_the_key_with_it() {
11183        let mut f = Fixture::new();
11184        f.run(&[b"SADD", b"s", b"a", b"b"]);
11185        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
11186        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
11187        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
11188        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
11189        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
11190    }
11191
11192    /// Pull the cursor and the members out of one `SSCAN` reply.
11193    ///
11194    /// Crude on purpose. A test that walked a set through a real client would
11195    /// be testing the client, and what these tests are about is the shape of
11196    /// the bytes and the fact that a walk sees every member once.
11197    fn split_scan(reply: &str) -> (String, Vec<String>) {
11198        let mut lines = reply.split("\r\n");
11199        assert_eq!(lines.next(), Some("*2"), "got {reply}");
11200        lines.next().expect("the cursor header");
11201        let cursor = lines.next().expect("the cursor").to_owned();
11202        let header = lines.next().expect("the member header");
11203        let n: usize = header[1..].parse().expect("a member count");
11204        let mut members = Vec::with_capacity(n);
11205        for _ in 0..n {
11206            lines.next().expect("a member header");
11207            members.push(lines.next().expect("a member").to_owned());
11208        }
11209        (cursor, members)
11210    }
11211
11212    #[test]
11213    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
11214        let mut f = Fixture::new();
11215        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
11216
11217        let one = f.run(&[b"SPOP", b"s"]);
11218        assert!(
11219            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
11220            "got {one}"
11221        );
11222        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
11223
11224        // A count takes that many, and the last one takes the key with it.
11225        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
11226        assert!(rest.starts_with("*3\r\n"), "got {rest}");
11227        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
11228        // And a pop at a key that is not there is a nil, not an empty bulk.
11229        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
11230        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
11231    }
11232
11233    #[test]
11234    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
11235        // The one place in the server where the reply type carries something
11236        // the command name does not. SPOP's members are distinct so a RESP3
11237        // client can build a set out of them. SRANDMEMBER with a negative count
11238        // can hand back the same member three times, and a set would lose two.
11239        let mut f = Fixture::new();
11240        f.run(&[b"HELLO", b"3"]);
11241        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
11242
11243        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
11244        // And a positive count is an array too, since Redis makes it one.
11245        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
11246
11247        // A negative count against a set of one is where the difference bites:
11248        // the same member three times, which is a three element reply and would
11249        // have been a one element reply if it had gone out as a set.
11250        f.run(&[b"SADD", b"one", b"z"]);
11251        assert_eq!(
11252            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
11253            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
11254        );
11255    }
11256
11257    #[test]
11258    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
11259        let mut f = Fixture::new();
11260        f.run(&[b"SADD", b"s", b"only"]);
11261        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
11262        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
11263        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
11264
11265        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
11266        // The count form answers an empty array rather than a nil, which is the
11267        // pair of answers Redis gives and is not the pair it looks like.
11268        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
11269        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
11270        // Asking for more than is there answers all of it once and not padding.
11271        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
11272    }
11273
11274    #[test]
11275    fn a_pop_count_that_is_not_a_positive_number_says_so() {
11276        let mut f = Fixture::new();
11277        f.run(&[b"SADD", b"s", b"a"]);
11278        let bad = "-ERR value is out of range, must be positive\r\n";
11279        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
11280        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
11281        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
11282        // Zero is allowed and is a real answer rather than an error.
11283        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
11284        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
11285    }
11286
11287    #[test]
11288    fn a_scan_walks_a_set_of_any_size_exactly_once() {
11289        let mut f = Fixture::new();
11290        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
11291        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
11292            .into_iter()
11293            .chain(members.iter().map(Vec::as_slice))
11294            .collect();
11295        f.run(&args);
11296
11297        let mut seen = Vec::new();
11298        let mut cursor = "0".to_owned();
11299        loop {
11300            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
11301            let (next, got) = split_scan(&reply);
11302            seen.extend(got);
11303            cursor = next;
11304            if cursor == "0" {
11305                break;
11306            }
11307        }
11308        seen.sort();
11309        seen.dedup();
11310        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
11311
11312        // A set small enough to be a listpack answers in one call whatever
11313        // cursor it was handed, which is what Redis does for that encoding.
11314        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
11315        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
11316        assert_eq!(cursor, "0");
11317        assert_eq!(got.len(), 3);
11318        // And a key that is not there is a finished scan of nothing.
11319        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
11320    }
11321
11322    #[test]
11323    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
11324        let mut f = Fixture::new();
11325        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
11326
11327        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
11328        let mut got = got;
11329        got.sort();
11330        assert_eq!(got, ["aa", "ab"]);
11331
11332        // An integer member has no digits stored anywhere, so MATCH is the one
11333        // place a scan pays to write some.
11334        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
11335        let mut got = got;
11336        got.sort();
11337        assert_eq!(got, ["12", "13"]);
11338
11339        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
11340        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
11341        assert_eq!(
11342            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
11343            "-ERR syntax error\r\n"
11344        );
11345        // A count under one is a syntax error and not a range error, which is
11346        // the odder of Redis's two answers and the reason it is copied exactly.
11347        assert_eq!(
11348            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
11349            "-ERR syntax error\r\n"
11350        );
11351    }
11352
11353    #[test]
11354    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
11355        let mut f = Fixture::new();
11356        f.run(&[b"SADD", b"src", b"a", b"b"]);
11357        f.run(&[b"SADD", b"dst", b"c"]);
11358
11359        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
11360        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
11361        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
11362        // A member that is not in the source is a zero and moves nothing.
11363        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
11364        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
11365
11366        // A destination that does not exist gets made, and a source that runs
11367        // out goes away.
11368        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
11369        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
11370        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
11371    }
11372
11373    #[test]
11374    fn moving_checks_the_types_in_the_order_redis_checks_them() {
11375        // Not the order it looks like it should be. A source that is not there
11376        // answers zero without ever looking at the destination, so this is a
11377        // zero and not a WRONGTYPE even though the destination is a string.
11378        let mut f = Fixture::new();
11379        f.run(&[b"SET", b"str", b"v"]);
11380        f.run(&[b"SADD", b"set", b"a"]);
11381
11382        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
11383        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
11384        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
11385        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
11386        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
11387        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
11388        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
11389        assert_eq!(
11390            f.run(&[b"SISMEMBER", b"set", b"a"]),
11391            ":1\r\n",
11392            "and none of that moved anything"
11393        );
11394    }
11395
11396    #[test]
11397    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
11398        // SSCAN writes an outer array header before it walks, so it is the
11399        // command most likely to get bytes out in front of an error.
11400        let mut f = Fixture::new();
11401        f.run(&[b"SADD", b"s", b"a"]);
11402        for bad in [
11403            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
11404            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
11405            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
11406        ] {
11407            let reply = f.run(bad);
11408            assert!(reply.starts_with("-ERR"), "got {reply}");
11409            assert!(!reply.contains('*'), "an array header went out in front");
11410        }
11411    }
11412
11413    #[test]
11414    fn a_hash_writes_reads_and_deletes_its_fields() {
11415        let mut f = Fixture::new();
11416        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
11417        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
11418        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
11419        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
11420        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
11421        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
11422        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
11423        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
11424        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
11425        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
11426
11427        // The value the client sent is `9`, so HGET h b must not find the `2`
11428        // that is a value. A search with a step of one would have.
11429        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
11430
11431        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
11432        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
11433        assert_eq!(
11434            f.run(&[b"EXISTS", b"h"]),
11435            ":0\r\n",
11436            "and losing the last field lost the key"
11437        );
11438    }
11439
11440    #[test]
11441    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
11442        let mut f = Fixture::new();
11443        f.run(&[b"HSET", b"h", b"a", b"1"]);
11444        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
11445        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
11446        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
11447        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
11448        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
11449
11450        f.run(&[b"HELLO", b"3"]);
11451        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
11452        assert_eq!(
11453            f.run(&[b"HGETALL", b"nokey"]),
11454            "%0\r\n",
11455            "a missing key is the empty hash and never a nil"
11456        );
11457        assert_eq!(
11458            f.run(&[b"HKEYS", b"h"]),
11459            "*1\r\n$1\r\na\r\n",
11460            "and the two that answer one side stay arrays"
11461        );
11462    }
11463
11464    #[test]
11465    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
11466        let mut f = Fixture::new();
11467        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
11468        assert_eq!(
11469            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
11470            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
11471            "the reply is positional, so b is a nil and not a gap"
11472        );
11473        assert_eq!(
11474            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
11475            "*2\r\n$-1\r\n$-1\r\n",
11476            "and a missing key is all nils rather than an empty array"
11477        );
11478
11479        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
11480        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
11481        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
11482    }
11483
11484    #[test]
11485    fn a_hash_counts_up_and_says_so_when_it_cannot() {
11486        let mut f = Fixture::new();
11487        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
11488        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
11489        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
11490        assert_eq!(
11491            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
11492            "$4\r\n10.5\r\n",
11493            "a bulk string and not a double, on both protocols"
11494        );
11495
11496        f.run(&[b"HSET", b"h", b"s", b"words"]);
11497        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
11498        assert!(
11499            bad.starts_with("-ERR hash value is not an integer"),
11500            "{bad}"
11501        );
11502        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
11503        assert!(
11504            bad.starts_with("-ERR value is not an integer"),
11505            "a bad argument is not yet a hash value, {bad}"
11506        );
11507        assert_eq!(
11508            f.run(&[b"HGET", b"h", b"s"]),
11509            "$5\r\nwords\r\n",
11510            "and neither of them wrote anything"
11511        );
11512    }
11513
11514    #[test]
11515    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
11516        // Fourteen minutes under Miri at five hundred, which was the slowest
11517        // test in this crate that was not about megabytes. What the count has
11518        // to be is more than one page of the cursor, and the count below is
11519        // thirty two, so ninety six is three pages and asks the same question.
11520        let fields = if cfg!(miri) { 96 } else { 500 };
11521        let mut f = Fixture::new();
11522        for i in 0..fields {
11523            let field = format!("field-{i}");
11524            let value = format!("value-{i}");
11525            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
11526        }
11527
11528        let mut seen: Vec<String> = Vec::new();
11529        let mut cursor = "0".to_owned();
11530        loop {
11531            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
11532            let (next, items) = scan_reply(&reply);
11533            assert_eq!(items.len() % 2, 0, "a pair went out half written");
11534            for pair in items.chunks(2) {
11535                assert_eq!(
11536                    pair[0].strip_prefix("field-"),
11537                    pair[1].strip_prefix("value-"),
11538                    "a field came back with someone else's value"
11539                );
11540                seen.push(pair[0].clone());
11541            }
11542            cursor = next;
11543            if cursor == "0" {
11544                break;
11545            }
11546        }
11547        seen.sort();
11548        seen.dedup();
11549        assert_eq!(seen.len(), fields, "every field once and only once");
11550
11551        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
11552        assert!(
11553            items.iter().all(|s| s.starts_with("field-")),
11554            "NOVALUES still sent the values"
11555        );
11556
11557        let last = fields - 1;
11558        let (_, one) = scan_reply(&f.run(&[
11559            b"HSCAN",
11560            b"h",
11561            b"0",
11562            b"MATCH",
11563            format!("field-{last}").as_bytes(),
11564            b"COUNT",
11565            b"1000",
11566        ]));
11567        assert_eq!(
11568            one,
11569            [format!("field-{last}"), format!("value-{last}")],
11570            "MATCH is on the field"
11571        );
11572    }
11573
11574    #[test]
11575    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
11576        let mut f = Fixture::new();
11577        f.run(&[b"HSET", b"h", b"a", b"1"]);
11578        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
11579        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
11580        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
11581        assert_eq!(
11582            f.run(&[b"HRANDFIELD", b"h", b"3"]),
11583            "*1\r\n$1\r\na\r\n",
11584            "a positive count is capped at the size of the hash"
11585        );
11586        assert_eq!(
11587            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
11588            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
11589            "and a negative one repeats itself"
11590        );
11591        assert_eq!(
11592            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
11593            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
11594            "flat on RESP2"
11595        );
11596
11597        f.run(&[b"HELLO", b"3"]);
11598        assert_eq!(
11599            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
11600            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
11601            "and nested on RESP3, but still an array and never a map"
11602        );
11603    }
11604
11605    #[test]
11606    fn every_hash_command_says_wrongtype_and_writes_nothing() {
11607        let mut f = Fixture::new();
11608        f.run(&[b"SET", b"str", b"v"]);
11609        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
11610
11611        for cmd in [
11612            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
11613            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
11614            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
11615            &[b"HGET".as_slice(), b"str", b"f"][..],
11616            &[b"HMGET".as_slice(), b"str", b"f"][..],
11617            &[b"HDEL".as_slice(), b"str", b"f"][..],
11618            &[b"HLEN".as_slice(), b"str"][..],
11619            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
11620            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
11621            &[b"HGETALL".as_slice(), b"str"][..],
11622            &[b"HKEYS".as_slice(), b"str"][..],
11623            &[b"HVALS".as_slice(), b"str"][..],
11624            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
11625            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
11626            &[b"HRANDFIELD".as_slice(), b"str"][..],
11627            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
11628            &[b"HSCAN".as_slice(), b"str", b"0"][..],
11629        ] {
11630            let reply = f.run(cmd);
11631            assert_eq!(reply, wrong, "{:?}", cmd[0]);
11632        }
11633        assert_eq!(
11634            f.run(&[b"GET", b"str"]),
11635            "$1\r\nv\r\n",
11636            "and none of them touched the value"
11637        );
11638    }
11639
11640    #[test]
11641    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
11642        let mut f = Fixture::new();
11643        f.run(&[b"HSET", b"h", b"f", b"v"]);
11644        for bad in [
11645            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
11646            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
11647            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
11648            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
11649        ] {
11650            let reply = f.run(bad);
11651            assert!(reply.starts_with("-ERR"), "got {reply}");
11652            assert!(!reply.contains('*'), "an array header went out in front");
11653        }
11654    }
11655
11656    #[test]
11657    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
11658        let mut f = Fixture::new();
11659        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
11660        assert_eq!(
11661            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
11662            "*1\r\n:1\r\n"
11663        );
11664        assert_eq!(
11665            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
11666            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
11667            "one answer per field, and the two sentinels are TTL's own"
11668        );
11669
11670        // The same deadline in the other three units, all of them derived from
11671        // the one number the store kept.
11672        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
11673        assert!((99_000..=100_000).contains(&ms), "got {ms}");
11674        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
11675        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
11676        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
11677        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
11678
11679        assert_eq!(
11680            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
11681            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
11682            "one for the deadline taken off, and it does not say what it was"
11683        );
11684        assert_eq!(
11685            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
11686            "*1\r\n:-1\r\n"
11687        );
11688        assert_eq!(
11689            f.run(&[b"HGET", b"h", b"a"]),
11690            "$1\r\n1\r\n",
11691            "and the field is still there with the value it had"
11692        );
11693    }
11694
11695    #[test]
11696    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
11697        let mut f = Fixture::new();
11698        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
11699        assert_eq!(
11700            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
11701            "*1\r\n:2\r\n",
11702            "two, and not one, because nothing was stored"
11703        );
11704        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
11705        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
11706
11707        assert_eq!(
11708            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
11709            "*1\r\n:2\r\n"
11710        );
11711        assert_eq!(
11712            f.run(&[b"EXISTS", b"h"]),
11713            ":0\r\n",
11714            "and the last field going took the key with it"
11715        );
11716
11717        // Zero is a delete and not an error, where minus one is an error. That
11718        // is Redis's split and it is easy to get backwards.
11719        f.run(&[b"HSET", b"h", b"a", b"1"]);
11720        assert_eq!(
11721            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
11722            "*1\r\n:2\r\n"
11723        );
11724    }
11725
11726    #[test]
11727    fn a_field_is_gone_once_its_moment_passes() {
11728        let mut f = Fixture::new();
11729        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
11730        assert_eq!(
11731            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
11732            "*1\r\n:1\r\n"
11733        );
11734        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
11735
11736        // Time moves once per turn of the event loop and nowhere else, so a
11737        // test moves it by hand rather than by sleeping. There is nothing to
11738        // sleep for: the deadline is a number and so is the clock.
11739        f.server.advance_clock_ms(60);
11740        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
11741        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
11742        assert_eq!(
11743            f.run(&[b"HGETALL", b"h"]),
11744            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
11745            "and the walks do not hand back a field that has expired"
11746        );
11747    }
11748
11749    #[test]
11750    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
11751        let mut f = Fixture::new();
11752        for cmd in [
11753            &[
11754                b"HEXPIRE".as_slice(),
11755                b"nokey",
11756                b"100",
11757                b"FIELDS",
11758                b"2",
11759                b"a",
11760                b"b",
11761            ][..],
11762            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
11763            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
11764            &[
11765                b"HEXPIRETIME".as_slice(),
11766                b"nokey",
11767                b"FIELDS",
11768                b"2",
11769                b"a",
11770                b"b",
11771            ][..],
11772            &[
11773                b"HPERSIST".as_slice(),
11774                b"nokey",
11775                b"FIELDS",
11776                b"2",
11777                b"a",
11778                b"b",
11779            ][..],
11780        ] {
11781            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
11782        }
11783    }
11784
11785    #[test]
11786    fn writing_a_field_clears_the_deadline_that_was_on_it() {
11787        let mut f = Fixture::new();
11788        f.run(&[b"HSET", b"h", b"a", b"1"]);
11789        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
11790        f.run(&[b"HSET", b"h", b"a", b"2"]);
11791        assert_eq!(
11792            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
11793            "*1\r\n:-1\r\n",
11794            "Redis has done this since 7.4, and it is why HGETEX exists"
11795        );
11796    }
11797
11798    #[test]
11799    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
11800        let mut f = Fixture::new();
11801        f.run(&[b"HSET", b"h", b"a", b"1"]);
11802        assert_eq!(
11803            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
11804            "*1\r\n:0\r\n",
11805            "XX on a field with no deadline changes nothing"
11806        );
11807        assert_eq!(
11808            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
11809            "*1\r\n:1\r\n"
11810        );
11811        assert_eq!(
11812            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
11813            "*1\r\n:0\r\n",
11814            "and NX will not move one that is already there"
11815        );
11816        assert_eq!(
11817            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
11818            "*1\r\n:0\r\n"
11819        );
11820        assert_eq!(
11821            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
11822            "*1\r\n:1\r\n"
11823        );
11824        assert_eq!(
11825            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
11826            "*1\r\n:1\r\n"
11827        );
11828        assert_eq!(
11829            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
11830            "*1\r\n:50\r\n"
11831        );
11832    }
11833
11834    #[test]
11835    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
11836        let mut f = Fixture::new();
11837        f.run(&[b"HSET", b"h", b"a", b"1"]);
11838        for (bad, want) in [
11839            (
11840                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
11841                "-ERR invalid expire time, must be >= 0",
11842            ),
11843            (
11844                &[
11845                    b"HEXPIRE".as_slice(),
11846                    b"h",
11847                    b"9999999999999999",
11848                    b"FIELDS",
11849                    b"1",
11850                    b"a",
11851                ][..],
11852                "-ERR invalid expire time in 'hexpire' command",
11853            ),
11854            (
11855                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
11856                "-ERR wrong number of arguments for 'hexpire' command",
11857            ),
11858            (
11859                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
11860                "-ERR Parameter `numFields` should be greater than 0",
11861            ),
11862            (
11863                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
11864                "-ERR wrong number of arguments",
11865            ),
11866            (
11867                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
11868                "-ERR wrong number of arguments",
11869            ),
11870        ] {
11871            let reply = f.run(bad);
11872            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
11873            assert!(!reply.contains('*'), "an array header went out in front");
11874        }
11875        assert_eq!(
11876            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
11877            "*1\r\n:-1\r\n",
11878            "and not one of them put a deadline on anything"
11879        );
11880    }
11881
11882    #[test]
11883    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
11884        let mut f = Fixture::new();
11885        f.run(&[b"SET", b"str", b"v"]);
11886        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
11887
11888        for cmd in [
11889            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
11890            &[
11891                b"HPEXPIRE".as_slice(),
11892                b"str",
11893                b"100",
11894                b"FIELDS",
11895                b"1",
11896                b"f",
11897            ][..],
11898            &[
11899                b"HEXPIREAT".as_slice(),
11900                b"str",
11901                b"9999999999",
11902                b"FIELDS",
11903                b"1",
11904                b"f",
11905            ][..],
11906            &[
11907                b"HPEXPIREAT".as_slice(),
11908                b"str",
11909                b"9999999999999",
11910                b"FIELDS",
11911                b"1",
11912                b"f",
11913            ][..],
11914            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
11915            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
11916            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
11917            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
11918            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
11919        ] {
11920            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
11921        }
11922        assert_eq!(
11923            f.run(&[b"GET", b"str"]),
11924            "$1\r\nv\r\n",
11925            "and none of them touched the value"
11926        );
11927    }
11928
11929    #[test]
11930    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
11931        let mut f = Fixture::new();
11932        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
11933        assert_eq!(
11934            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
11935            "*2\r\n$1\r\n1\r\n$-1\r\n",
11936            "positional, so the field that was not there is a nil in its place"
11937        );
11938        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
11939        assert_eq!(
11940            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
11941            "*1\r\n$-1\r\n"
11942        );
11943        assert_eq!(
11944            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
11945            "*1\r\n$1\r\n2\r\n"
11946        );
11947        assert_eq!(
11948            f.run(&[b"EXISTS", b"h"]),
11949            ":0\r\n",
11950            "and the last field took the key"
11951        );
11952    }
11953
11954    #[test]
11955    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
11956        let mut f = Fixture::new();
11957        f.run(&[b"HSET", b"h", b"a", b"1"]);
11958        assert_eq!(
11959            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
11960            "*1\r\n$1\r\n1\r\n"
11961        );
11962        assert_eq!(
11963            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
11964            "*1\r\n:-1\r\n",
11965            "no option means leave it alone, which is the one place this is not GETEX"
11966        );
11967
11968        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
11969        assert_eq!(
11970            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
11971            "*1\r\n:100\r\n"
11972        );
11973        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
11974        assert_eq!(
11975            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
11976            "*1\r\n:100\r\n",
11977            "and a plain read really does leave it alone"
11978        );
11979        assert_eq!(
11980            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
11981            "*1\r\n$1\r\n1\r\n"
11982        );
11983        assert_eq!(
11984            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
11985            "*1\r\n:-1\r\n"
11986        );
11987
11988        assert_eq!(
11989            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
11990            "*1\r\n$1\r\n1\r\n",
11991            "the value goes out before the deadline that has already gone is applied"
11992        );
11993        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
11994        assert_eq!(
11995            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
11996            "*1\r\n$-1\r\n"
11997        );
11998    }
11999
12000    #[test]
12001    fn hsetex_writes_all_of_it_or_none_of_it() {
12002        let mut f = Fixture::new();
12003        assert_eq!(
12004            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
12005            ":1\r\n"
12006        );
12007        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
12008        assert_eq!(
12009            f.run(&[
12010                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
12011            ]),
12012            ":0\r\n",
12013            "FNX wants every field named to be missing"
12014        );
12015        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
12016        assert_eq!(
12017            f.run(&[b"HEXISTS", b"h", b"new"]),
12018            ":0\r\n",
12019            "and none of the list was written"
12020        );
12021        assert_eq!(
12022            f.run(&[
12023                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
12024            ]),
12025            ":0\r\n",
12026            "and FXX wants every one of them to be there"
12027        );
12028        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
12029        assert_eq!(
12030            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
12031            ":1\r\n"
12032        );
12033        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
12034
12035        assert_eq!(
12036            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
12037            ":0\r\n"
12038        );
12039        assert_eq!(
12040            f.run(&[b"EXISTS", b"gone"]),
12041            ":0\r\n",
12042            "a key with no fields cannot meet FXX and is not created trying"
12043        );
12044    }
12045
12046    #[test]
12047    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
12048        let mut f = Fixture::new();
12049        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
12050        assert_eq!(
12051            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12052            "*1\r\n:100\r\n"
12053        );
12054
12055        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
12056        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
12057        assert_eq!(
12058            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12059            "*1\r\n:100\r\n",
12060            "KEEPTTL put back what the write cleared"
12061        );
12062
12063        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
12064        assert_eq!(
12065            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12066            "*1\r\n:-1\r\n",
12067            "and without it a write clears the deadline the way HSET does"
12068        );
12069
12070        // Any order, because Redis reads these in a loop and not in a fixed
12071        // sequence.
12072        assert_eq!(
12073            f.run(&[
12074                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
12075            ]),
12076            ":1\r\n"
12077        );
12078        assert_eq!(
12079            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12080            "*1\r\n:100\r\n"
12081        );
12082
12083        assert_eq!(
12084            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
12085            ":1\r\n",
12086            "written, and not the separate code the HEXPIRE family has for this"
12087        );
12088        assert_eq!(
12089            f.run(&[b"EXISTS", b"h"]),
12090            ":0\r\n",
12091            "and storing it and then removing it emptied the hash"
12092        );
12093    }
12094
12095    #[test]
12096    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
12097        let mut f = Fixture::new();
12098        f.run(&[b"HSET", b"h", b"a", b"1"]);
12099        for (bad, want) in [
12100            // HGETDEL has three sentences of its own for these three mistakes.
12101            (
12102                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
12103                "-ERR Number of fields must be a positive integer",
12104            ),
12105            (
12106                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
12107                "-ERR The `numfields` parameter must match the number of arguments",
12108            ),
12109            (
12110                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
12111                "-ERR Mandatory argument FIELDS is missing or not at the right position",
12112            ),
12113            // And HGETEX and HSETEX have three different ones between them.
12114            (
12115                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
12116                "-ERR invalid number of fields",
12117            ),
12118            (
12119                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
12120                "-ERR wrong number of arguments",
12121            ),
12122            (
12123                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
12124                "-ERR unknown argument: FIELD",
12125            ),
12126            (
12127                &[
12128                    b"HGETEX".as_slice(),
12129                    b"h",
12130                    b"KEEPTTL",
12131                    b"FIELDS",
12132                    b"1",
12133                    b"a",
12134                ][..],
12135                "-ERR unknown argument: KEEPTTL",
12136            ),
12137            (
12138                &[
12139                    b"HGETEX".as_slice(),
12140                    b"h",
12141                    b"EX",
12142                    b"100",
12143                    b"PERSIST",
12144                    b"FIELDS",
12145                    b"1",
12146                    b"a",
12147                ][..],
12148                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
12149            ),
12150            (
12151                &[
12152                    b"HSETEX".as_slice(),
12153                    b"h",
12154                    b"EX",
12155                    b"1",
12156                    b"KEEPTTL",
12157                    b"FIELDS",
12158                    b"1",
12159                    b"a",
12160                    b"1",
12161                ][..],
12162                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
12163            ),
12164            (
12165                &[
12166                    b"HSETEX".as_slice(),
12167                    b"h",
12168                    b"FNX",
12169                    b"FXX",
12170                    b"FIELDS",
12171                    b"1",
12172                    b"a",
12173                    b"1",
12174                ][..],
12175                "-ERR Only one of FXX or FNX arguments can be specified",
12176            ),
12177            (
12178                &[
12179                    b"HSETEX".as_slice(),
12180                    b"h",
12181                    b"FIELDS",
12182                    b"2",
12183                    b"a",
12184                    b"1",
12185                    b"b",
12186                ][..],
12187                "-ERR wrong number of arguments",
12188            ),
12189            (
12190                &[
12191                    b"HGETEX".as_slice(),
12192                    b"h",
12193                    b"EX",
12194                    b"-1",
12195                    b"FIELDS",
12196                    b"1",
12197                    b"a",
12198                ][..],
12199                "-ERR invalid expire time, must be >= 0",
12200            ),
12201            (
12202                &[
12203                    b"HGETEX".as_slice(),
12204                    b"h",
12205                    b"PXAT",
12206                    b"99999999999999",
12207                    b"FIELDS",
12208                    b"1",
12209                    b"a",
12210                ][..],
12211                "-ERR invalid expire time in 'hgetex' command",
12212            ),
12213            (
12214                &[
12215                    b"HSETEX".as_slice(),
12216                    b"h",
12217                    b"EX",
12218                    b"abc",
12219                    b"FIELDS",
12220                    b"1",
12221                    b"a",
12222                    b"1",
12223                ][..],
12224                "-ERR value is not an integer or out of range",
12225            ),
12226        ] {
12227            let reply = f.run(bad);
12228            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
12229            assert!(!reply.contains('*'), "an array header went out in front");
12230        }
12231        assert_eq!(
12232            f.run(&[b"HGET", b"h", b"a"]),
12233            "$1\r\n1\r\n",
12234            "and not one of them wrote anything"
12235        );
12236        assert_eq!(
12237            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
12238            "*1\r\n:-1\r\n"
12239        );
12240    }
12241
12242    #[test]
12243    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
12244        let mut f = Fixture::new();
12245        f.run(&[b"SET", b"str", b"v"]);
12246        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
12247        for cmd in [
12248            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
12249            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
12250            &[
12251                b"HGETEX".as_slice(),
12252                b"str",
12253                b"EX",
12254                b"100",
12255                b"FIELDS",
12256                b"1",
12257                b"f",
12258            ][..],
12259            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
12260        ] {
12261            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
12262        }
12263        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
12264    }
12265
12266    /// The two orders `HIMPORT` juggles, which are not the same order.
12267    ///
12268    /// Values arrive in the order the fields were declared in and the hash is
12269    /// built in sorted order, so the first value is not generally the first
12270    /// field. And the sort is by length before bytes, which nothing else here
12271    /// sorts names with: `b` comes before `aa` where a plain byte comparison
12272    /// would put `aa` first. Both read off 8.10.1.
12273    #[test]
12274    fn himport_writes_declared_values_into_sorted_fields() {
12275        let mut f = Fixture::new();
12276        assert_eq!(
12277            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
12278            "+OK\r\n"
12279        );
12280        assert_eq!(
12281            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
12282            "+OK\r\n"
12283        );
12284        assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
12285        assert_eq!(
12286            f.run(&[b"HGETALL", b"k"]),
12287            bulks(&["a", "3", "b", "1", "aa", "2"])
12288        );
12289    }
12290
12291    /// It replaces the key rather than writing over it, so a field the fieldset
12292    /// does not name is gone afterwards and so is the deadline.
12293    #[test]
12294    fn himport_set_replaces_the_whole_key() {
12295        let mut f = Fixture::new();
12296        f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
12297        f.run(&[b"EXPIRE", b"k", b"100"]);
12298        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
12299        assert_eq!(
12300            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
12301            "+OK\r\n"
12302        );
12303        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
12304        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
12305    }
12306
12307    /// A fieldset is connection state. `SELECT` leaves them alone and `RESET`
12308    /// throws them away, and a key built from one outlives it.
12309    #[test]
12310    fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
12311        let mut f = Fixture::new();
12312        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
12313        f.run(&[b"SELECT", b"1"]);
12314        assert_eq!(
12315            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
12316            "+OK\r\n"
12317        );
12318        f.run(&[b"SELECT", b"0"]);
12319        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
12320        assert_eq!(
12321            f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
12322            "-ERR no such fieldset\r\n"
12323        );
12324    }
12325
12326    /// Which complaint wins when a line is wrong in more than one place.
12327    ///
12328    /// The type of the key beats both of the others, so a `HIMPORT SET` against
12329    /// a string is a WRONGTYPE even when the fieldset is missing too, which is
12330    /// the ordering a real server has and not the one the argument order
12331    /// suggests.
12332    #[test]
12333    fn himport_complains_in_the_order_a_real_server_does() {
12334        let mut f = Fixture::new();
12335        f.run(&[b"SET", b"str", b"v"]);
12336        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
12337        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
12338        assert_eq!(
12339            f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
12340            wrong,
12341            "the type beats a missing fieldset"
12342        );
12343        assert_eq!(
12344            f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
12345            wrong,
12346            "and it beats a value count that does not fit"
12347        );
12348        assert_eq!(
12349            f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
12350            "-ERR no such fieldset\r\n"
12351        );
12352        // One sentence for too few and for too many alike.
12353        for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
12354            let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
12355            line.extend_from_slice(values);
12356            assert_eq!(
12357                f.run(&line),
12358                "-ERR value count does not match fieldset field count\r\n",
12359                "{} values into two fields",
12360                values.len()
12361            );
12362        }
12363        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
12364    }
12365
12366    /// The arity of each subcommand, and the unknown one.
12367    #[test]
12368    fn himport_checks_each_subcommand_count_under_its_own_name() {
12369        let mut f = Fixture::new();
12370        assert_eq!(
12371            f.run(&[b"HIMPORT"]),
12372            "-ERR wrong number of arguments for 'himport' command\r\n"
12373        );
12374        for (rest, name) in [
12375            (&["PREPARE"][..], "prepare"),
12376            (&["PREPARE", "fs"][..], "prepare"),
12377            (&["SET"][..], "set"),
12378            (&["SET", "k"][..], "set"),
12379            (&["SET", "k", "fs"][..], "set"),
12380            (&["DISCARD"][..], "discard"),
12381            (&["DISCARD", "a", "b"][..], "discard"),
12382            (&["DISCARDALL", "x"][..], "discardall"),
12383        ] {
12384            let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
12385            line.extend(rest.iter().map(|a| a.as_bytes()));
12386            assert_eq!(
12387                f.run(&line),
12388                format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
12389                "HIMPORT {}",
12390                rest.join(" ")
12391            );
12392        }
12393        assert_eq!(
12394            f.run(&[b"HIMPORT", b"NOPE", b"x"]),
12395            "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
12396        );
12397    }
12398
12399    /// A `PREPARE` that fails leaves the name pointing where it pointed, which
12400    /// is the answer of the two that could not be guessed from outside.
12401    #[test]
12402    fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
12403        let mut f = Fixture::new();
12404        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
12405        assert_eq!(
12406            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
12407            "-ERR duplicate field name in fieldset\r\n"
12408        );
12409        assert_eq!(
12410            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
12411            "+OK\r\n"
12412        );
12413        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
12414    }
12415
12416    /// Preparing the same name twice replaces it, and the two discards count
12417    /// what they took rather than answering OK.
12418    #[test]
12419    fn himport_prepare_replaces_and_the_discards_count() {
12420        let mut f = Fixture::new();
12421        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
12422        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
12423        assert_eq!(
12424            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
12425            "+OK\r\n"
12426        );
12427        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
12428
12429        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
12430        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
12431        f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
12432        f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
12433        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
12434        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
12435    }
12436
12437    /// The one integer of a single element array reply.
12438    /// The number out of a plain integer reply.
12439    ///
12440    /// [`int_reply`] is the same thing wrapped in a one element array, which is
12441    /// the shape every hash field command answers in.
12442    fn int(reply: &str) -> i64 {
12443        let body = reply
12444            .strip_prefix(':')
12445            .and_then(|s| s.strip_suffix("\r\n"))
12446            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
12447        body.parse().expect("an integer")
12448    }
12449
12450    fn int_reply(reply: &str) -> i64 {
12451        let body = reply
12452            .strip_prefix("*1\r\n:")
12453            .and_then(|s| s.strip_suffix("\r\n"))
12454            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
12455        body.parse().expect("an integer")
12456    }
12457
12458    /// The cursor and the flat items of a scan reply.
12459    fn scan_reply(reply: &str) -> (String, Vec<String>) {
12460        let mut lines = reply.split("\r\n");
12461        assert_eq!(lines.next(), Some("*2"), "got {reply}");
12462        lines.next().expect("the cursor header");
12463        let cursor = lines.next().expect("a cursor").to_owned();
12464        let header = lines.next().expect("an item count");
12465        let n: usize = header[1..].parse().expect("a count");
12466        let mut items = Vec::with_capacity(n);
12467        for _ in 0..n {
12468            lines.next().expect("an item header");
12469            items.push(lines.next().expect("an item").to_owned());
12470        }
12471        (cursor, items)
12472    }
12473
12474    /// The members of a set reply, sorted, since none of these promise an
12475    /// order and a test that asserted one would be asserting an accident.
12476    fn sorted(reply: &str) -> Vec<String> {
12477        let mut lines = reply.split("\r\n");
12478        let header = lines.next().expect("a header");
12479        assert!(
12480            header.starts_with('*') || header.starts_with('~'),
12481            "got {reply}"
12482        );
12483        let n: usize = header[1..].parse().expect("a member count");
12484        let mut got = Vec::with_capacity(n);
12485        for _ in 0..n {
12486            lines.next().expect("a member header");
12487            got.push(lines.next().expect("a member").to_owned());
12488        }
12489        got.sort();
12490        got
12491    }
12492
12493    #[test]
12494    fn the_algebra_answers_what_the_sets_share_and_do_not() {
12495        let mut f = Fixture::new();
12496        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
12497        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
12498        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
12499
12500        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
12501        assert_eq!(
12502            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
12503            ["1", "2", "3", "4", "5"]
12504        );
12505        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
12506        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
12507
12508        // A key that is not there is an empty set, which empties an
12509        // intersection and does nothing at all to a union.
12510        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
12511        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
12512        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
12513        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
12514    }
12515
12516    #[test]
12517    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
12518        let mut f = Fixture::new();
12519        f.run(&[b"SADD", b"a", b"x"]);
12520        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
12521        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
12522        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
12523
12524        f.run(&[b"HELLO", b"3"]);
12525        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
12526        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
12527        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
12528        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
12529    }
12530
12531    #[test]
12532    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
12533        let mut f = Fixture::new();
12534        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
12535        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
12536
12537        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
12538        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
12539        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
12540        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
12541        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
12542        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
12543
12544        // An empty answer deletes the destination rather than leaving an empty
12545        // set behind, and the destination may be one of the sources.
12546        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
12547        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
12548        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
12549        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
12550
12551        // And a destination holding something else is overwritten, the same way
12552        // SET overwrites, rather than refused.
12553        f.run(&[b"SET", b"str", b"v"]);
12554        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
12555        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
12556    }
12557
12558    #[test]
12559    fn sintercard_counts_without_building_and_stops_at_a_limit() {
12560        let mut f = Fixture::new();
12561        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
12562        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
12563
12564        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
12565        assert_eq!(
12566            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
12567            ":2\r\n"
12568        );
12569        assert_eq!(
12570            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
12571            ":3\r\n",
12572            "a limit of zero is no limit"
12573        );
12574        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
12575        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
12576
12577        // The counted keys are what make its three error messages its own.
12578        assert_eq!(
12579            f.run(&[b"SINTERCARD", b"0", b"a"]),
12580            "-ERR numkeys should be greater than 0\r\n"
12581        );
12582        assert_eq!(
12583            f.run(&[b"SINTERCARD", b"abc", b"a"]),
12584            "-ERR numkeys should be greater than 0\r\n"
12585        );
12586        assert_eq!(
12587            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
12588            "-ERR Number of keys can't be greater than number of args\r\n"
12589        );
12590        assert_eq!(
12591            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
12592            "-ERR LIMIT can't be negative\r\n"
12593        );
12594        assert_eq!(
12595            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
12596            "-ERR syntax error\r\n"
12597        );
12598        // A key really can be called LIMIT, which is why the count exists.
12599        f.run(&[b"SADD", b"LIMIT", b"2"]);
12600        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
12601    }
12602
12603    /// The two Redis 8.10 added, which are SINTERCARD's shape over a union and
12604    /// over a difference. Every number here was read off 8.10.1 first.
12605    #[test]
12606    fn sunioncard_and_sdiffcard_count_without_building() {
12607        let mut f = Fixture::new();
12608        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
12609        f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
12610
12611        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
12612        assert_eq!(
12613            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
12614            ":2\r\n"
12615        );
12616        assert_eq!(
12617            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
12618            ":6\r\n",
12619            "a limit of zero is no limit"
12620        );
12621        assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
12622        assert_eq!(
12623            f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
12624            ":4\r\n",
12625            "a missing key adds nothing to a union"
12626        );
12627
12628        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
12629        assert_eq!(
12630            f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
12631            ":1\r\n"
12632        );
12633        assert_eq!(
12634            f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
12635            ":2\r\n",
12636            "a difference is not symmetric"
12637        );
12638        assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
12639        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
12640        assert_eq!(
12641            f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
12642            ":0\r\n",
12643            "nothing taken away from nothing"
12644        );
12645
12646        // The same three messages SINTERCARD has, because the line is the same
12647        // line and is parsed once for all three.
12648        for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
12649            assert_eq!(
12650                f.run(&[name, b"0", b"a"]),
12651                "-ERR numkeys should be greater than 0\r\n"
12652            );
12653            assert_eq!(
12654                f.run(&[name, b"abc", b"a"]),
12655                "-ERR numkeys should be greater than 0\r\n"
12656            );
12657            assert_eq!(
12658                f.run(&[name, b"-1", b"a"]),
12659                "-ERR numkeys should be greater than 0\r\n"
12660            );
12661            assert_eq!(
12662                f.run(&[name, b"3", b"a", b"b"]),
12663                "-ERR Number of keys can't be greater than number of args\r\n"
12664            );
12665            assert_eq!(
12666                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
12667                "-ERR LIMIT can't be negative\r\n"
12668            );
12669            assert_eq!(
12670                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
12671                "-ERR LIMIT can't be negative\r\n",
12672                "a LIMIT that is not a number gets the negative message too"
12673            );
12674            assert_eq!(
12675                f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
12676                "-ERR syntax error\r\n"
12677            );
12678            assert_eq!(
12679                f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
12680                "-ERR syntax error\r\n"
12681            );
12682            assert_eq!(
12683                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
12684                "-ERR syntax error\r\n"
12685            );
12686        }
12687
12688        // And a key called LIMIT is a key, here as much as on SINTERCARD.
12689        f.run(&[b"SADD", b"LIMIT", b"2"]);
12690        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
12691        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
12692    }
12693
12694    #[test]
12695    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
12696        let mut f = Fixture::new();
12697        f.run(&[b"SADD", b"a", b"1"]);
12698        f.run(&[b"SADD", b"d", b"old"]);
12699        f.run(&[b"SET", b"str", b"v"]);
12700
12701        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
12702        for bad in [
12703            &[b"SINTER".as_slice(), b"a", b"str"][..],
12704            &[b"SUNION".as_slice(), b"str"][..],
12705            &[b"SDIFF".as_slice(), b"a", b"str"][..],
12706            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
12707            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
12708            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
12709            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
12710        ] {
12711            let reply = f.run(bad);
12712            assert_eq!(reply, wrong, "for {:?}", bad[0]);
12713        }
12714        assert_eq!(
12715            f.run(&[b"SMEMBERS", b"d"]),
12716            "*1\r\n$3\r\nold\r\n",
12717            "and the destination was left alone every time"
12718        );
12719    }
12720
12721    /// The leak a set can spring that nothing on the wire would ever show: the
12722    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
12723    /// Not under Miri. What this claims is that memory does not grow over two
12724    /// hundred passes, so the passes are the claim rather than the way it
12725    /// happens to be written, and two hundred passes of a two hundred member
12726    /// collection is forty thousand trips through dispatch, which is what an
12727    /// interpreter charges for. A count small enough to run there would leave a
12728    /// server that reclaims nothing inside the bound and the test would pass on
12729    /// a leak. Nothing about memory safety goes uninterpreted either way: this
12730    /// is an accounting claim, and the same commands are run a few at a time by
12731    /// the tests around it.
12732    #[cfg_attr(miri, ignore = "the volume is the claim")]
12733    #[test]
12734    fn churning_sets_does_not_grow_the_server() {
12735        let mut f = Fixture::new();
12736        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
12737        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
12738            .chain(std::iter::once(&b"s"[..]))
12739            .chain(members.iter().map(Vec::as_slice))
12740            .collect();
12741
12742        f.run(&args);
12743        f.run(&[b"DEL", b"s"]);
12744        f.server.compact_step();
12745        let after_first = f.server.memory_bytes();
12746
12747        for _ in 0..200 {
12748            f.run(&args);
12749            f.run(&[b"DEL", b"s"]);
12750            f.server.compact_step();
12751        }
12752        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
12753        assert!(
12754            f.server.memory_bytes() <= after_first * 2,
12755            "held {} after two hundred passes against {after_first} after one",
12756            f.server.memory_bytes()
12757        );
12758    }
12759
12760    // --------------------------------------------------------------- bitmaps
12761
12762    /// The two single bit commands, and the encoding rule underneath them.
12763    ///
12764    /// A write always leaves the value `raw` and a read never re-encodes, which
12765    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
12766    /// with its first digit changed after a `SETBIT`.
12767    #[test]
12768    fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
12769        let mut f = Fixture::new();
12770        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
12771        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
12772        assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
12773        assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
12774        assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
12775        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
12776
12777        // Writing a nought past the end still creates the key and still pads.
12778        assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
12779        assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
12780        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
12781
12782        f.run(&[b"SET", b"num", b"12345"]);
12783        assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
12784        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
12785        assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
12786        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
12787        assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
12788    }
12789
12790    /// Counting, in bytes and in bits.
12791    ///
12792    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
12793    /// says 22 for it. The server is the thing being copied here.
12794    #[test]
12795    fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
12796        let mut f = Fixture::new();
12797        f.run(&[b"SET", b"mykey", b"foobar"]);
12798        assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
12799        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
12800        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
12801        assert_eq!(
12802            f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
12803            ":6\r\n"
12804        );
12805        assert_eq!(
12806            f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
12807            ":25\r\n"
12808        );
12809        assert_eq!(
12810            f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
12811            ":17\r\n"
12812        );
12813        assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
12814
12815        // A start past the end is left where it is and the end is pulled back,
12816        // so the range comes out backwards and counts nothing.
12817        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
12818
12819        // A lone start is a syntax error here, where BITPOS allows it.
12820        assert_eq!(
12821            f.run(&[b"BITCOUNT", b"mykey", b"0"]),
12822            "-ERR syntax error\r\n"
12823        );
12824        assert_eq!(
12825            f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
12826            "-ERR syntax error\r\n"
12827        );
12828    }
12829
12830    /// Searching, and the one place a miss is not minus one.
12831    ///
12832    /// A search for a nought that runs to the end of the string answers the
12833    /// length in bits, because the string is treated as if it had noughts after
12834    /// it forever. Give it an explicit end and it answers minus one instead.
12835    #[test]
12836    fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
12837        let mut f = Fixture::new();
12838        f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
12839        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
12840        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
12841        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
12842        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
12843        assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
12844
12845        f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
12846        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
12847        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
12848        assert_eq!(
12849            f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
12850            ":8\r\n"
12851        );
12852
12853        // A missing key is all noughts, so a one is never found and a nought is
12854        // at position zero.
12855        assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
12856        assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
12857    }
12858
12859    /// The eight operations, with the answers a real server gives for them.
12860    #[test]
12861    fn the_eight_combinations_write_what_a_real_server_writes() {
12862        let mut f = Fixture::new();
12863        f.run(&[b"SET", b"a", b"abc"]);
12864        f.run(&[b"SET", b"b", b"abd"]);
12865        let cases: &[(&[u8], &str)] = &[
12866            (b"AND", "ab`"),
12867            (b"OR", "abg"),
12868            (b"XOR", "\u{0}\u{0}\u{7}"),
12869            (b"DIFF", "\u{0}\u{0}\u{3}"),
12870            (b"DIFF1", "\u{0}\u{0}\u{4}"),
12871            (b"ANDOR", "ab`"),
12872            (b"ONE", "\u{0}\u{0}\u{7}"),
12873        ];
12874        for (op, want) in cases {
12875            assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
12876            assert_eq!(
12877                f.run(&[b"GET", b"d"]),
12878                format!("$3\r\n{want}\r\n"),
12879                "{op:?}"
12880            );
12881        }
12882        // The one whose answer is not text, so it is compared as bytes.
12883        assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
12884        assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
12885
12886        // A missing source is a string of noughts as long as it needs to be, so
12887        // an AND against one writes three zero bytes rather than nothing.
12888        assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
12889        assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
12890
12891        // Every source missing is an empty result, and an empty result takes
12892        // the destination with it.
12893        f.run(&[b"SET", b"dest", b"x"]);
12894        assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
12895        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
12896    }
12897
12898    /// What `BITOP` says when it is asked for something it cannot do.
12899    #[test]
12900    fn bitop_names_the_operation_in_its_own_complaints() {
12901        let mut f = Fixture::new();
12902        f.run(&[b"SET", b"a", b"abc"]);
12903        assert_eq!(
12904            f.run(&[b"BITOP", b"nope", b"d", b"a"]),
12905            "-ERR syntax error\r\n"
12906        );
12907        assert_eq!(
12908            f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
12909            "-ERR BITOP NOT must be called with a single source key.\r\n"
12910        );
12911        for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
12912            assert_eq!(
12913                f.run(&[b"BITOP", op, b"d", b"a"]),
12914                format!(
12915                    "-ERR BITOP {} must be called with at least two source keys.\r\n",
12916                    String::from_utf8_lossy(op)
12917                )
12918            );
12919        }
12920        f.run(&[b"LPUSH", b"l", b"x"]);
12921        assert_eq!(
12922            f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
12923            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12924        );
12925    }
12926
12927    /// Packed fields, the three overflow policies and the `#` offset.
12928    #[test]
12929    fn bitfield_reads_and_writes_packed_fields() {
12930        let mut f = Fixture::new();
12931        assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
12932        assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
12933
12934        assert_eq!(
12935            f.run(&[
12936                b"BITFIELD",
12937                b"bf",
12938                b"INCRBY",
12939                b"u2",
12940                b"100",
12941                b"1",
12942                b"GET",
12943                b"u4",
12944                b"0"
12945            ]),
12946            "*2\r\n:1\r\n:0\r\n"
12947        );
12948        // The field at bit 100 is two bits wide, so it ends in the thirteenth
12949        // byte and the value grew to thirteen bytes to hold it.
12950        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
12951
12952        // A `#` offset counts in fields rather than in bits.
12953        assert_eq!(
12954            f.run(&[
12955                b"BITFIELD",
12956                b"bf",
12957                b"SET",
12958                b"u8",
12959                b"#0",
12960                b"255",
12961                b"GET",
12962                b"u8",
12963                b"#0"
12964            ]),
12965            "*2\r\n:0\r\n:255\r\n"
12966        );
12967
12968        assert_eq!(
12969            f.run(&[
12970                b"BITFIELD",
12971                b"bf",
12972                b"OVERFLOW",
12973                b"SAT",
12974                b"INCRBY",
12975                b"i8",
12976                b"0",
12977                b"120",
12978                b"INCRBY",
12979                b"i8",
12980                b"0",
12981                b"120"
12982            ]),
12983            "*2\r\n:119\r\n:127\r\n"
12984        );
12985        assert_eq!(
12986            f.run(&[
12987                b"BITFIELD",
12988                b"bf2",
12989                b"OVERFLOW",
12990                b"FAIL",
12991                b"INCRBY",
12992                b"u2",
12993                b"0",
12994                b"5"
12995            ]),
12996            "*1\r\n$-1\r\n"
12997        );
12998        assert_eq!(
12999            f.run(&[
13000                b"BITFIELD",
13001                b"bf3",
13002                b"OVERFLOW",
13003                b"WRAP",
13004                b"INCRBY",
13005                b"u2",
13006                b"0",
13007                b"5"
13008            ]),
13009            "*1\r\n:1\r\n"
13010        );
13011        assert_eq!(
13012            f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
13013            "*1\r\n:4611686018427387904\r\n"
13014        );
13015    }
13016
13017    /// A bad subcommand anywhere in the line stops all of it.
13018    ///
13019    /// Redis checks the whole argument list before it runs any of it, so the
13020    /// `SET` in front of the bad type here never happens and the key it would
13021    /// have created is not there afterwards.
13022    #[test]
13023    fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
13024        let mut f = Fixture::new();
13025        let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
13026        assert_eq!(
13027            f.run(&[
13028                b"BITFIELD",
13029                b"bad",
13030                b"SET",
13031                b"u8",
13032                b"0",
13033                b"1",
13034                b"GET",
13035                b"u99",
13036                b"0"
13037            ]),
13038            bad_type
13039        );
13040        assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
13041        assert_eq!(
13042            f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
13043            bad_type
13044        );
13045        assert_eq!(
13046            f.run(&[b"BITFIELD", b"bad", b"GET"]),
13047            "-ERR syntax error\r\n"
13048        );
13049        assert_eq!(
13050            f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
13051            "-ERR syntax error\r\n"
13052        );
13053        assert_eq!(
13054            f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
13055            "-ERR syntax error\r\n"
13056        );
13057        assert_eq!(
13058            f.run(&[
13059                b"BITFIELD",
13060                b"bad",
13061                b"OVERFLOW",
13062                b"NOPE",
13063                b"GET",
13064                b"u8",
13065                b"0"
13066            ]),
13067            "-ERR Invalid OVERFLOW type specified\r\n"
13068        );
13069        assert_eq!(
13070            f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
13071            "-ERR value is not an integer or out of range\r\n"
13072        );
13073        for at in [&b"#-1"[..], b"abc"] {
13074            assert_eq!(
13075                f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
13076                "-ERR bit offset is not an integer or out of range\r\n"
13077            );
13078        }
13079    }
13080
13081    /// The read only twin reads, refuses to write, and creates nothing.
13082    #[test]
13083    fn bitfield_ro_answers_gets_and_refuses_the_rest() {
13084        let mut f = Fixture::new();
13085        f.run(&[b"SET", b"n", b"123"]);
13086        assert_eq!(
13087            f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
13088            "*1\r\n:49\r\n"
13089        );
13090        // A read does not unpack an int the way a write does.
13091        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
13092
13093        // An OVERFLOW word is allowed even though nothing here can overflow.
13094        assert_eq!(
13095            f.run(&[
13096                b"BITFIELD_RO",
13097                b"n",
13098                b"OVERFLOW",
13099                b"SAT",
13100                b"GET",
13101                b"u8",
13102                b"0"
13103            ]),
13104            "*1\r\n:49\r\n"
13105        );
13106        for sub in [&b"SET"[..], b"INCRBY"] {
13107            assert_eq!(
13108                f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
13109                "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
13110            );
13111        }
13112
13113        assert_eq!(
13114            f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
13115            "*1\r\n:0\r\n"
13116        );
13117        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13118    }
13119
13120    /// The offsets a bitmap command will not take.
13121    #[test]
13122    fn an_offset_off_the_end_of_the_world_is_refused() {
13123        let mut f = Fixture::new();
13124        let bad = "-ERR bit offset is not an integer or out of range\r\n";
13125        for arg in [&b"abc"[..], b"-1", b"4294967296"] {
13126            assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
13127            assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
13128        }
13129        for arg in [&b"2"[..], b"-1"] {
13130            assert_eq!(
13131                f.run(&[b"BITPOS", b"k", arg]),
13132                "-ERR The bit argument must be 1 or 0.\r\n"
13133            );
13134        }
13135        assert_eq!(
13136            f.run(&[b"BITPOS", b"k", b"abc"]),
13137            "-ERR value is not an integer or out of range\r\n"
13138        );
13139        assert_eq!(
13140            f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
13141            "-ERR value is not an integer or out of range\r\n"
13142        );
13143        let bad_bit = "-ERR bit is not an integer or out of range\r\n";
13144        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
13145        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
13146    }
13147
13148    /// Every one of the seven refuses a key that is not a string.
13149    #[test]
13150    fn every_bitmap_command_says_wrongtype() {
13151        let mut f = Fixture::new();
13152        f.run(&[b"LPUSH", b"l", b"x"]);
13153        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
13154        let cases: &[&[&[u8]]] = &[
13155            &[b"SETBIT", b"l", b"0", b"1"],
13156            &[b"GETBIT", b"l", b"0"],
13157            &[b"BITCOUNT", b"l"],
13158            &[b"BITPOS", b"l", b"1"],
13159            &[b"BITOP", b"AND", b"d", b"l"],
13160            &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
13161            &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
13162        ];
13163        for case in cases {
13164            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
13165        }
13166    }
13167
13168    // --------------------------------------------------------- hyperloglogs
13169
13170    #[test]
13171    fn a_sketch_is_added_to_and_counted() {
13172        let mut f = Fixture::new();
13173        // Creating the key counts as a change, even with nothing to add.
13174        assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
13175        assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
13176        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
13177        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
13178        // And it is a string, which is not an implementation detail: a client
13179        // can `GET` a sketch out of one server and `SET` it into another.
13180        assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
13181        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
13182
13183        assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
13184        assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
13185        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
13186    }
13187
13188    #[test]
13189    fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
13190        let mut f = Fixture::new();
13191        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
13192        // Not text, so it is compared as bytes.
13193        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";
13194        let mut reply = b"$27\r\n".to_vec();
13195        reply.extend_from_slice(want);
13196        reply.extend_from_slice(b"\r\n");
13197        assert_eq!(f.raw(&[b"GET", b"h"]), reply);
13198    }
13199
13200    #[test]
13201    fn counting_several_keys_counts_their_union() {
13202        let mut f = Fixture::new();
13203        f.run(&[b"PFADD", b"a", b"x", b"y"]);
13204        f.run(&[b"PFADD", b"b", b"y", b"z"]);
13205        assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
13206        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
13207        // A key that is not there is an empty sketch, not an error and not
13208        // something that gets created by being counted.
13209        assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
13210        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
13211        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13212    }
13213
13214    #[test]
13215    fn a_merge_keeps_what_the_destination_had() {
13216        let mut f = Fixture::new();
13217        f.run(&[b"PFADD", b"a", b"x", b"y"]);
13218        f.run(&[b"PFADD", b"b", b"z"]);
13219        assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
13220        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
13221        // The destination is one of the sources, so a second merge adds to it.
13222        f.run(&[b"PFADD", b"c", b"w"]);
13223        assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
13224        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
13225        // And with no sources it is a no-op that still answers OK and still
13226        // creates a destination that was not there.
13227        assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
13228        assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
13229    }
13230
13231    /// Not under Miri, and not for the number of commands: a dense sketch is
13232    /// sixteen thousand three hundred and eighty four registers and every
13233    /// command here walks all of them, so one `PFCOUNT` is more interpreted
13234    /// work than a hundred ordinary tests. The registers and the walking are in
13235    /// `yo-kv`, where fifteen tests of their own cover both encodings and where
13236    /// the interpreter does run over them. What is left here is the dispatch
13237    /// around it, which is the same dispatch every other command in this file
13238    /// goes through.
13239    #[cfg_attr(miri, ignore = "sixteen thousand registers a command")]
13240    #[test]
13241    fn the_debug_forms_answer_four_different_shapes() {
13242        let mut f = Fixture::new();
13243        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
13244        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
13245        assert_eq!(
13246            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
13247            "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
13248        );
13249        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
13250        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
13251        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
13252        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
13253        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
13254        // A dense sketch has no opcodes left to print.
13255        assert_eq!(
13256            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
13257            "-ERR HLL encoding is not sparse\r\n"
13258        );
13259
13260        // All 16384 registers, of which three are not nought.
13261        let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
13262        assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
13263        assert_eq!(reply.matches(":0\r\n").count(), 16381);
13264        assert_eq!(reply.matches(":1\r\n").count(), 2);
13265        assert_eq!(reply.matches(":2\r\n").count(), 1);
13266
13267        assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
13268    }
13269
13270    #[test]
13271    fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
13272        let mut f = Fixture::new();
13273        f.run(&[b"SET", b"plain", b"not a sketch"]);
13274        let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
13275        assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
13276        assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
13277        assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
13278        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
13279
13280        // A key that is not a string at all gets the ordinary sentence, and a
13281        // destination that would have been written is not created.
13282        f.run(&[b"RPUSH", b"l", b"x"]);
13283        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
13284        assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
13285        assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
13286        assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
13287        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
13288        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
13289    }
13290
13291    #[test]
13292    fn pfdebug_has_its_own_complaints() {
13293        let mut f = Fixture::new();
13294        f.run(&[b"PFADD", b"h", b"a"]);
13295        // The word is quoted exactly as the client spelled it, and this is not
13296        // the "Try X HELP." sentence every other container command uses.
13297        assert_eq!(
13298            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
13299            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
13300        );
13301        // Where all three of the real commands take a missing key as empty.
13302        let gone = "-ERR The specified key does not exist\r\n";
13303        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
13304        assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
13305        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
13306        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
13307        assert_eq!(
13308            f.run(&[b"PFDEBUG"]),
13309            "-ERR wrong number of arguments for 'pfdebug' command\r\n"
13310        );
13311        assert_eq!(
13312            f.run(&[b"PFSELFTEST", b"x"]),
13313            "-ERR wrong number of arguments for 'pfselftest' command\r\n"
13314        );
13315    }
13316
13317    #[test]
13318    fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
13319        let mut f = Fixture::new();
13320        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
13321        // The sketch with its last byte cut off, which is still a header and a
13322        // magic and is a run length encoding that stops short of register 16384.
13323        let reply = f.raw(&[b"GET", b"h"]);
13324        let short = reply[5..reply.len() - 3].to_vec();
13325        f.run(&[b"SET", b"h", &short]);
13326        assert_eq!(
13327            f.run(&[b"PFCOUNT", b"h"]),
13328            "-INVALIDOBJ Corrupted HLL object detected\r\n"
13329        );
13330    }
13331
13332    #[test]
13333    fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
13334        let mut f = Fixture::new();
13335        // One that stays sparse and one that has gone dense, since the payload
13336        // carries the bytes and the two encodings are different lengths.
13337        f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
13338        // Ten thousand elements is what takes a sketch dense on its own, and it
13339        // is ten thousand trips through dispatch, which is what Miri charges
13340        // for. There the same sketch is taken across by hand. What this test is
13341        // about is a dense payload surviving a round trip and the encoding is
13342        // dense either way: that a sketch converts when it fills up is what
13343        // `the_debug_forms_answer_four_different_shapes` is for.
13344        if cfg!(miri) {
13345            f.run(&[b"PFADD", b"big", b"a", b"b", b"c"]);
13346            f.run(&[b"PFDEBUG", b"TODENSE", b"big"]);
13347        } else {
13348            for i in 0..10_000u32 {
13349                let ele = format!("e{i}");
13350                f.run(&[b"PFADD", b"big", ele.as_bytes()]);
13351            }
13352        }
13353        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
13354        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
13355
13356        for key in [&b"small"[..], b"big"] {
13357            let mut copy = key.to_vec();
13358            copy.push(b'2');
13359            let bytes = payload(&f.raw(&[b"DUMP", key]));
13360            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
13361            // The bytes, the encoding and the estimate all come back, which is
13362            // the whole of what byte compatibility across a round trip means.
13363            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
13364            assert_eq!(
13365                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
13366                f.run(&[b"PFDEBUG", b"ENCODING", key])
13367            );
13368            assert_eq!(f.run(&[b"PFCOUNT", &copy]), f.run(&[b"PFCOUNT", key]));
13369        }
13370        assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
13371        assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
13372    }
13373
13374    /// One RESP2 bulk string. The JSON replies are almost all one of these and
13375    /// the text inside them has quotes in it, so writing the frame out by hand
13376    /// buries the part of the assertion that matters.
13377    fn bulk(s: &str) -> String {
13378        format!("${}\r\n{s}\r\n", s.len())
13379    }
13380
13381    /// A RESP2 array of bulk strings, which is what most of the list replies
13382    /// are and what writing them out by hand in every assertion looks like.
13383    fn bulks(parts: &[&str]) -> String {
13384        let mut s = format!("*{}\r\n", parts.len());
13385        for p in parts {
13386            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
13387        }
13388        s
13389    }
13390
13391    #[test]
13392    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
13393        let mut f = Fixture::new();
13394        // Each element in turn goes at the head, so the last one sent is at the
13395        // front when it is over. That reads like a bug in the client and it is
13396        // what every Redis has always done.
13397        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
13398        assert_eq!(
13399            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13400            bulks(&["c", "b", "a"])
13401        );
13402        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
13403        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
13404        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
13405        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
13406        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
13407        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
13408    }
13409
13410    #[test]
13411    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
13412        let mut f = Fixture::new();
13413        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
13414        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
13415        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
13416        f.run(&[b"RPUSH", b"k", b"a"]);
13417        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
13418        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
13419        assert_eq!(
13420            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13421            bulks(&["z", "a", "y"])
13422        );
13423    }
13424
13425    /// The four ways a pop can come back with nothing, which are three
13426    /// different replies and a RESP2 client can tell all of them apart.
13427    #[test]
13428    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
13429        let mut f = Fixture::new();
13430        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
13431        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
13432        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
13433        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
13434        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
13435        // A count of zero against a list that is there is an empty array and
13436        // not a null array, which is the fourth answer.
13437        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
13438        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
13439        // More than there is takes what there is and the key goes with it.
13440        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
13441        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
13442    }
13443
13444    #[test]
13445    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
13446        let mut f = Fixture::new();
13447        f.run(&[b"RPUSH", b"k", b"a"]);
13448        let range = "-ERR value is out of range, must be positive\r\n";
13449        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
13450        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
13451        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
13452        // Redis calls this an arity error and not a syntax error, which is a
13453        // distinction it does not always make.
13454        assert_eq!(
13455            f.run(&[b"LPOP", b"k", b"1", b"2"]),
13456            "-ERR wrong number of arguments for 'lpop' command\r\n"
13457        );
13458        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
13459    }
13460
13461    #[test]
13462    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
13463        let mut f = Fixture::new();
13464        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
13465        assert_eq!(
13466            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13467            bulks(&["a", "b", "c"])
13468        );
13469        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
13470        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
13471        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
13472        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
13473        assert_eq!(
13474            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
13475            bulks(&["a", "b", "c"])
13476        );
13477        // A key that is not there is an empty range and not a nil, which is the
13478        // one place a list disagrees with a set.
13479        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
13480        assert_eq!(
13481            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
13482            "-ERR value is not an integer or out of range\r\n"
13483        );
13484    }
13485
13486    #[test]
13487    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
13488        let mut f = Fixture::new();
13489        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
13490        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
13491        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
13492        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
13493        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
13494        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
13495        assert_eq!(
13496            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13497            bulks(&["a", "b", "z"])
13498        );
13499        // Both ways of missing are errors here rather than a nil, because a
13500        // list is never empty and there is nothing else the reply could be.
13501        assert_eq!(
13502            f.run(&[b"LSET", b"k", b"99", b"z"]),
13503            "-ERR index out of range\r\n"
13504        );
13505        assert_eq!(
13506            f.run(&[b"LSET", b"nope", b"0", b"z"]),
13507            "-ERR no such key\r\n"
13508        );
13509    }
13510
13511    #[test]
13512    fn linsert_says_three_things_with_one_signed_number() {
13513        let mut f = Fixture::new();
13514        // Zero for a key that is not there, which is not the same as minus one
13515        // for a pivot that is not in a list that is.
13516        assert_eq!(
13517            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
13518            ":0\r\n"
13519        );
13520        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
13521        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
13522        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
13523        assert_eq!(
13524            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13525            bulks(&["X", "a", "b", "Y"])
13526        );
13527        assert_eq!(
13528            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
13529            ":-1\r\n"
13530        );
13531        assert_eq!(
13532            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
13533            "-ERR syntax error\r\n"
13534        );
13535    }
13536
13537    #[test]
13538    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
13539        let mut f = Fixture::new();
13540        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
13541        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
13542        assert_eq!(
13543            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
13544            bulks(&["b", "c", "a"])
13545        );
13546        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
13547        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
13548        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
13549        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
13550        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
13551        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
13552    }
13553
13554    #[test]
13555    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
13556        let mut f = Fixture::new();
13557        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
13558        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
13559        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
13560        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
13561        // leave `EXISTS` answering zero rather than leaving an empty one.
13562        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
13563        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
13564        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
13565    }
13566
13567    #[test]
13568    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
13569        let mut f = Fixture::new();
13570        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
13571        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
13572        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
13573        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
13574        assert_eq!(
13575            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
13576            "*2\r\n:0\r\n:3\r\n"
13577        );
13578        assert_eq!(
13579            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
13580            "*3\r\n:6\r\n:3\r\n:0\r\n"
13581        );
13582        // MAXLEN counts elements looked at and not matches found, so three
13583        // stops after `a b c` and finds the one match in it.
13584        assert_eq!(
13585            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
13586            "*1\r\n:0\r\n"
13587        );
13588        // Nothing found is three different replies depending on how it was
13589        // asked and whether the key is there at all.
13590        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
13591        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
13592        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
13593        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
13594    }
13595
13596    #[test]
13597    fn lpos_words_its_three_mistakes_the_way_redis_does() {
13598        let mut f = Fixture::new();
13599        f.run(&[b"RPUSH", b"p", b"a"]);
13600        // The whole sentence and not a prefix, because the older wording of it
13601        // is still all over the internet and clients match on the text.
13602        assert_eq!(
13603            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
13604            "-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"
13605        );
13606        assert_eq!(
13607            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
13608            "-ERR COUNT can't be negative\r\n"
13609        );
13610        assert_eq!(
13611            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
13612            "-ERR MAXLEN can't be negative\r\n"
13613        );
13614        assert_eq!(
13615            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
13616            "-ERR syntax error\r\n"
13617        );
13618        assert_eq!(
13619            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
13620            "-ERR syntax error\r\n"
13621        );
13622    }
13623
13624    #[test]
13625    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
13626        let mut f = Fixture::new();
13627        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
13628        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
13629        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
13630        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
13631        assert_eq!(
13632            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
13633            "$1\r\na\r\n"
13634        );
13635        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
13636        // The same key twice is the documented way to rotate a list and falls
13637        // out of taking the element before deciding where to put it.
13638        f.run(&[b"DEL", b"r"]);
13639        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
13640        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
13641        assert_eq!(
13642            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
13643            bulks(&["3", "1", "2"])
13644        );
13645        assert_eq!(
13646            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
13647            "$-1\r\n"
13648        );
13649        assert_eq!(
13650            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
13651            "-ERR syntax error\r\n"
13652        );
13653    }
13654
13655    #[test]
13656    fn a_move_checks_the_destination_before_it_takes_anything() {
13657        let mut f = Fixture::new();
13658        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
13659        f.run(&[b"SET", b"str", b"v"]);
13660        assert_eq!(
13661            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
13662            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
13663        );
13664        // The element is still where it was, rather than having gone nowhere.
13665        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
13666    }
13667
13668    #[test]
13669    fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
13670        // OBO is what you get from sending LMOVE that many times, BULK keeps
13671        // the source order. The two only differ when both ends are the same,
13672        // which is the whole reason the word exists.
13673        for (from, to, order, want) in [
13674            ("LEFT", "RIGHT", "OBO", ["a", "b"]),
13675            ("LEFT", "RIGHT", "BULK", ["a", "b"]),
13676            ("LEFT", "LEFT", "OBO", ["b", "a"]),
13677            ("LEFT", "LEFT", "BULK", ["a", "b"]),
13678            ("RIGHT", "LEFT", "OBO", ["d", "e"]),
13679            ("RIGHT", "LEFT", "BULK", ["d", "e"]),
13680            ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
13681            ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
13682        ] {
13683            let mut f = Fixture::new();
13684            f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
13685            let how = format!("{from} {to} {order}");
13686            let reply = f.run(&[
13687                b"LMOVEM",
13688                b"s",
13689                b"d",
13690                from.as_bytes(),
13691                to.as_bytes(),
13692                b"COUNT",
13693                b"2",
13694                order.as_bytes(),
13695            ]);
13696            assert_eq!(reply, bulks(&want), "the reply for {how}");
13697            assert_eq!(
13698                f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
13699                bulks(&want),
13700                "the destination for {how}"
13701            );
13702        }
13703    }
13704
13705    #[test]
13706    fn a_block_move_of_one_needs_no_count_at_all() {
13707        let mut f = Fixture::new();
13708        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
13709        assert_eq!(
13710            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
13711            bulks(&["a"])
13712        );
13713        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
13714        // Six and seven arguments are neither of the two forms, so the
13715        // reference calls both of them a syntax error rather than guessing.
13716        assert_eq!(
13717            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
13718            "-ERR syntax error\r\n"
13719        );
13720        assert_eq!(
13721            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
13722            "-ERR syntax error\r\n"
13723        );
13724    }
13725
13726    #[test]
13727    fn a_block_move_with_exactly_takes_all_of_them_or_none() {
13728        let mut f = Fixture::new();
13729        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
13730        // A null array and not a null bulk string, which `redis-cli` prints as
13731        // `(nil)` either way and only the raw wire tells apart. What it would
13732        // have sent is an array, so its nothing is an array's nothing.
13733        assert_eq!(
13734            f.run(&[
13735                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
13736            ]),
13737            "*-1\r\n"
13738        );
13739        assert_eq!(
13740            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
13741            bulks(&["a", "b", "c"])
13742        );
13743        // COUNT takes what there is, and an emptied source goes away.
13744        assert_eq!(
13745            f.run(&[
13746                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
13747            ]),
13748            bulks(&["a", "b", "c"])
13749        );
13750        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
13751        assert_eq!(
13752            f.run(&[
13753                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
13754            ]),
13755            "*-1\r\n"
13756        );
13757    }
13758
13759    #[test]
13760    fn a_block_move_onto_itself_rotates_by_the_count() {
13761        let mut f = Fixture::new();
13762        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
13763        assert_eq!(
13764            f.run(&[
13765                b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
13766            ]),
13767            bulks(&["a", "b"])
13768        );
13769        assert_eq!(
13770            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
13771            bulks(&["c", "a", "b"])
13772        );
13773    }
13774
13775    #[test]
13776    fn a_block_move_reads_the_count_before_the_ordering_word() {
13777        let mut f = Fixture::new();
13778        f.run(&[b"RPUSH", b"s", b"a", b"b"]);
13779        f.run(&[b"SET", b"str", b"v"]);
13780        let count = "-ERR count should be greater than 0\r\n";
13781        assert_eq!(
13782            f.run(&[
13783                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
13784            ]),
13785            count
13786        );
13787        assert_eq!(
13788            f.run(&[
13789                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
13790            ]),
13791            count
13792        );
13793        assert_eq!(
13794            f.run(&[
13795                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
13796            ]),
13797            "-ERR syntax error\r\n"
13798        );
13799        assert_eq!(
13800            f.run(&[
13801                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
13802            ]),
13803            "-ERR syntax error\r\n"
13804        );
13805        // Every argument is read before the keys are looked at, so a bad count
13806        // beats a wrong type even when the type is wrong on the source.
13807        assert_eq!(
13808            f.run(&[
13809                b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
13810            ]),
13811            count
13812        );
13813        assert_eq!(
13814            f.run(&[
13815                b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
13816            ]),
13817            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
13818        );
13819        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
13820    }
13821
13822    #[test]
13823    fn lmpop_answers_from_the_first_key_that_has_anything() {
13824        let mut f = Fixture::new();
13825        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
13826        // The name of the key that answered comes back with the elements,
13827        // because the client cannot work out which one it was.
13828        assert_eq!(
13829            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
13830            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
13831        );
13832        assert_eq!(
13833            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
13834            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
13835        );
13836        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
13837        // A null array and not a null, even though what it stands in for is an
13838        // array holding a key name and then another array.
13839        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
13840    }
13841
13842    #[test]
13843    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
13844        let mut f = Fixture::new();
13845        f.run(&[b"RPUSH", b"k", b"a"]);
13846        assert_eq!(
13847            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
13848            "-ERR numkeys should be greater than 0\r\n"
13849        );
13850        assert_eq!(
13851            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
13852            "-ERR numkeys should be greater than 0\r\n"
13853        );
13854        assert_eq!(
13855            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
13856            "-ERR count should be greater than 0\r\n"
13857        );
13858        // A key count that eats the direction is a syntax error and not a
13859        // sentence about key counts, because the direction is simply not there.
13860        assert_eq!(
13861            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
13862            "-ERR syntax error\r\n"
13863        );
13864        assert_eq!(
13865            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
13866            "-ERR syntax error\r\n"
13867        );
13868        assert_eq!(
13869            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
13870            "-ERR syntax error\r\n"
13871        );
13872        assert_eq!(
13873            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
13874            "-ERR syntax error\r\n"
13875        );
13876        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
13877    }
13878
13879    #[test]
13880    fn every_list_command_says_wrongtype_and_writes_nothing() {
13881        let mut f = Fixture::new();
13882        f.run(&[b"SET", b"str", b"v"]);
13883        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
13884        for cmd in [
13885            &[b"LPUSH".as_slice(), b"str", b"a"][..],
13886            &[b"RPUSH", b"str", b"a"],
13887            &[b"LPUSHX", b"str", b"a"],
13888            &[b"RPUSHX", b"str", b"a"],
13889            &[b"LPOP", b"str"],
13890            &[b"LPOP", b"str", b"2"],
13891            &[b"RPOP", b"str"],
13892            &[b"LLEN", b"str"],
13893            &[b"LRANGE", b"str", b"0", b"-1"],
13894            &[b"LINDEX", b"str", b"0"],
13895            &[b"LSET", b"str", b"0", b"a"],
13896            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
13897            &[b"LREM", b"str", b"0", b"a"],
13898            &[b"LTRIM", b"str", b"0", b"-1"],
13899            &[b"LPOS", b"str", b"a"],
13900            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
13901            &[b"RPOPLPUSH", b"str", b"d"],
13902            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
13903            &[b"LMPOP", b"1", b"str", b"LEFT"],
13904        ] {
13905            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
13906        }
13907        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
13908        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
13909    }
13910
13911    /// A timeout is not an integer and it is not an ordinary float either: the
13912    /// three sentences it can answer with are its own, and which one a given
13913    /// argument gets is not what reading the code would suggest.
13914    #[test]
13915    fn a_timeout_has_three_ways_of_being_wrong() {
13916        let mut f = Fixture::new();
13917        let not_float = "-ERR timeout is not a float or out of range\r\n";
13918        let range = "-ERR timeout is out of range\r\n";
13919        for (bad, want) in [
13920            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
13921            (&[b"BLPOP", b"k", b"nan"], not_float),
13922            (&[b"BLPOP", b"k", b""], not_float),
13923            // Whitespace on either side, which `strtold` would take and Redis
13924            // does not.
13925            (&[b"BLPOP", b"k", b" 1"], not_float),
13926            (&[b"BLPOP", b"k", b"1 "], not_float),
13927            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
13928            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
13929            // These three parse, so they are not the not-a-float error, and all
13930            // three are further off than an i64 of milliseconds reaches.
13931            (&[b"BLPOP", b"k", b"1e400"], range),
13932            (&[b"BLPOP", b"k", b"inf"], range),
13933            (&[b"BLPOP", b"k", b"9999999999999999"], range),
13934            (&[b"BRPOP", b"k", b"abc"], not_float),
13935            (
13936                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
13937                not_float,
13938            ),
13939            (
13940                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
13941                "-ERR timeout is negative\r\n",
13942            ),
13943            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
13944        ] {
13945            assert_eq!(f.run(bad), want, "for {bad:?}");
13946        }
13947    }
13948
13949    /// A timeout of exactly zero means no timeout, and there are two ways of
13950    /// writing exactly zero.
13951    #[test]
13952    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
13953        let mut f = Fixture::new();
13954        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
13955            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
13956            assert_eq!(flow, Flow::Block, "for {timeout:?}");
13957            assert!(out.is_empty(), "for {timeout:?}");
13958        }
13959        // Positive, so it is a real deadline, and the deadline is this
13960        // millisecond. Nothing is written here either: the reply comes from the
13961        // sweep, which is the engine's and not this layer's.
13962        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
13963        assert_eq!(flow, Flow::Block);
13964        assert!(out.is_empty());
13965    }
13966
13967    #[test]
13968    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
13969        let mut f = Fixture::new();
13970        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
13971
13972        // The one difference from LPOP: the reply names the key that answered,
13973        // which is what makes BLPOP over several keys usable.
13974        assert_eq!(
13975            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
13976            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
13977        );
13978        assert_eq!(
13979            f.run(&[b"BRPOP", b"L", b"0"]),
13980            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
13981        );
13982        assert_eq!(
13983            f.run(&[
13984                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
13985            ]),
13986            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
13987        );
13988        assert_eq!(
13989            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
13990            "$1\r\nd\r\n"
13991        );
13992        assert_eq!(
13993            f.run(&[b"EXISTS", b"L"]),
13994            ":0\r\n",
13995            "and the key went with it"
13996        );
13997        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
13998        // Onto itself, which is how a list is rotated and is a real thing to ask
13999        // a blocking move for.
14000        f.run(&[b"RPUSH", b"D", b"x"]);
14001        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
14002        assert_eq!(
14003            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
14004            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
14005        );
14006    }
14007
14008    #[test]
14009    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
14010        let mut f = Fixture::new();
14011        f.run(&[b"RPUSH", b"k", b"a"]);
14012        for (bad, want) in [
14013            (
14014                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
14015                "-ERR numkeys should be greater than 0\r\n",
14016            ),
14017            (
14018                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
14019                "-ERR numkeys should be greater than 0\r\n",
14020            ),
14021            // Two keys named and one given, so the word that should have been
14022            // the direction is a key and there is no direction left.
14023            (
14024                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
14025                "-ERR syntax error\r\n",
14026            ),
14027            (
14028                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
14029                "-ERR syntax error\r\n",
14030            ),
14031            (
14032                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
14033                "-ERR syntax error\r\n",
14034            ),
14035            (
14036                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
14037                "-ERR syntax error\r\n",
14038            ),
14039            // A count that is not a number at all gets the same sentence a zero
14040            // or a negative one gets, rather than the usual one about integers.
14041            (
14042                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
14043                "-ERR count should be greater than 0\r\n",
14044            ),
14045            (
14046                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
14047                "-ERR count should be greater than 0\r\n",
14048            ),
14049        ] {
14050            assert_eq!(f.run(bad), want, "for {bad:?}");
14051        }
14052        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
14053    }
14054
14055    #[test]
14056    fn a_blocking_move_reads_its_directions_before_its_timeout() {
14057        let mut f = Fixture::new();
14058        // Both are wrong. Redis checks the directions first, so this is the
14059        // syntax error and not a complaint about the timeout.
14060        assert_eq!(
14061            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
14062            "-ERR syntax error\r\n"
14063        );
14064        assert_eq!(
14065            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
14066            "-ERR syntax error\r\n"
14067        );
14068    }
14069
14070    /// `BLMOVEM` answers exactly what `LMOVEM` answers when it does not have to
14071    /// wait, which is the same relationship every other command in this file has
14072    /// with the one it wraps.
14073    #[test]
14074    fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
14075        let mut f = Fixture::new();
14076        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
14077        assert_eq!(
14078            f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
14079            (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
14080        );
14081        assert_eq!(
14082            f.run(&[
14083                b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
14084            ]),
14085            bulks(&["e", "d"])
14086        );
14087        assert_eq!(
14088            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
14089            bulks(&["a", "e", "d"])
14090        );
14091        // `EXACTLY` with enough there does not wait either.
14092        assert_eq!(
14093            f.run(&[
14094                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
14095            ]),
14096            bulks(&["b", "c"])
14097        );
14098        assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
14099    }
14100
14101    /// The one thing `BLMOVEM` decides differently from the other five: `COUNT`
14102    /// is ready as soon as there is anything and `EXACTLY` is not ready until the
14103    /// whole block has arrived.
14104    #[test]
14105    fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
14106        let mut f = Fixture::new();
14107        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
14108        // Two there and three asked for. `COUNT` takes the two.
14109        assert_eq!(
14110            f.flow(&[
14111                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
14112            ]),
14113            (Flow::Continue, bulks(&["a", "b"]))
14114        );
14115
14116        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
14117        // The same line with `EXACTLY` parks instead, and takes nothing on the
14118        // way past.
14119        assert_eq!(
14120            f.flow(&[
14121                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
14122            ])
14123            .0,
14124            Flow::Block
14125        );
14126        assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
14127    }
14128
14129    #[test]
14130    fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
14131        let mut f = Fixture::new();
14132        let syntax = "-ERR syntax error\r\n";
14133        // All three are wrong and the directions are read first.
14134        assert_eq!(
14135            f.run(&[
14136                b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
14137            ]),
14138            syntax
14139        );
14140        // Directions fine, timeout and count both wrong, so the timeout wins.
14141        assert_eq!(
14142            f.run(&[
14143                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
14144            ]),
14145            "-ERR timeout is not a float or out of range\r\n"
14146        );
14147        assert_eq!(
14148            f.run(&[
14149                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
14150            ]),
14151            "-ERR timeout is negative\r\n"
14152        );
14153        // And with the timeout fine, the count before the ordering word.
14154        assert_eq!(
14155            f.run(&[
14156                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
14157            ]),
14158            "-ERR count should be greater than 0\r\n"
14159        );
14160        assert_eq!(
14161            f.run(&[
14162                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
14163            ]),
14164            syntax
14165        );
14166        // Seven and eight arguments are neither of the two forms, the same way
14167        // six and seven are for `LMOVEM`.
14168        assert_eq!(
14169            f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
14170            syntax
14171        );
14172        assert_eq!(
14173            f.run(&[
14174                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
14175            ]),
14176            syntax
14177        );
14178    }
14179
14180    /// The four ways a blocking command sees a key of another type, and the one
14181    /// way it does not.
14182    #[test]
14183    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
14184        let mut f = Fixture::new();
14185        f.run(&[b"SET", b"S", b"v"]);
14186        f.run(&[b"RPUSH", b"D", b"x"]);
14187        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14188
14189        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
14190        // Every key is checked even when an earlier one would have blocked, so
14191        // an empty key in front of a string does not hide it.
14192        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
14193        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
14194        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
14195        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
14196        // The destination, which is only reached because the source has
14197        // something in it.
14198        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
14199        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
14200        assert_eq!(
14201            f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
14202            wrong
14203        );
14204        assert_eq!(
14205            f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
14206            wrong
14207        );
14208
14209        // And the one that does not: an empty source means the destination is
14210        // never looked at, so this waits rather than erroring, and on a real
14211        // server it times out.
14212        assert_eq!(
14213            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
14214                .0,
14215            Flow::Block
14216        );
14217        // `BLMOVEM` has a second way of not being ready, and it hides the
14218        // destination just as well: the source is a list with two elements in it
14219        // and `EXACTLY` wants three, so the string never gets looked at.
14220        assert_eq!(
14221            f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
14222                .0,
14223            Flow::Block
14224        );
14225        f.run(&[b"RPUSH", b"E", b"1", b"2"]);
14226        assert_eq!(
14227            f.flow(&[
14228                b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
14229            ])
14230            .0,
14231            Flow::Block
14232        );
14233    }
14234
14235    /// The same churn the set and the string get, because a list that leaks a
14236    /// chunk per push looks exactly like one that does not until it has run for
14237    /// an afternoon.
14238    /// Not under Miri, for the reason on `churning_sets_does_not_grow_the_server`.
14239    #[cfg_attr(miri, ignore = "the volume is the claim")]
14240    #[test]
14241    fn churning_lists_does_not_grow_the_server() {
14242        let mut f = Fixture::new();
14243        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
14244        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
14245            .into_iter()
14246            .chain(vals.iter().map(Vec::as_slice))
14247            .collect();
14248
14249        f.run(&args);
14250        f.run(&[b"DEL", b"k"]);
14251        f.server.compact_step();
14252        let after_first = f.server.memory_bytes();
14253
14254        for _ in 0..200 {
14255            f.run(&args);
14256            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
14257            f.server.compact_step();
14258        }
14259        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
14260        assert!(
14261            f.server.memory_bytes() <= after_first * 2,
14262            "held {} after two hundred passes against {after_first} after one",
14263            f.server.memory_bytes()
14264        );
14265    }
14266
14267    // ------------------------------------------------------------ sorted set
14268
14269    #[test]
14270    fn a_sorted_set_takes_scores_and_gives_them_back() {
14271        let mut f = Fixture::new();
14272        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
14273        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
14274        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
14275        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
14276        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
14277        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
14278        assert_eq!(
14279            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
14280            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
14281        );
14282        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
14283        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
14284        // The key goes when the last member does.
14285        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
14286        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
14287    }
14288
14289    #[test]
14290    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
14291        let mut f = Fixture::new();
14292        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
14293        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
14294        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
14295        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
14296
14297        f.out = Out::new(Proto::Resp3);
14298        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
14299        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
14300        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
14301        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
14302    }
14303
14304    #[test]
14305    fn the_zadd_options_gate_what_gets_written() {
14306        let mut f = Fixture::new();
14307        f.run(&[b"ZADD", b"z", b"5", b"a"]);
14308        // NX leaves a member that is there alone, XX will not create one.
14309        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
14310        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
14311        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
14312        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
14313        // GT and LT only move a score one way.
14314        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
14315        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
14316        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
14317        // CH counts a moved score and plain ZADD does not.
14318        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
14319        assert_eq!(
14320            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
14321            ":2\r\n"
14322        );
14323    }
14324
14325    #[test]
14326    fn zadd_incr_answers_a_score_or_nothing_at_all() {
14327        let mut f = Fixture::new();
14328        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
14329        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
14330        // A gate that refuses is the string nil, because the reply it stands in
14331        // for is a score.
14332        assert_eq!(
14333            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
14334            "$-1\r\n"
14335        );
14336        assert_eq!(
14337            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
14338            "$-1\r\n"
14339        );
14340        assert_eq!(
14341            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
14342            "$-1\r\n"
14343        );
14344        assert_eq!(
14345            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
14346            "$1\r\n8\r\n"
14347        );
14348        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
14349        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
14350    }
14351
14352    #[test]
14353    fn the_two_infinities_will_not_be_added_together() {
14354        let mut f = Fixture::new();
14355        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
14356        let nan = "-ERR resulting score is not a number (NaN)\r\n";
14357        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
14358        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
14359        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
14360        // And a key made for an increment that then fails does not stay behind.
14361        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
14362    }
14363
14364    #[test]
14365    fn zadd_says_its_mistakes_the_way_redis_says_them() {
14366        let mut f = Fixture::new();
14367        // The pairs are counted before the options are looked at, so this is a
14368        // syntax error about having none and not a complaint about NX and XX.
14369        assert_eq!(
14370            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
14371            "-ERR syntax error\r\n"
14372        );
14373        assert_eq!(
14374            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
14375            "-ERR XX and NX options at the same time are not compatible\r\n"
14376        );
14377        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
14378        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
14379        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
14380        assert_eq!(
14381            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
14382            "-ERR INCR option supports a single increment-element pair\r\n"
14383        );
14384        // An odd number of arguments after the options.
14385        assert_eq!(
14386            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
14387            "-ERR syntax error\r\n"
14388        );
14389        // Every score is read before the first is stored.
14390        assert_eq!(
14391            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
14392            "-ERR value is not a valid float\r\n"
14393        );
14394        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
14395    }
14396
14397    #[test]
14398    fn a_rank_says_where_a_member_sits_from_either_end() {
14399        let mut f = Fixture::new();
14400        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14401        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
14402        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
14403        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
14404        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
14405        // WITHSCORE changes both shapes: the answer and the nothing.
14406        assert_eq!(
14407            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
14408            "*2\r\n:1\r\n$1\r\n2\r\n"
14409        );
14410        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
14411        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
14412        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
14413        // A bad option is a syntax error and one argument too many is an arity
14414        // error, which is Redis's split.
14415        assert_eq!(
14416            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
14417            "-ERR syntax error\r\n"
14418        );
14419        assert_eq!(
14420            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
14421            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
14422        );
14423    }
14424
14425    #[test]
14426    fn the_two_counts_read_their_two_kinds_of_bound() {
14427        let mut f = Fixture::new();
14428        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14429        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
14430        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
14431        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
14432        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
14433        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
14434        assert_eq!(
14435            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
14436            "-ERR min or max is not a float\r\n"
14437        );
14438
14439        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
14440        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
14441        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
14442        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
14443        // A bare member is not a bound, because a member can start with any
14444        // byte and there would be no way to say the bracket if it were optional.
14445        assert_eq!(
14446            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
14447            "-ERR min or max not valid string range item\r\n"
14448        );
14449    }
14450
14451    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
14452    ///
14453    /// Every byte in here was read off a real 8.10.1 rather than worked out,
14454    /// because the interesting part of this command is not what it selects, it
14455    /// is which of the two ends the client is expected to name first.
14456    #[test]
14457    fn one_range_command_selects_by_rank_or_score_or_name() {
14458        let mut f = Fixture::new();
14459        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14460        assert_eq!(
14461            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
14462            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
14463        );
14464        assert_eq!(
14465            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
14466            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
14467        );
14468        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
14469        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
14470        // REV over ranks reverses the walk and leaves the two arguments alone,
14471        // because a rank counts from the end the walk starts at.
14472        assert_eq!(
14473            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
14474            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
14475        );
14476        assert_eq!(
14477            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
14478            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
14479        );
14480        // And REV over scores does swap them, since a bound does not count from
14481        // anywhere. This is the one line of the parse that tells the two apart.
14482        assert_eq!(
14483            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
14484            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
14485        );
14486        assert_eq!(
14487            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
14488            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
14489        );
14490        assert_eq!(
14491            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
14492            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
14493        );
14494    }
14495
14496    /// The older spellings, which are the same six windows with the mode in the
14497    /// name and the high end named first on the three that go backwards.
14498    #[test]
14499    fn the_older_range_spellings_name_their_high_end_first() {
14500        let mut f = Fixture::new();
14501        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14502        assert_eq!(
14503            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
14504            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
14505        );
14506        assert_eq!(
14507            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
14508            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
14509        );
14510        assert_eq!(
14511            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
14512            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
14513        );
14514        assert_eq!(
14515            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
14516            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
14517        );
14518        // The two arguments the wrong way round is an empty answer and not an
14519        // error, which is what the swap being in the parse rather than in the
14520        // window buys.
14521        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
14522        assert_eq!(
14523            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
14524            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
14525        );
14526        assert_eq!(
14527            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
14528            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
14529        );
14530        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
14531        // way of spelling the mode, they are a syntax error.
14532        for cmd in [
14533            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
14534            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
14535            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
14536        ] {
14537            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
14538        }
14539    }
14540
14541    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
14542    /// only some of them accept.
14543    #[test]
14544    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
14545        let mut f = Fixture::new();
14546        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14547        assert_eq!(
14548            f.run(&[
14549                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
14550            ]),
14551            "*1\r\n$1\r\nb\r\n"
14552        );
14553        // A negative offset skips past everything, a negative count is no bound.
14554        assert_eq!(
14555            f.run(&[
14556                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
14557            ]),
14558            "*0\r\n"
14559        );
14560        assert_eq!(
14561            f.run(&[
14562                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
14563            ]),
14564            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
14565        );
14566        // The two options in either order, which falls out of the parse loop.
14567        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";
14568        assert_eq!(
14569            f.run(&[
14570                b"ZRANGEBYSCORE",
14571                b"z",
14572                b"1",
14573                b"3",
14574                b"WITHSCORES",
14575                b"LIMIT",
14576                b"0",
14577                b"2"
14578            ]),
14579            both
14580        );
14581        assert_eq!(
14582            f.run(&[
14583                b"ZRANGEBYSCORE",
14584                b"z",
14585                b"1",
14586                b"3",
14587                b"LIMIT",
14588                b"0",
14589                b"2",
14590                b"WITHSCORES"
14591            ]),
14592            both
14593        );
14594        // LIMIT on a range by rank is refused after the whole option list has
14595        // been read, so this complains about LIMIT and not about WITHSCORES.
14596        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
14597        assert_eq!(
14598            f.run(&[
14599                b"ZREVRANGE",
14600                b"z",
14601                b"0",
14602                b"-1",
14603                b"WITHSCORES",
14604                b"LIMIT",
14605                b"0",
14606                b"1"
14607            ]),
14608            needs_by
14609        );
14610        assert_eq!(
14611            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
14612            needs_by
14613        );
14614        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
14615        assert_eq!(
14616            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
14617            not_bylex
14618        );
14619        assert_eq!(
14620            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
14621            not_bylex
14622        );
14623        // Two modes at once, an option nobody knows, a LIMIT missing its count,
14624        // and the three number errors, which are three different sentences.
14625        for cmd in [
14626            &[
14627                b"ZRANGE".as_slice(),
14628                b"z",
14629                b"0",
14630                b"-1",
14631                b"BYSCORE",
14632                b"BYLEX",
14633            ][..],
14634            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
14635            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
14636        ] {
14637            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
14638        }
14639        assert_eq!(
14640            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
14641            "-ERR min or max is not a float\r\n"
14642        );
14643        assert_eq!(
14644            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
14645            "-ERR min or max not valid string range item\r\n"
14646        );
14647        assert_eq!(
14648            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
14649            "-ERR value is not an integer or out of range\r\n"
14650        );
14651    }
14652
14653    /// `WITHSCORES` is the one place in this group where the two protocols
14654    /// disagree about the shape of the reply and not just the type of a value.
14655    #[test]
14656    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
14657        let mut f = Fixture::new();
14658        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14659        assert_eq!(
14660            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
14661            "*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"
14662        );
14663        f.out = Out::new(Proto::Resp3);
14664        assert_eq!(
14665            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
14666            "*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"
14667        );
14668        assert_eq!(
14669            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
14670            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
14671        );
14672    }
14673
14674    /// The store form, which is the same parse with the destination in front.
14675    #[test]
14676    fn a_range_store_writes_the_window_into_another_key() {
14677        let mut f = Fixture::new();
14678        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14679        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
14680        // A window that selects nothing deletes the destination rather than
14681        // leaving an empty sorted set, because an empty one does not exist.
14682        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
14683        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
14684        assert_eq!(
14685            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
14686            ":2\r\n"
14687        );
14688        assert_eq!(
14689            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
14690            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
14691        );
14692        // The destination is allowed to be the source, because the result is
14693        // built whole before anything is written over.
14694        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
14695        assert_eq!(
14696            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
14697            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
14698        );
14699        // It takes every option ZRANGE takes except WITHSCORES, which is a
14700        // plain syntax error here and not the sentence about BYLEX.
14701        assert_eq!(
14702            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
14703            "-ERR syntax error\r\n"
14704        );
14705    }
14706
14707    /// The three removals, which are the read side's window with the walk
14708    /// turned into a removal and no options at all.
14709    #[test]
14710    fn the_three_removals_share_their_window_with_the_reads() {
14711        let mut f = Fixture::new();
14712        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14713        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
14714        assert_eq!(
14715            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
14716            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
14717        );
14718        assert_eq!(
14719            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
14720            ":1\r\n"
14721        );
14722        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
14723        // The last member going takes the key with it.
14724        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
14725        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
14726        assert_eq!(
14727            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
14728            ":0\r\n"
14729        );
14730        assert_eq!(
14731            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
14732            "-ERR value is not an integer or out of range\r\n"
14733        );
14734    }
14735
14736    /// The algebra, which is one gather and three names for it.
14737    #[test]
14738    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
14739        let mut f = Fixture::new();
14740        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14741        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
14742        assert_eq!(
14743            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
14744            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
14745        );
14746        // The scores are added where a member is in both, and the answer comes
14747        // out in the order those combined scores put it in.
14748        assert_eq!(
14749            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
14750            "*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"
14751        );
14752        assert_eq!(
14753            f.run(&[
14754                b"ZUNION",
14755                b"2",
14756                b"z",
14757                b"y",
14758                b"WEIGHTS",
14759                b"2",
14760                b"3",
14761                b"WITHSCORES"
14762            ]),
14763            "*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"
14764        );
14765        assert_eq!(
14766            f.run(&[
14767                b"ZUNION",
14768                b"2",
14769                b"z",
14770                b"y",
14771                b"AGGREGATE",
14772                b"MIN",
14773                b"WITHSCORES"
14774            ]),
14775            "*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"
14776        );
14777        assert_eq!(
14778            f.run(&[
14779                b"ZUNION",
14780                b"2",
14781                b"z",
14782                b"y",
14783                b"AGGREGATE",
14784                b"MAX",
14785                b"WITHSCORES"
14786            ]),
14787            "*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"
14788        );
14789        assert_eq!(
14790            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
14791            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
14792        );
14793        assert_eq!(
14794            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
14795            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
14796        );
14797        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
14798        // A plain set is an input, and it behaves as a sorted set in which
14799        // every member scores one.
14800        f.run(&[b"SADD", b"p", b"a", b"d"]);
14801        assert_eq!(
14802            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
14803            "*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"
14804        );
14805        // A difference never combines two scores, so it has nothing for either
14806        // of the two options to do and refuses both.
14807        for cmd in [
14808            &[
14809                b"ZDIFF".as_slice(),
14810                b"2",
14811                b"z",
14812                b"y",
14813                b"WEIGHTS",
14814                b"1",
14815                b"1",
14816            ][..],
14817            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
14818        ] {
14819            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
14820        }
14821    }
14822
14823    /// The count of keys, which is what lets a key be named `WEIGHTS`.
14824    #[test]
14825    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
14826        let mut f = Fixture::new();
14827        f.run(&[b"ZADD", b"z", b"1", b"a"]);
14828        f.run(&[b"ZADD", b"y", b"2", b"b"]);
14829        // Redis names the command in this one, so each spelling says its own.
14830        assert_eq!(
14831            f.run(&[b"ZUNION", b"0", b"z"]),
14832            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
14833        );
14834        assert_eq!(
14835            f.run(&[b"ZUNION", b"-1", b"z"]),
14836            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
14837        );
14838        assert_eq!(
14839            f.run(&[b"ZINTERCARD", b"0", b"z"]),
14840            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
14841        );
14842        // A count bigger than the line is a plain syntax error, which reads
14843        // oddly and is what Redis says.
14844        assert_eq!(
14845            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
14846            "-ERR syntax error\r\n"
14847        );
14848        assert_eq!(
14849            f.run(&[b"ZUNION", b"x", b"z"]),
14850            "-ERR value is not an integer or out of range\r\n"
14851        );
14852        // A WEIGHTS list that is not one per key is a syntax error, and a
14853        // weight that is not a number gets a sentence of its own.
14854        assert_eq!(
14855            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
14856            "-ERR syntax error\r\n"
14857        );
14858        assert_eq!(
14859            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
14860            "-ERR weight value is not a float\r\n"
14861        );
14862        assert_eq!(
14863            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
14864            "-ERR syntax error\r\n"
14865        );
14866    }
14867
14868    /// The three store forms, which answer a count and take no WITHSCORES.
14869    #[test]
14870    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
14871        let mut f = Fixture::new();
14872        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14873        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
14874        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
14875        assert_eq!(
14876            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
14877            "*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"
14878        );
14879        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
14880        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
14881        // An empty result deletes the destination rather than leaving an empty
14882        // sorted set, because an empty one does not exist.
14883        assert_eq!(
14884            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
14885            ":0\r\n"
14886        );
14887        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
14888        // The destination is allowed to name its own source.
14889        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
14890        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
14891        for cmd in [
14892            &[
14893                b"ZUNIONSTORE".as_slice(),
14894                b"d",
14895                b"2",
14896                b"z",
14897                b"y",
14898                b"WITHSCORES",
14899            ][..],
14900            &[
14901                b"ZDIFFSTORE",
14902                b"d",
14903                b"2",
14904                b"z",
14905                b"y",
14906                b"WEIGHTS",
14907                b"1",
14908                b"1",
14909            ],
14910        ] {
14911            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
14912        }
14913    }
14914
14915    /// `ZINTERCARD`, which counts without building anything.
14916    #[test]
14917    fn intercard_counts_and_stops_at_its_limit() {
14918        let mut f = Fixture::new();
14919        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14920        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
14921        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
14922        // A limit of zero is no limit, which is Redis's reading of it.
14923        assert_eq!(
14924            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
14925            ":2\r\n"
14926        );
14927        assert_eq!(
14928            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
14929            ":1\r\n"
14930        );
14931        // A negative limit and a limit that is not a number at all get the same
14932        // sentence, which looks like a mistake in Redis and is copied as one.
14933        let bad = "-ERR LIMIT can't be negative\r\n";
14934        assert_eq!(
14935            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
14936            bad
14937        );
14938        assert_eq!(
14939            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
14940            bad
14941        );
14942        for cmd in [
14943            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
14944            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
14945            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
14946        ] {
14947            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
14948        }
14949    }
14950
14951    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
14952    #[test]
14953    fn a_draw_answers_one_member_or_an_array_of_them() {
14954        let mut f = Fixture::new();
14955        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
14956        // No count is one member or a nil, a count is an array that may be
14957        // empty, and those are two reply types the client has to tell apart.
14958        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
14959        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
14960        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
14961        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
14962        // A positive count draws without replacement, so a count over the size
14963        // answers the whole set and never a member twice.
14964        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
14965        assert!(all.starts_with("*3\r\n"), "{all}");
14966        for m in ["a", "b", "c"] {
14967            assert!(all.contains(m), "{all}");
14968        }
14969        // A negative one draws with replacement and answers exactly as many as
14970        // it was asked for, whatever the size of the set.
14971        assert!(
14972            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
14973            "five draws with replacement"
14974        );
14975        assert!(
14976            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
14977                .starts_with("*4\r\n"),
14978            "two pairs, flat on RESP2"
14979        );
14980        f.out = Out::new(Proto::Resp3);
14981        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
14982        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
14983        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
14984        f.out = Out::new(Proto::Resp2);
14985        assert_eq!(
14986            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
14987            "-ERR syntax error\r\n"
14988        );
14989        assert_eq!(
14990            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
14991            "-ERR value is not an integer or out of range\r\n"
14992        );
14993    }
14994
14995    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
14996    #[test]
14997    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
14998        let mut f = Fixture::new();
14999        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15000        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";
15001        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
15002        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
15003        assert_eq!(
15004            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
15005            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
15006        );
15007        assert_eq!(
15008            f.run(&[b"ZSCAN", b"nokey", b"0"]),
15009            "*2\r\n$1\r\n0\r\n*0\r\n"
15010        );
15011        // A score stays a bulk string on RESP3, which is the one place the two
15012        // protocols agree about a score and everywhere else they do not.
15013        f.out = Out::new(Proto::Resp3);
15014        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
15015        f.out = Out::new(Proto::Resp2);
15016        assert_eq!(
15017            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
15018            "-ERR NOVALUES option can only be used in HSCAN\r\n"
15019        );
15020        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
15021        assert_eq!(
15022            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
15023            "-ERR syntax error\r\n"
15024        );
15025    }
15026
15027    /// The count is what decides the shape, and its value is not.
15028    #[test]
15029    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
15030        let mut f = Fixture::new();
15031        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15032        // No count, so one flat pair, and the score is a bulk string on RESP2.
15033        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
15034        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
15035        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
15036        // A count, so pairs, and on RESP2 they are flattened into one run.
15037        assert_eq!(
15038            f.run(&[b"ZPOPMIN", b"z", b"2"]),
15039            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
15040        );
15041        // An empty array rather than a null, which is where a sorted set pop and
15042        // a list pop part company, and the same answer a count of zero gives.
15043        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
15044        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
15045        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
15046        // The last member takes the key with it.
15047        assert_eq!(
15048            f.run(&[b"ZPOPMIN", b"z", b"9"]),
15049            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
15050        );
15051        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
15052
15053        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
15054        f.out = Out::new(Proto::Resp3);
15055        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
15056        assert_eq!(
15057            f.run(&[b"ZPOPMIN", b"z", b"1"]),
15058            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
15059        );
15060        f.out = Out::new(Proto::Resp2);
15061        // Both of these are the range error rather than the usual sentence about
15062        // integers, which is the odd answer and so the one worth copying.
15063        let bad = "-ERR value is out of range, must be positive\r\n";
15064        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
15065        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
15066        assert_eq!(
15067            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
15068            "-ERR syntax error\r\n"
15069        );
15070    }
15071
15072    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
15073    #[test]
15074    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
15075        let mut f = Fixture::new();
15076        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15077        assert_eq!(
15078            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
15079            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
15080        );
15081        // Nested on RESP2 as well, because the key name is already in front of
15082        // the pairs and there is nothing left to flatten into.
15083        assert_eq!(
15084            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
15085            "*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"
15086        );
15087        // A null array and not a null, the same as LMPOP.
15088        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
15089        f.out = Out::new(Proto::Resp3);
15090        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
15091        f.out = Out::new(Proto::Resp2);
15092        let numkeys = "-ERR numkeys should be greater than 0\r\n";
15093        for bad in [
15094            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
15095            &[b"ZMPOP", b"-1", b"z", b"MIN"],
15096            &[b"ZMPOP", b"x", b"z", b"MIN"],
15097        ] {
15098            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
15099        }
15100        let count = "-ERR count should be greater than 0\r\n";
15101        for bad in [
15102            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
15103            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
15104            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
15105        ] {
15106            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
15107        }
15108        let syntax = "-ERR syntax error\r\n";
15109        for bad in [
15110            // Two keys named and one given, so the word that should have been
15111            // the direction is a key and there is no direction left.
15112            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
15113            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
15114            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
15115            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
15116        ] {
15117            assert_eq!(f.run(bad), syntax, "{bad:?}");
15118        }
15119    }
15120
15121    /// The three that wait, when there is something there and they do not have
15122    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
15123    #[test]
15124    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
15125        let mut f = Fixture::new();
15126        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
15127        assert_eq!(
15128            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
15129            (
15130                Flow::Continue,
15131                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
15132            )
15133        );
15134        assert_eq!(
15135            f.run(&[b"BZPOPMAX", b"z", b"0"]),
15136            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
15137        );
15138        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
15139        assert_eq!(
15140            f.run(&[
15141                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
15142            ]),
15143            "*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"
15144        );
15145        f.out = Out::new(Proto::Resp3);
15146        assert_eq!(
15147            f.run(&[b"BZPOPMIN", b"z", b"0"]),
15148            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
15149        );
15150        f.out = Out::new(Proto::Resp2);
15151        // Nothing to take, so the client is parked and nothing was written.
15152        assert_eq!(
15153            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
15154            (Flow::Block, String::new())
15155        );
15156        assert_eq!(
15157            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
15158            (Flow::Block, String::new())
15159        );
15160        // The timeout is read before the key count, so this complains about the
15161        // timeout and not about the count.
15162        assert_eq!(
15163            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
15164            "-ERR timeout is not a float or out of range\r\n"
15165        );
15166        assert_eq!(
15167            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
15168            "-ERR numkeys should be greater than 0\r\n"
15169        );
15170        assert_eq!(
15171            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
15172            "-ERR timeout is negative\r\n"
15173        );
15174    }
15175
15176    /// A parked sorted set client is served by whatever puts a member under one
15177    /// of its keys, and is not served by something of another type landing
15178    /// there.
15179    #[test]
15180    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
15181        let mut f = Fixture::new();
15182        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
15183        assert_eq!(f.server.parked(), 1);
15184        // A string under the key is not what it asked for, so it stays parked
15185        // rather than being handed a WRONGTYPE on a command that was accepted.
15186        f.run(&[b"SET", b"z", b"v"]);
15187        let mut out = Out::new(Proto::Resp2);
15188        assert!(!f.server.serve_waiter(7, 0, &mut out));
15189        assert!(out.as_slice().is_empty());
15190        f.run(&[b"DEL", b"z"]);
15191        f.run(&[b"ZADD", b"z", b"5", b"m"]);
15192        assert!(f.server.serve_waiter(7, 0, &mut out));
15193        assert_eq!(
15194            core::str::from_utf8(out.as_slice()).expect("ascii"),
15195            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
15196        );
15197        // And the member is gone, which is what makes a queue of workers on a
15198        // sorted set work at all.
15199        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
15200    }
15201
15202    #[test]
15203    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
15204        let mut f = Fixture::new();
15205        f.run(&[b"SET", b"s", b"v"]);
15206        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
15207        for cmd in [
15208            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
15209            &[b"ZINCRBY", b"s", b"1", b"a"],
15210            &[b"ZCARD", b"s"],
15211            &[b"ZSCORE", b"s", b"a"],
15212            &[b"ZMSCORE", b"s", b"a"],
15213            &[b"ZREM", b"s", b"a"],
15214            &[b"ZRANK", b"s", b"a"],
15215            &[b"ZREVRANK", b"s", b"a"],
15216            &[b"ZCOUNT", b"s", b"1", b"2"],
15217            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
15218            &[b"ZRANGE", b"s", b"0", b"-1"],
15219            &[b"ZREVRANGE", b"s", b"0", b"-1"],
15220            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
15221            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
15222            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
15223            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
15224            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
15225            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
15226            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
15227            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
15228            &[b"ZUNION", b"1", b"s"],
15229            &[b"ZINTER", b"1", b"s"],
15230            &[b"ZDIFF", b"1", b"s"],
15231            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
15232            &[b"ZINTERSTORE", b"d", b"1", b"s"],
15233            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
15234            &[b"ZINTERCARD", b"1", b"s"],
15235            &[b"ZRANDMEMBER", b"s"],
15236            &[b"ZSCAN", b"s", b"0"],
15237            &[b"ZPOPMIN", b"s"],
15238            &[b"ZPOPMAX", b"s", b"2"],
15239            &[b"ZMPOP", b"1", b"s", b"MIN"],
15240            &[b"BZPOPMIN", b"s", b"0"],
15241            &[b"BZPOPMAX", b"s", b"0"],
15242            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
15243        ] {
15244            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
15245        }
15246        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
15247    }
15248
15249    /// The same churn the set, the string and the list get, because a sorted
15250    /// set that leaks a tree node per add looks exactly like one that does not
15251    /// until it has run for an afternoon.
15252    /// Not under Miri, for the reason on `churning_sets_does_not_grow_the_server`.
15253    #[cfg_attr(miri, ignore = "the volume is the claim")]
15254    #[test]
15255    fn churning_sorted_sets_does_not_grow_the_server() {
15256        let mut f = Fixture::new();
15257        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
15258        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
15259        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
15260        for i in 0..200 {
15261            args.push(&scores[i]);
15262            args.push(&members[i]);
15263        }
15264
15265        f.run(&args);
15266        f.run(&[b"DEL", b"z"]);
15267        f.server.compact_step();
15268        let after_first = f.server.memory_bytes();
15269
15270        for _ in 0..200 {
15271            f.run(&args);
15272            f.run(&[b"DEL", b"z"]);
15273            f.server.compact_step();
15274        }
15275        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
15276        assert!(
15277            f.server.memory_bytes() <= after_first * 2,
15278            "held {} after two hundred passes against {after_first} after one",
15279            f.server.memory_bytes()
15280        );
15281    }
15282
15283    // ------------------------------------------------------------------- geo
15284
15285    /// The three places every Redis geo example uses, and one more.
15286    ///
15287    /// Every reply this section asserts on came off a running 8.10.1 with these
15288    /// three loaded, byte for byte, including the number of digits in a
15289    /// coordinate and the four places on a distance.
15290    fn sicily(f: &mut Fixture) {
15291        f.run(&[
15292            b"GEOADD",
15293            b"Sicily",
15294            b"13.361389",
15295            b"38.115556",
15296            b"Palermo",
15297            b"15.087269",
15298            b"37.502669",
15299            b"Catania",
15300        ]);
15301        f.run(&[
15302            b"GEOADD",
15303            b"Sicily",
15304            b"13.583333",
15305            b"37.316667",
15306            b"Agrigento",
15307        ]);
15308    }
15309
15310    #[test]
15311    fn places_go_in_as_scores_and_come_back_as_positions() {
15312        let mut f = Fixture::new();
15313        assert_eq!(
15314            f.run(&[
15315                b"GEOADD",
15316                b"Sicily",
15317                b"13.361389",
15318                b"38.115556",
15319                b"Palermo",
15320                b"15.087269",
15321                b"37.502669",
15322                b"Catania"
15323            ]),
15324            ":2\r\n"
15325        );
15326        // A geo key is a sorted set and says so, which is not an implementation
15327        // detail either: a client removes a place with ZREM and counts them
15328        // with ZCARD, and the score is the number a real server stores.
15329        assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
15330        assert_eq!(
15331            f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
15332            "$16\r\n3479099956230698\r\n"
15333        );
15334        assert_eq!(
15335            f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
15336            "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
15337        );
15338        assert_eq!(
15339            f.run(&[
15340                b"GEOHASH",
15341                b"Sicily",
15342                b"Palermo",
15343                b"Catania",
15344                b"NonExisting"
15345            ]),
15346            "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
15347        );
15348        // A key that is not there is an empty one, and the two nulls are not
15349        // the same null: GEOPOS answers the array one and GEOHASH the string
15350        // one, which a RESP2 client can tell apart.
15351        assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
15352        assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
15353    }
15354
15355    #[test]
15356    fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
15357        let mut f = Fixture::new();
15358        sicily(&mut f);
15359        assert_eq!(
15360            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
15361            "$11\r\n166274.1516\r\n"
15362        );
15363        assert_eq!(
15364            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
15365            "$8\r\n166.2742\r\n"
15366        );
15367        assert_eq!(
15368            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
15369            "$8\r\n103.3182\r\n"
15370        );
15371        // A member that is not there and a key that is not there are the same
15372        // nil, and the unit is read before the key is looked up, so a bad unit
15373        // on a missing key is still an error.
15374        assert_eq!(
15375            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
15376            "$-1\r\n"
15377        );
15378        assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
15379        assert_eq!(
15380            f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
15381            "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
15382        );
15383        assert_eq!(
15384            f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
15385            "-ERR syntax error\r\n"
15386        );
15387    }
15388
15389    #[test]
15390    fn a_search_finds_what_is_inside_it_nearest_first() {
15391        let mut f = Fixture::new();
15392        sicily(&mut f);
15393        let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
15394        assert_eq!(
15395            f.run(&[
15396                b"GEOSEARCH",
15397                b"Sicily",
15398                b"FROMLONLAT",
15399                b"15",
15400                b"37",
15401                b"BYRADIUS",
15402                b"200",
15403                b"km",
15404                b"ASC"
15405            ]),
15406            all
15407        );
15408        // The older spelling of the same search, which is the same nine boxes
15409        // and the same order.
15410        assert_eq!(
15411            f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
15412            all
15413        );
15414        assert_eq!(
15415            f.run(&[
15416                b"GEORADIUS_RO",
15417                b"Sicily",
15418                b"15",
15419                b"37",
15420                b"200",
15421                b"km",
15422                b"ASC"
15423            ]),
15424            all
15425        );
15426        // A count with no ordering means the nearest ones, so DESC has to be
15427        // asked for to get the far end.
15428        assert_eq!(
15429            f.run(&[
15430                b"GEORADIUS",
15431                b"Sicily",
15432                b"15",
15433                b"37",
15434                b"200",
15435                b"km",
15436                b"DESC",
15437                b"COUNT",
15438                b"1"
15439            ]),
15440            "*1\r\n$7\r\nPalermo\r\n"
15441        );
15442        assert_eq!(
15443            f.run(&[
15444                b"GEORADIUS",
15445                b"Sicily",
15446                b"15",
15447                b"37",
15448                b"200",
15449                b"km",
15450                b"COUNT",
15451                b"1"
15452            ]),
15453            "*1\r\n$7\r\nCatania\r\n"
15454        );
15455        // Nothing inside a kilometre of that point, and nothing in a key that
15456        // is not there, and both are the empty array rather than an error.
15457        let empty = "*0\r\n";
15458        assert_eq!(
15459            f.run(&[
15460                b"GEOSEARCH",
15461                b"Sicily",
15462                b"FROMLONLAT",
15463                b"15",
15464                b"37",
15465                b"BYRADIUS",
15466                b"1",
15467                b"km"
15468            ]),
15469            empty
15470        );
15471        assert_eq!(
15472            f.run(&[
15473                b"GEOSEARCH",
15474                b"nokey",
15475                b"FROMLONLAT",
15476                b"15",
15477                b"37",
15478                b"BYRADIUS",
15479                b"1",
15480                b"km"
15481            ]),
15482            empty
15483        );
15484        assert_eq!(
15485            f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
15486            empty
15487        );
15488    }
15489
15490    #[test]
15491    fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
15492        let mut f = Fixture::new();
15493        sicily(&mut f);
15494        assert_eq!(
15495            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
15496            "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
15497        );
15498        // The member itself is nothing away from itself, which is where the
15499        // fixed point writer's zero shows up on the wire.
15500        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";
15501        assert_eq!(
15502            f.run(&[
15503                b"GEORADIUSBYMEMBER_RO",
15504                b"Sicily",
15505                b"Agrigento",
15506                b"100",
15507                b"km",
15508                b"WITHDIST"
15509            ]),
15510            with_dist
15511        );
15512        assert_eq!(
15513            f.run(&[
15514                b"GEOSEARCH",
15515                b"Sicily",
15516                b"FROMMEMBER",
15517                b"Agrigento",
15518                b"BYRADIUS",
15519                b"100",
15520                b"km",
15521                b"ASC",
15522                b"WITHDIST"
15523            ]),
15524            with_dist
15525        );
15526        assert_eq!(
15527            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
15528            "-ERR could not decode requested zset member\r\n"
15529        );
15530    }
15531
15532    #[test]
15533    fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
15534        let mut f = Fixture::new();
15535        sicily(&mut f);
15536        // Three options asked for, so each result is a four element array of
15537        // the member, the distance, the hash and a pair. The order of the three
15538        // is Redis's and not the order they were written in the command.
15539        assert_eq!(
15540            f.run(&[
15541                b"GEOSEARCH",
15542                b"Sicily",
15543                b"FROMLONLAT",
15544                b"15",
15545                b"37",
15546                b"BYBOX",
15547                b"400",
15548                b"400",
15549                b"km",
15550                b"ASC",
15551                b"WITHCOORD",
15552                b"WITHDIST",
15553                b"WITHHASH"
15554            ]),
15555            "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
15556             $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
15557             *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
15558             $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
15559             *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
15560             $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
15561        );
15562    }
15563
15564    #[test]
15565    fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
15566        let mut f = Fixture::new();
15567        sicily(&mut f);
15568        let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
15569                      $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
15570                      $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
15571        assert_eq!(
15572            f.run(&[
15573                b"GEOSEARCHSTORE",
15574                b"dst",
15575                b"Sicily",
15576                b"FROMLONLAT",
15577                b"15",
15578                b"37",
15579                b"BYRADIUS",
15580                b"200",
15581                b"km",
15582                b"ASC"
15583            ]),
15584            ":3\r\n"
15585        );
15586        assert_eq!(
15587            f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
15588            hashes
15589        );
15590        // The same again through the older spelling, which stores the same
15591        // scores, so a key written by either is a geo key.
15592        assert_eq!(
15593            f.run(&[
15594                b"GEORADIUS",
15595                b"Sicily",
15596                b"15",
15597                b"37",
15598                b"200",
15599                b"km",
15600                b"STORE",
15601                b"dst3"
15602            ]),
15603            ":3\r\n"
15604        );
15605        assert_eq!(
15606            f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
15607            hashes
15608        );
15609        // STOREDIST stores the distance in the search unit instead, and those
15610        // are full doubles rather than the four places WITHDIST writes. The
15611        // numbers on the right are what 8.10.1 stored for this search, and they
15612        // are compared with a tolerance rather than byte for byte because the
15613        // last bit of a haversine is the platform's sin, cos and asin: this
15614        // machine and that one disagree in the sixteenth digit, and so do two
15615        // Redis builds. Everything a client actually reads back is four places
15616        // and is asserted exactly above.
15617        assert_eq!(
15618            f.run(&[
15619                b"GEOSEARCHSTORE",
15620                b"dst2",
15621                b"Sicily",
15622                b"FROMLONLAT",
15623                b"15",
15624                b"37",
15625                b"BYRADIUS",
15626                b"200",
15627                b"km",
15628                b"ASC",
15629                b"STOREDIST"
15630            ]),
15631            ":3\r\n"
15632        );
15633        for (member, want) in [
15634            ("Catania", 56.441_257_870_158_19),
15635            ("Agrigento", 130.423_487_067_147_14),
15636            ("Palermo", 190.442_429_847_757_92),
15637        ] {
15638            let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
15639            let got: f64 = reply
15640                .trim_start_matches(|c: char| c != '\n')
15641                .trim()
15642                .parse()
15643                .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
15644            assert!(
15645                (got - want).abs() < 1e-9,
15646                "{member} scored {got} not {want}"
15647            );
15648        }
15649        // The order they went in is the order the scores put them in, which is
15650        // the point of storing the distance rather than the hash.
15651        assert_eq!(
15652            f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
15653            "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
15654        );
15655        // A search that finds nothing takes the destination with it rather than
15656        // leaving what was there, and a source key that is not there is a
15657        // search that finds nothing.
15658        assert_eq!(
15659            f.run(&[
15660                b"GEOSEARCHSTORE",
15661                b"dst",
15662                b"nokey",
15663                b"FROMLONLAT",
15664                b"15",
15665                b"37",
15666                b"BYRADIUS",
15667                b"200",
15668                b"km"
15669            ]),
15670            ":0\r\n"
15671        );
15672        assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
15673    }
15674
15675    #[test]
15676    fn the_gates_on_geoadd_are_the_ones_zadd_has() {
15677        let mut f = Fixture::new();
15678        sicily(&mut f);
15679        // XX on a member that is already where it is changes nothing, and NX on
15680        // one that is there refuses to move it.
15681        assert_eq!(
15682            f.run(&[
15683                b"GEOADD",
15684                b"Sicily",
15685                b"XX",
15686                b"CH",
15687                b"13.361389",
15688                b"38.115556",
15689                b"Palermo"
15690            ]),
15691            ":0\r\n"
15692        );
15693        assert_eq!(
15694            f.run(&[
15695                b"GEOADD",
15696                b"Sicily",
15697                b"NX",
15698                b"13.361389",
15699                b"38.9",
15700                b"Palermo"
15701            ]),
15702            ":0\r\n"
15703        );
15704        assert_eq!(
15705            f.run(&[
15706                b"GEOADD",
15707                b"Sicily",
15708                b"CH",
15709                b"13.361389",
15710                b"38.9",
15711                b"Palermo"
15712            ]),
15713            ":1\r\n"
15714        );
15715        // Out of range, and nothing is stored: the whole call is refused rather
15716        // than the good pairs going in and the bad one stopping it.
15717        assert_eq!(
15718            f.run(&[
15719                b"GEOADD",
15720                b"new",
15721                b"13.361389",
15722                b"38.115556",
15723                b"here",
15724                b"181",
15725                b"38",
15726                b"there"
15727            ]),
15728            "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
15729        );
15730        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
15731        assert_eq!(
15732            f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
15733            "-ERR value is not a valid float\r\n"
15734        );
15735        // The count of triples is checked before the two gates are, and a call
15736        // with no triples at all reaches the same sentence.
15737        assert_eq!(
15738            f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
15739            "-ERR syntax error\r\n"
15740        );
15741        assert_eq!(
15742            f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
15743            "-ERR syntax error\r\n"
15744        );
15745        assert_eq!(
15746            f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
15747            "-ERR syntax error\r\n"
15748        );
15749        assert_eq!(
15750            f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
15751            "-ERR wrong number of arguments for 'geoadd' command\r\n"
15752        );
15753    }
15754
15755    /// The sentences a search answers, which are its contract as much as the
15756    /// results are.
15757    #[test]
15758    fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
15759        let mut f = Fixture::new();
15760        sicily(&mut f);
15761        let cases: &[(&[&[u8]], &str)] = &[
15762            (
15763                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
15764                "-ERR need numeric radius\r\n",
15765            ),
15766            (
15767                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
15768                "-ERR radius cannot be negative\r\n",
15769            ),
15770            (
15771                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
15772                "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
15773            ),
15774            (
15775                &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
15776                "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
15777            ),
15778            (
15779                &[
15780                    b"GEOSEARCH",
15781                    b"Sicily",
15782                    b"FROMLONLAT",
15783                    b"15",
15784                    b"37",
15785                    b"BYBOX",
15786                    b"x",
15787                    b"1",
15788                    b"km",
15789                ],
15790                "-ERR need numeric width\r\n",
15791            ),
15792            (
15793                &[
15794                    b"GEOSEARCH",
15795                    b"Sicily",
15796                    b"FROMLONLAT",
15797                    b"15",
15798                    b"37",
15799                    b"BYBOX",
15800                    b"1",
15801                    b"y",
15802                    b"km",
15803                ],
15804                "-ERR need numeric height\r\n",
15805            ),
15806            (
15807                &[
15808                    b"GEOSEARCH",
15809                    b"Sicily",
15810                    b"FROMLONLAT",
15811                    b"15",
15812                    b"37",
15813                    b"BYBOX",
15814                    b"-1",
15815                    b"1",
15816                    b"km",
15817                ],
15818                "-ERR height or width cannot be negative\r\n",
15819            ),
15820            (
15821                &[
15822                    b"GEOSEARCH",
15823                    b"Sicily",
15824                    b"FROMLONLAT",
15825                    b"15",
15826                    b"37",
15827                    b"BYRADIUS",
15828                    b"1",
15829                    b"km",
15830                    b"ANY",
15831                ],
15832                "-ERR the ANY argument requires COUNT argument\r\n",
15833            ),
15834            (
15835                &[
15836                    b"GEOSEARCH",
15837                    b"Sicily",
15838                    b"FROMLONLAT",
15839                    b"15",
15840                    b"37",
15841                    b"BYRADIUS",
15842                    b"1",
15843                    b"km",
15844                    b"COUNT",
15845                    b"0",
15846                ],
15847                "-ERR COUNT must be > 0\r\n",
15848            ),
15849            (
15850                &[
15851                    b"GEOSEARCH",
15852                    b"Sicily",
15853                    b"BYRADIUS",
15854                    b"1",
15855                    b"km",
15856                    b"BYBOX",
15857                    b"1",
15858                    b"1",
15859                    b"km",
15860                ],
15861                "-ERR syntax error\r\n",
15862            ),
15863            (
15864                &[
15865                    b"GEOSEARCH",
15866                    b"Sicily",
15867                    b"FROMMEMBER",
15868                    b"Palermo",
15869                    b"FROMLONLAT",
15870                    b"1",
15871                    b"2",
15872                    b"BYRADIUS",
15873                    b"1",
15874                    b"km",
15875                ],
15876                "-ERR syntax error\r\n",
15877            ),
15878            // The two options a GEOSEARCH cannot leave out, each with its own
15879            // sentence, and the command quoted the way the client spelled it.
15880            (
15881                &[
15882                    b"geosearch",
15883                    b"Sicily",
15884                    b"BYRADIUS",
15885                    b"1",
15886                    b"km",
15887                    b"ASC",
15888                    b"WITHDIST",
15889                ],
15890                "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
15891            ),
15892            (
15893                &[
15894                    b"GEOSEARCH",
15895                    b"Sicily",
15896                    b"FROMLONLAT",
15897                    b"15",
15898                    b"37",
15899                    b"ASC",
15900                    b"WITHDIST",
15901                ],
15902                "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
15903            ),
15904            // A store cannot also be asked for the distance, and the two
15905            // families name themselves differently in the same sentence.
15906            (
15907                &[
15908                    b"GEOSEARCHSTORE",
15909                    b"d",
15910                    b"Sicily",
15911                    b"FROMLONLAT",
15912                    b"15",
15913                    b"37",
15914                    b"BYRADIUS",
15915                    b"1",
15916                    b"km",
15917                    b"WITHCOORD",
15918                ],
15919                "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
15920            ),
15921            (
15922                &[
15923                    b"GEORADIUS",
15924                    b"Sicily",
15925                    b"15",
15926                    b"37",
15927                    b"1",
15928                    b"km",
15929                    b"WITHDIST",
15930                    b"STORE",
15931                    b"d",
15932                ],
15933                "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
15934            ),
15935            // The read only forms have no store at all, so the word is a stray
15936            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
15937            (
15938                &[
15939                    b"GEORADIUS_RO",
15940                    b"Sicily",
15941                    b"15",
15942                    b"37",
15943                    b"1",
15944                    b"km",
15945                    b"STORE",
15946                    b"d",
15947                ],
15948                "-ERR syntax error\r\n",
15949            ),
15950            (
15951                &[
15952                    b"GEOSEARCH",
15953                    b"Sicily",
15954                    b"FROMLONLAT",
15955                    b"15",
15956                    b"37",
15957                    b"BYRADIUS",
15958                    b"1",
15959                    b"km",
15960                    b"STOREDIST",
15961                ],
15962                "-ERR syntax error\r\n",
15963            ),
15964        ];
15965        for (parts, want) in cases {
15966            assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
15967        }
15968    }
15969
15970    /// A wrong type wins over a bad argument, because the key is looked up
15971    /// first, and every one of the ten says the same thing about it.
15972    #[test]
15973    fn every_geo_command_says_wrongtype() {
15974        let mut f = Fixture::new();
15975        f.run(&[b"SET", b"s", b"v"]);
15976        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
15977        let cases: &[&[&[u8]]] = &[
15978            &[b"GEOADD", b"s", b"13", b"38", b"m"],
15979            &[b"GEOPOS", b"s", b"m"],
15980            &[b"GEOHASH", b"s", b"m"],
15981            &[b"GEODIST", b"s", b"a", b"b"],
15982            &[
15983                b"GEOSEARCH",
15984                b"s",
15985                b"FROMLONLAT",
15986                b"15",
15987                b"37",
15988                b"BYRADIUS",
15989                b"1",
15990                b"km",
15991            ],
15992            &[
15993                b"GEOSEARCHSTORE",
15994                b"d",
15995                b"s",
15996                b"FROMLONLAT",
15997                b"15",
15998                b"37",
15999                b"BYRADIUS",
16000                b"1",
16001                b"km",
16002            ],
16003            &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
16004            &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
16005            &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
16006            &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
16007        ];
16008        for case in cases {
16009            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
16010        }
16011        // And it wins over an argument that will not parse, which is the whole
16012        // reason the lookup comes first.
16013        assert_eq!(
16014            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
16015            wrong
16016        );
16017    }
16018
16019    // ----------------------------------------------------------------- array
16020
16021    #[test]
16022    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
16023        let mut f = Fixture::new();
16024        // Three consecutive positions from a high index, and the reply is how
16025        // many of them were empty before rather than how many were written.
16026        assert_eq!(
16027            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
16028            ":3\r\n"
16029        );
16030        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
16031        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
16032        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
16033        // A hole and a key that is not there are the same answer.
16034        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
16035        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
16036        assert_eq!(
16037            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
16038            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
16039        );
16040        // Scattered pairs in one command, last write wins within it.
16041        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
16042        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
16043    }
16044
16045    /// The two numbers an array reports are not the same number, and one of
16046    /// them does not fit a signed integer.
16047    #[test]
16048    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
16049        let mut f = Fixture::new();
16050        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
16051        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
16052        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
16053        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
16054        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
16055        // Deleting in the middle leaves the high water mark where it was.
16056        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
16057        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
16058        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
16059
16060        // The top of the space is addressable, and its length is a number with
16061        // bit sixty three set, so the reply has to be unsigned or it comes back
16062        // negative.
16063        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
16064        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
16065        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
16066        // And one past it does not exist, so a write that would reach it fails
16067        // before any of it lands.
16068        assert_eq!(
16069            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
16070            "-ERR array index overflow\r\n"
16071        );
16072        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
16073    }
16074
16075    /// One reply per position and not one per element, which is the whole
16076    /// reason the range is capped.
16077    #[test]
16078    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
16079        let mut f = Fixture::new();
16080        f.run(&[b"ARSET", b"a", b"1", b"x"]);
16081        assert_eq!(
16082            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
16083            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
16084        );
16085        // The two ends may come in either order, and the answer is reversed
16086        // rather than empty.
16087        assert_eq!(
16088            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
16089            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
16090        );
16091        // A key that is not there reads like an array of nothing but holes.
16092        assert_eq!(
16093            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
16094            "*2\r\n$-1\r\n$-1\r\n"
16095        );
16096        // A range wider than a million positions is refused and not trimmed,
16097        // because against a missing key it is a request for as many nulls as
16098        // the range is wide.
16099        assert_eq!(
16100            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
16101            "-ERR range exceeds maximum of 1000000 items\r\n"
16102        );
16103    }
16104
16105    /// Every index in the argument list is read before the key is touched, so
16106    /// a bad one at the end leaves nothing half written.
16107    #[test]
16108    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
16109        let mut f = Fixture::new();
16110        assert_eq!(
16111            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
16112            "-ERR invalid array index\r\n"
16113        );
16114        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
16115        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
16116        assert_eq!(
16117            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
16118            "-ERR invalid array index\r\n"
16119        );
16120        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
16121        // An index is unsigned here, so the numbers a list would take are not
16122        // the last element, they are errors.
16123        assert_eq!(
16124            f.run(&[b"ARGET", b"a", b"-1"]),
16125            "-ERR invalid array index\r\n"
16126        );
16127        // And a pair list with an odd tail is an arity error rather than a
16128        // syntax one.
16129        assert_eq!(
16130            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
16131            "-ERR wrong number of arguments for 'armset' command\r\n"
16132        );
16133        assert_eq!(
16134            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
16135            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
16136        );
16137    }
16138
16139    #[test]
16140    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
16141        let mut f = Fixture::new();
16142        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
16143        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
16144        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
16145        // Two ranges in one command, and the second one covers the whole space
16146        // without walking it.
16147        assert_eq!(
16148            f.run(&[
16149                b"ARDELRANGE",
16150                b"a",
16151                b"100",
16152                b"200",
16153                b"0",
16154                b"18446744073709551614"
16155            ]),
16156            ":2\r\n"
16157        );
16158        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
16159        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
16160        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
16161    }
16162
16163    /// A value goes out as the bytes it came in as, whichever of the three ways
16164    /// the array found to store it.
16165    #[test]
16166    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
16167        let mut f = Fixture::new();
16168        let long = vec![b'v'; 200];
16169        f.run(&[
16170            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
16171            b"short", b"5", &long, b"6", b"-0",
16172        ]);
16173        // 42 is an integer, 007 is not one because it does not print back the
16174        // same, 3.5 survives a double and 3.14 does not, and the last two are a
16175        // word packed string and a blob.
16176        assert_eq!(
16177            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
16178            format!(
16179                "*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",
16180                String::from_utf8_lossy(&long)
16181            )
16182        );
16183    }
16184
16185    #[test]
16186    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
16187        let mut f = Fixture::new();
16188        f.run(&[b"ARSET", b"a", b"0", b"x"]);
16189        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
16190        assert_eq!(
16191            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
16192            "$12\r\nsliced-array\r\n"
16193        );
16194        // And it is a body like any other, so the key commands work on it.
16195        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
16196        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
16197        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
16198        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
16199        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
16200        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
16201    }
16202
16203    #[test]
16204    fn every_array_command_refuses_a_key_holding_something_else() {
16205        let mut f = Fixture::new();
16206        f.run(&[b"SET", b"s", b"v"]);
16207        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
16208        for cmd in [
16209            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
16210            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
16211            &[b"ARGET".as_ref(), b"s", b"0"][..],
16212            &[b"ARMGET".as_ref(), b"s", b"0"][..],
16213            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
16214            &[b"ARLEN".as_ref(), b"s"][..],
16215            &[b"ARCOUNT".as_ref(), b"s"][..],
16216            &[b"ARDEL".as_ref(), b"s", b"0"][..],
16217            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
16218            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
16219            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
16220            &[b"ARNEXT".as_ref(), b"s"][..],
16221            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
16222            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
16223            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
16224            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
16225            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
16226            &[b"ARINFO".as_ref(), b"s"][..],
16227        ] {
16228            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
16229        }
16230    }
16231
16232    /// Two of the array commands look the key up before they read the index and
16233    /// the rest read the index first, so the same broken argument gets two
16234    /// different errors depending on which command it went to.
16235    #[test]
16236    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
16237        let mut f = Fixture::new();
16238        f.run(&[b"SET", b"s", b"v"]);
16239        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
16240        let bad = "-ERR invalid array index\r\n";
16241        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
16242        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
16243        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
16244        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
16245        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
16246        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
16247        // And on a key that is an array the index is just an index.
16248        f.run(&[b"ARSET", b"a", b"0", b"x"]);
16249        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
16250        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
16251    }
16252
16253    #[test]
16254    fn an_append_follows_a_cursor_the_client_can_move() {
16255        let mut f = Fixture::new();
16256        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
16257        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
16258        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
16259        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
16260        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
16261
16262        // A seek says where the next one goes, and a missing key has no cursor
16263        // to move and is not created by the asking.
16264        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
16265        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
16266        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
16267        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
16268        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
16269        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
16270        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
16271
16272        // The top of the space is the one index only ARSEEK will take, and it
16273        // leaves the cursor with nowhere to go.
16274        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
16275        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
16276        assert_eq!(
16277            f.run(&[b"ARINSERT", b"a", b"x"]),
16278            "-ERR insert index overflow\r\n"
16279        );
16280        assert_eq!(
16281            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
16282            "-ERR invalid array index\r\n"
16283        );
16284    }
16285
16286    #[test]
16287    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
16288        let mut f = Fixture::new();
16289        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
16290        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
16291        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
16292        assert_eq!(
16293            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
16294            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
16295        );
16296        // Growing it after it has wrapped puts the survivors back in the order
16297        // they arrived, which is the whole point of paying for the rebuild.
16298        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
16299        assert_eq!(
16300            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
16301            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
16302        );
16303        // The size is read before the key, so a bad one is a bad size wherever
16304        // it is sent.
16305        assert_eq!(
16306            f.run(&[b"ARRING", b"r", b"0", b"x"]),
16307            "-ERR size must be positive\r\n"
16308        );
16309        assert_eq!(
16310            f.run(&[b"ARRING", b"r", b"big", b"x"]),
16311            "-ERR invalid size\r\n"
16312        );
16313    }
16314
16315    #[test]
16316    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
16317        let mut f = Fixture::new();
16318        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
16319        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
16320        assert_eq!(
16321            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
16322            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
16323        );
16324        assert_eq!(
16325            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
16326            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
16327        );
16328        assert_eq!(
16329            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
16330            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
16331            "more than there is gets what there is"
16332        );
16333        // Nothing asked for is an empty reply, and Redis answers that before it
16334        // has read the option or looked at the key.
16335        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
16336        assert_eq!(
16337            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
16338            "-ERR syntax error\r\n"
16339        );
16340        assert_eq!(
16341            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
16342            "-ERR invalid COUNT\r\n"
16343        );
16344
16345        // With no cursor the tail of the array is the anchor, and a hole inside
16346        // the window is reported as one.
16347        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
16348        assert_eq!(
16349            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
16350            "*2\r\n$-1\r\n$1\r\nz\r\n"
16351        );
16352    }
16353
16354    #[test]
16355    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
16356        let mut f = Fixture::new();
16357        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
16358        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
16359        // The whole index space, which ARGETRANGE refuses and this one answers
16360        // in three visits because holes cost nothing.
16361        assert_eq!(
16362            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
16363            "*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"
16364        );
16365        assert_eq!(
16366            f.run(&[
16367                b"ARSCAN",
16368                b"a",
16369                b"18446744073709551614",
16370                b"0",
16371                b"LIMIT",
16372                b"1"
16373            ]),
16374            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
16375        );
16376        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
16377        assert_eq!(
16378            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
16379            "-ERR LIMIT must be positive\r\n"
16380        );
16381        assert_eq!(
16382            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
16383            "-ERR syntax error\r\n"
16384        );
16385        assert_eq!(
16386            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
16387            "-ERR wrong number of arguments for 'arscan' command\r\n"
16388        );
16389    }
16390
16391    #[test]
16392    fn a_grep_answers_the_indexes_whose_elements_match() {
16393        let mut f = Fixture::new();
16394        assert_eq!(
16395            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
16396            "*0\r\n"
16397        );
16398        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
16399
16400        // The two bounds take the ends of the array as well as an index, and a
16401        // reversed range is walked backwards the way ARSCAN walks one.
16402        assert_eq!(
16403            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
16404            "*3\r\n:0\r\n:1\r\n:2\r\n"
16405        );
16406        assert_eq!(
16407            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
16408            "*3\r\n:2\r\n:1\r\n:0\r\n"
16409        );
16410        assert_eq!(
16411            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
16412            "*2\r\n:1\r\n:2\r\n"
16413        );
16414
16415        // One test each. NOCASE reaches all four of them and it may be written
16416        // after the pattern it applies to.
16417        assert_eq!(
16418            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
16419            "*1\r\n:0\r\n"
16420        );
16421        assert_eq!(
16422            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
16423            "*2\r\n:0\r\n:3\r\n"
16424        );
16425        assert_eq!(
16426            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
16427            "*1\r\n:2\r\n"
16428        );
16429        assert_eq!(
16430            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
16431            "*2\r\n:1\r\n:2\r\n"
16432        );
16433
16434        // OR is the default and AND has to be asked for, and either way the
16435        // last of a repeated option wins.
16436        let both: &[&[u8]] = &[
16437            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
16438        ];
16439        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
16440        assert_eq!(
16441            f.run(&[
16442                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
16443            ]),
16444            "*0\r\n"
16445        );
16446        assert_eq!(
16447            f.run(&[
16448                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
16449            ]),
16450            "*2\r\n:0\r\n:1\r\n"
16451        );
16452
16453        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
16454        // not the positions it had to look at.
16455        assert_eq!(
16456            f.run(&[
16457                b"ARGREP",
16458                b"a",
16459                b"-",
16460                b"+",
16461                b"MATCH",
16462                b"a",
16463                b"WITHVALUES",
16464                b"LIMIT",
16465                b"2"
16466            ]),
16467            "*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"
16468        );
16469        assert_eq!(
16470            f.run(&[
16471                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
16472            ]),
16473            "*1\r\n:3\r\n"
16474        );
16475    }
16476
16477    /// Everything ARGREP refuses, in the order it refuses it.
16478    #[test]
16479    fn a_grep_reports_a_broken_command_the_way_redis_does() {
16480        let mut f = Fixture::new();
16481        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
16482        let syntax = "-ERR syntax error\r\n";
16483
16484        // The bounds are read before the plan, so a bad index beats a bad
16485        // predicate whichever way round the two are written.
16486        assert_eq!(
16487            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
16488            "-ERR invalid array index\r\n"
16489        );
16490        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
16491        // A keyword with nothing after it, and a command that asks for nothing.
16492        assert_eq!(
16493            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
16494            syntax
16495        );
16496        assert_eq!(
16497            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
16498            syntax
16499        );
16500        assert_eq!(
16501            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
16502            syntax,
16503            "a command with no predicate in it at all"
16504        );
16505        assert_eq!(
16506            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
16507            "-ERR LIMIT must be positive\r\n"
16508        );
16509        assert_eq!(
16510            f.run(&[
16511                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
16512            ]),
16513            "-ERR value is not an integer or out of range\r\n"
16514        );
16515        assert_eq!(
16516            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
16517            "-ERR regular expression is empty\r\n"
16518        );
16519        assert_eq!(
16520            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
16521            "-ERR invalid regular expression: Missing ')'\r\n"
16522        );
16523        assert_eq!(
16524            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
16525            "-ERR regular expression backreferences are not supported\r\n"
16526        );
16527        // The arity is minus six, so a predicate keyword with no pattern after
16528        // it is short by one and never reaches the parser.
16529        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
16530        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
16531        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
16532    }
16533
16534    #[test]
16535    fn an_op_reduces_a_range_to_one_number() {
16536        let mut f = Fixture::new();
16537        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
16538        assert_eq!(
16539            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
16540            "$4\r\n-0.5\r\n"
16541        );
16542        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
16543        assert_eq!(
16544            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
16545            "$3\r\n2.5\r\n"
16546        );
16547        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
16548        assert_eq!(
16549            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
16550            ":1\r\n"
16551        );
16552        // An aggregate is written with seventeen significant digits, which is
16553        // Redis's own choice and not what a score comes back as.
16554        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
16555        assert_eq!(
16556            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
16557            "$19\r\n0.30000000000000004\r\n"
16558        );
16559        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
16560        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
16561
16562        // Nothing to work with is a null, and a missing key is a null for the
16563        // aggregates and a zero for the two that count.
16564        f.run(&[b"ARSET", b"w", b"0", b"word"]);
16565        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
16566        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
16567        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
16568
16569        assert_eq!(
16570            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
16571            "-ERR unknown operation\r\n"
16572        );
16573        assert_eq!(
16574            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
16575            "-ERR MATCH requires a value argument\r\n"
16576        );
16577        assert_eq!(
16578            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
16579            "-ERR wrong number of arguments for 'arop' command\r\n"
16580        );
16581    }
16582
16583    #[test]
16584    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
16585        let mut f = Fixture::new();
16586        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
16587        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
16588        let short = f.run(&[b"ARINFO", b"a"]);
16589        assert!(
16590            short.starts_with("*14\r\n"),
16591            "seven pairs on RESP2: {short}"
16592        );
16593        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
16594        assert!(
16595            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
16596            "{short}"
16597        );
16598        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
16599        let full = f.run(&[b"ARINFO", b"a", b"full"]);
16600        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
16601        // Two values one apart are held sparsely, so the dense count is zero and
16602        // the two dense averages have nothing to average.
16603        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
16604        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
16605        assert!(
16606            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
16607            "{full}"
16608        );
16609        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
16610
16611        // On RESP3 the same reply is a map and the averages are doubles.
16612        let mut g = Fixture::new();
16613        g.run(&[b"HELLO", b"3"]);
16614        g.run(&[b"ARINSERT", b"a", b"x"]);
16615        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
16616        assert!(map.starts_with("%12\r\n"), "{map}");
16617        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
16618        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
16619    }
16620
16621    #[test]
16622    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
16623        let mut f = Fixture::new();
16624        // Whole numbers up to two to the sixty second come back as integers,
16625        // and past that the digit generator takes over and uses an exponent.
16626        for (score, want) in [
16627            ("3", "3"),
16628            ("3.5", "3.5"),
16629            ("0.3", "0.3"),
16630            ("1e30", "1e+30"),
16631            ("1e19", "1e+19"),
16632            ("1e-7", "1e-7"),
16633            ("0.000001", "0.000001"),
16634            ("4611686018427387904", "4611686018427387904"),
16635            ("-0", "-0"),
16636        ] {
16637            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
16638            assert_eq!(
16639                f.run(&[b"ZSCORE", b"z", b"m"]),
16640                format!("${}\r\n{want}\r\n", want.len()),
16641                "score {score}"
16642            );
16643        }
16644
16645        // The same bytes on RESP3, where the reply is a double rather than a
16646        // bulk string.
16647        let mut g = Fixture::new();
16648        g.run(&[b"HELLO", b"3"]);
16649        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
16650        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
16651        // The two float increments are not this printer. They go through
16652        // ld2string in its human mode, which is a fixed point conversion with
16653        // the trailing zeros taken off, so they never write an exponent, and
16654        // they reply with a bulk string on both protocols.
16655        assert_eq!(
16656            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
16657            "$31\r\n1000000000000000000000000000000\r\n"
16658        );
16659        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
16660        assert_eq!(
16661            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
16662            "$20\r\n10000000000000000000\r\n"
16663        );
16664    }
16665
16666    // ----------------------------------------------------------------- graph
16667
16668    #[test]
16669    fn a_node_comes_back_with_the_fields_it_went_in_with() {
16670        let mut f = Fixture::new();
16671        assert_eq!(
16672            f.run(&[
16673                b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
16674            ]),
16675            ":1\r\n"
16676        );
16677        // The year comes back as the four bytes that were sent and not as a
16678        // number, because every property is text and there is nothing on the
16679        // wire that says which of `1815` and `"1815"` the client meant. The
16680        // fields are in the document's order, which is sorted by name, because
16681        // that is what makes a field lookup a binary search.
16682        assert_eq!(
16683            f.run(&[b"G.NGET", b"social", b"ada"]),
16684            "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
16685        );
16686        // A second write to the same id replaces the document and says so with
16687        // a zero, so an ingest can count what it created.
16688        assert_eq!(
16689            f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
16690            ":0\r\n"
16691        );
16692        assert_eq!(
16693            f.run(&[b"G.NGET", b"social", b"ada"]),
16694            "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
16695        );
16696        // A node with no properties is an empty map and not a null, which is
16697        // how a client tells an isolated node from one that is not there.
16698        assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
16699        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
16700        assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
16701        assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
16702
16703        // A field with no value creates nothing, because the pairs are checked
16704        // before the key is touched.
16705        assert_eq!(
16706            f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
16707            "-ERR syntax error\r\n"
16708        );
16709        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
16710
16711        // On RESP3 the same reply is a map.
16712        let mut g = Fixture::new();
16713        g.run(&[b"HELLO", b"3"]);
16714        g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
16715        assert_eq!(
16716            g.run(&[b"G.NGET", b"social", b"ada"]),
16717            "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
16718        );
16719    }
16720
16721    #[test]
16722    fn an_edge_creates_the_ends_it_needs() {
16723        let mut f = Fixture::new();
16724        assert_eq!(
16725            f.run(&[
16726                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
16727            ]),
16728            ":1\r\n"
16729        );
16730        // Neither end was written first and both are there, as empty nodes.
16731        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
16732        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
16733        assert_eq!(
16734            f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
16735            "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
16736        );
16737        assert_eq!(
16738            f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
16739            "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
16740        );
16741        // The same pair under the same label again updates the edge rather than
16742        // making a second one.
16743        assert_eq!(
16744            f.run(&[
16745                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
16746            ]),
16747            ":0\r\n"
16748        );
16749        assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
16750        // A different label between the same pair is a different edge.
16751        assert_eq!(
16752            f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
16753            ":1\r\n"
16754        );
16755        assert_eq!(
16756            f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
16757            ":1\r\n"
16758        );
16759
16760        assert_eq!(
16761            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
16762            ":1\r\n"
16763        );
16764        assert_eq!(
16765            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
16766            ":0\r\n"
16767        );
16768        // A label nothing has used, an end that is not there, and a key that is
16769        // not there are all a zero rather than an error.
16770        assert_eq!(
16771            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
16772            ":0\r\n"
16773        );
16774        assert_eq!(
16775            f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
16776            ":0\r\n"
16777        );
16778        assert_eq!(
16779            f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
16780            ":0\r\n"
16781        );
16782    }
16783
16784    /// A run is paged the way `SCAN` is paged, so a client that can walk one
16785    /// can walk the other.
16786    #[test]
16787    fn a_hop_answers_a_cursor_and_a_page() {
16788        let mut f = Fixture::new();
16789        for i in 0..25u32 {
16790            let dst = format!("n{i}");
16791            f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
16792        }
16793        // Ten without being asked, and the cursor is where to carry on from.
16794        let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
16795        assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
16796
16797        let mut seen = 0;
16798        let mut cursor = String::from("0");
16799        loop {
16800            let page = f.run(&[
16801                b"G.OUT",
16802                b"social",
16803                b"hub",
16804                b"FOLLOWS",
16805                b"COUNT",
16806                b"7",
16807                b"CURSOR",
16808                cursor.as_bytes(),
16809            ]);
16810            let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
16811            cursor = head
16812                .rsplit("\r\n")
16813                .next()
16814                .expect("the cursor line")
16815                .to_string();
16816            seen += rest
16817                .split_once("\r\n")
16818                .expect("the page length")
16819                .0
16820                .parse::<usize>()
16821                .expect("a length");
16822            if cursor == "0" {
16823                break;
16824            }
16825        }
16826        assert_eq!(seen, 25, "every neighbour once across the pages");
16827
16828        // A cursor past the end is an empty page and not an error, and so is a
16829        // key or a label that is not there.
16830        assert_eq!(
16831            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
16832            "*2\r\n$1\r\n0\r\n*0\r\n"
16833        );
16834        assert_eq!(
16835            f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
16836            "*2\r\n$1\r\n0\r\n*0\r\n"
16837        );
16838        assert_eq!(
16839            f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
16840            "*2\r\n$1\r\n0\r\n*0\r\n"
16841        );
16842        assert_eq!(
16843            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
16844            "-ERR COUNT must be a positive integer\r\n"
16845        );
16846        assert_eq!(
16847            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
16848            "-ERR syntax error\r\n"
16849        );
16850    }
16851
16852    #[test]
16853    fn a_degree_counts_one_way_or_both() {
16854        let mut f = Fixture::new();
16855        f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
16856        f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
16857        f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
16858        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
16859        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
16860        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
16861        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
16862        assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
16863        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
16864        assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
16865        assert_eq!(
16866            f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
16867            "-ERR syntax error\r\n"
16868        );
16869    }
16870
16871    /// A walk answers which nodes it can reach and not by how many routes, so a
16872    /// node two ways out is in the frontier once.
16873    #[test]
16874    fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
16875        let mut f = Fixture::new();
16876        for (src, dst) in [
16877            ("ada", "grace"),
16878            ("ada", "alan"),
16879            ("grace", "edsger"),
16880            ("alan", "edsger"),
16881            ("edsger", "barbara"),
16882        ] {
16883            f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
16884        }
16885        // Two hops without being asked, the start left out, and edsger once
16886        // even though both of the first hop's nodes point at it.
16887        assert_eq!(
16888            f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
16889            "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
16890        );
16891        assert_eq!(
16892            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
16893            "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
16894        );
16895        let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
16896        assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
16897        assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
16898        // COUNT stops the walk rather than trimming what it found.
16899        assert_eq!(
16900            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
16901            "*1\r\n$5\r\ngrace\r\n"
16902        );
16903        // A node nothing leaves is an empty array and not an error.
16904        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
16905        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
16906        assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
16907        assert_eq!(
16908            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
16909            "-ERR DEPTH must be a positive integer\r\n"
16910        );
16911        assert_eq!(
16912            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
16913            "-ERR syntax error\r\n"
16914        );
16915    }
16916
16917    /// The two sided search, which is the whole reason `G.PATH` is a command
16918    /// and not something a client builds out of `G.OUT`.
16919    #[test]
16920    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
16921        let mut f = Fixture::new();
16922        // A chain of six, and a shortcut that makes a shorter way round under a
16923        // second label so the search has to take either kind of hop.
16924        for i in 0..6u32 {
16925            let src = format!("n{i}");
16926            let dst = format!("n{}", i + 1);
16927            f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
16928        }
16929        assert_eq!(
16930            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
16931            "*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"
16932        );
16933        f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
16934        assert_eq!(
16935            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
16936            "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
16937        );
16938        // A node to itself is a path of one, and a depth too short to reach is
16939        // no path at all.
16940        assert_eq!(
16941            f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
16942            "*1\r\n$2\r\nn2\r\n"
16943        );
16944        assert_eq!(
16945            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
16946            "*0\r\n"
16947        );
16948        // Direction counts: the chain only goes one way.
16949        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
16950        // An unreachable node, a node that is not there, and a key that is not
16951        // there are the same empty answer.
16952        f.run(&[b"G.NADD", b"road", b"island"]);
16953        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
16954        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
16955        assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
16956        assert_eq!(
16957            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
16958            "-ERR syntax error\r\n"
16959        );
16960    }
16961
16962    /// The point of the escape in the record tag: the keyspace owns a graph key
16963    /// the way it owns every other key, and none of these commands know a graph
16964    /// exists.
16965    #[test]
16966    fn the_keyspace_sees_a_graph_key_like_any_other() {
16967        let mut f = Fixture::new();
16968        f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
16969        assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
16970        assert_eq!(
16971            f.run(&[b"OBJECT", b"ENCODING", b"social"]),
16972            "$9\r\nadjacency\r\n"
16973        );
16974        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
16975        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
16976        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
16977        // A graph is counted against the server the way every other body is,
16978        // which is what `maxmemory` will read when this key is a million nodes.
16979        // There is no `MEMORY USAGE` command yet, so this asks the server.
16980        let held = f.server.memory_bytes();
16981        for i in 0..200u32 {
16982            let dst = format!("n{i}");
16983            f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
16984        }
16985        assert!(
16986            f.server.memory_bytes() > held,
16987            "two hundred edges cost something: {held} then {}",
16988            f.server.memory_bytes()
16989        );
16990        f.run(&[b"DEL", b"big"]);
16991
16992        // An expiry, then a rename, then a move to another database, all of
16993        // which are the keyspace moving a record it cannot look inside.
16994        assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
16995        assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
16996        assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
16997        assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
16998        assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
16999        f.run(&[b"SELECT", b"1"]);
17000        assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
17001
17002        assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
17003        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
17004        f.run(&[b"G.NADD", b"g", b"n"]);
17005        assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
17006        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
17007    }
17008
17009    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
17010    /// rather than answering the way they answer for a key that is not there.
17011    #[test]
17012    fn a_graph_cannot_be_copied_or_dumped() {
17013        let mut f = Fixture::new();
17014        f.run(&[b"G.NADD", b"social", b"ada"]);
17015        assert_eq!(
17016            f.run(&[b"COPY", b"social", b"other"]),
17017            "-ERR COPY is not supported for a graph\r\n"
17018        );
17019        assert_eq!(
17020            f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
17021            "-ERR COPY is not supported for a graph\r\n"
17022        );
17023        assert_eq!(
17024            f.run(&[b"DUMP", b"social"]),
17025            "-ERR DUMP is not supported for a graph\r\n"
17026        );
17027        // A refused copy leaves both keys exactly as they were.
17028        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
17029    }
17030
17031    /// A graph key is a key, so the commands for the other types refuse it and
17032    /// the graph commands refuse theirs.
17033    #[test]
17034    fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
17035        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
17036        let mut f = Fixture::new();
17037        f.run(&[b"G.NADD", b"social", b"ada"]);
17038        assert_eq!(f.run(&[b"GET", b"social"]), wrong);
17039        assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
17040        assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
17041
17042        f.run(&[b"SET", b"str", b"v"]);
17043        for cmd in [
17044            vec![b"G.NADD".as_ref(), b"str", b"n"],
17045            vec![b"G.NGET".as_ref(), b"str", b"n"],
17046            vec![b"G.NDEL".as_ref(), b"str", b"n"],
17047            vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
17048            vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
17049            vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
17050            vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
17051            vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
17052            vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
17053            vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
17054        ] {
17055            assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
17056        }
17057    }
17058
17059    /// Every other collection here takes its key with it when its last member
17060    /// goes, and a graph is no different.
17061    #[test]
17062    fn a_graph_goes_when_its_last_node_does() {
17063        let mut f = Fixture::new();
17064        f.run(&[
17065            b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
17066        ]);
17067        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
17068        // The node and the edges that hung off it are both gone.
17069        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
17070        assert_eq!(
17071            f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
17072            ":0\r\n"
17073        );
17074        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
17075        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
17076
17077        assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
17078        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
17079        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
17080        assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
17081
17082        // The id the removed node had is not handed out again, so a client
17083        // holding an id from an earlier reply cannot have it mean another node.
17084        f.run(&[b"G.NADD", b"social", b"first"]);
17085        f.run(&[b"G.NADD", b"social", b"second"]);
17086        f.run(&[b"G.NDEL", b"social", b"first"]);
17087        f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
17088        assert_eq!(
17089            f.run(&[b"G.OUT", b"social", b"third", b"F"]),
17090            "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
17091        );
17092    }
17093
17094    // ------------------------------------------------------------------ json
17095
17096    /// The two path syntaxes answer different shapes, which is the thing a
17097    /// client is most likely to be broken by and so the thing to pin first.
17098    #[test]
17099    fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
17100        let mut f = Fixture::new();
17101        let doc = br#"{"a":1,"b":{"c":true}}"#;
17102        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
17103        // No path at all is the legacy root and not `$`, so the document comes
17104        // back as itself rather than wrapped.
17105        assert_eq!(
17106            f.run(&[b"JSON.GET", b"doc"]),
17107            bulk(r#"{"a":1,"b":{"c":true}}"#)
17108        );
17109        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
17110        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
17111        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
17112        // A path that matched nothing is an empty set on one syntax and an
17113        // error on the other, and the error does not quote the path.
17114        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
17115        assert_eq!(
17116            f.run(&[b"JSON.GET", b"doc", b".nope"]),
17117            "-ERR Path does not exist\r\n"
17118        );
17119        assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
17120        // The key is a document to the rest of the keyspace, under the name
17121        // RedisJSON registers, and every generic command works on it.
17122        assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
17123        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
17124        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
17125        assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
17126        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
17127    }
17128
17129    /// The two error lines RedisJSON sends without a prefix in front of them.
17130    ///
17131    /// Every other error this server writes starts `ERR` or `WRONGTYPE`. These
17132    /// two do not, on a real server, and a differential harness compares the
17133    /// whole line.
17134    #[test]
17135    fn the_two_json_errors_that_carry_no_prefix() {
17136        let mut f = Fixture::new();
17137        f.run(&[b"SET", b"plain", b"x"]);
17138        let wrong = "-Existing key has wrong Redis type\r\n";
17139        assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
17140        assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
17141        assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
17142        assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
17143        assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
17144
17145        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
17146        // A wildcard that matched something writes to all of it. A wildcard
17147        // that matched nothing would have to invent a place, and that is the
17148        // other unprefixed line.
17149        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
17150        assert_eq!(
17151            f.run(&[b"JSON.GET", b"doc"]),
17152            bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
17153        );
17154        assert_eq!(
17155            f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
17156            "-Err wrong static path\r\n"
17157        );
17158    }
17159
17160    /// What `JSON.SET` does with a path that named nowhere.
17161    #[test]
17162    fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
17163        let mut f = Fixture::new();
17164        // A key that is not there can only be written whole.
17165        assert_eq!(
17166            f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
17167            "-ERR new objects must be created at the root\r\n"
17168        );
17169        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
17170        // The root check comes before NX and XX, which is the order a real
17171        // server checks them in.
17172        assert_eq!(
17173            f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
17174            "-ERR new objects must be created at the root\r\n"
17175        );
17176        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
17177        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
17178
17179        f.run(&[
17180            b"JSON.SET",
17181            b"doc",
17182            b"$",
17183            br#"{"o":{},"arr":[1,2],"s":"x"}"#,
17184        ]);
17185        // One step past a container that is there is a place to write.
17186        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
17187        // One step past something that is not, or past something that is not an
17188        // object, is not an error and is not a write either.
17189        assert_eq!(
17190            f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
17191            "$-1\r\n"
17192        );
17193        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
17194        // An index past the end does not append. JSON.ARRAPPEND appends.
17195        assert_eq!(
17196            f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
17197            "-ERR array index out of range\r\n"
17198        );
17199        assert_eq!(
17200            f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
17201            "-ERR array index out of range\r\n"
17202        );
17203        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
17204        // NX on a path that is there and XX on a path that is not are both a
17205        // nil and neither changes anything.
17206        assert_eq!(
17207            f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
17208            "$-1\r\n"
17209        );
17210        assert_eq!(
17211            f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
17212            "$-1\r\n"
17213        );
17214        assert_eq!(
17215            f.run(&[b"JSON.GET", b"doc"]),
17216            bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
17217        );
17218        // Text that is not JSON is refused before the key is touched. The
17219        // line has no `ERR` in front of it, which is this command's and not
17220        // every command's, and is in D-37.
17221        assert!(
17222            f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
17223                .starts_with("-this is not the start of a value")
17224        );
17225        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
17226    }
17227
17228    /// `JSON.DEL`, `JSON.TYPE`, `JSON.TOGGLE` and `JSON.CLEAR`, each of which
17229    /// answers a count or a word rather than text.
17230    #[test]
17231    fn the_json_commands_that_do_not_answer_text() {
17232        let mut f = Fixture::new();
17233        let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
17234        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
17235
17236        assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
17237        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
17238        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
17239        assert_eq!(
17240            f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
17241            format!("*1\r\n{}", bulk("integer"))
17242        );
17243        // The one place a legacy path that matched nothing is a nil rather than
17244        // an error, which lines up with a key that is not there.
17245        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
17246        assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
17247
17248        // A boolean flips and answers the value it now has, as an integer on
17249        // one syntax and as the word on the other.
17250        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
17251        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
17252        // Something that is not a boolean is a hole on one syntax and one
17253        // sentence covering both cases on the other.
17254        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
17255        assert_eq!(
17256            f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
17257            "-ERR Path does not exist or not a bool\r\n"
17258        );
17259        assert_eq!(
17260            f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
17261            "-ERR Path does not exist or not a bool\r\n"
17262        );
17263        assert_eq!(
17264            f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
17265            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17266        );
17267
17268        // Clearing empties containers and zeroes numbers and leaves everything
17269        // else alone, and counts only what it changed.
17270        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
17271        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
17272        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
17273        assert_eq!(
17274            f.run(&[b"JSON.GET", b"doc"]),
17275            bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
17276        );
17277
17278        // Deleting counts what it removed, and deleting the root is deleting
17279        // the key.
17280        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
17281        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
17282        // Deleting the last member of the root container deletes the key, the
17283        // same way popping the last element off a list does. It is a rule about
17284        // deleting and not about shape: a document written as an empty object
17285        // by JSON.SET stays, because nothing was removed from it.
17286        assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
17287        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
17288        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
17289        assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
17290        assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
17291        assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
17292        assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
17293        assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
17294    }
17295
17296    /// `JSON.GET` with more than one path, and with a layout.
17297    ///
17298    /// The wrapper the reply is built in is laid out too, so what a path
17299    /// matched starts one level in for a single JSONPath and two for one of
17300    /// several, and getting that wrong is the kind of thing only a byte for
17301    /// byte comparison catches.
17302    #[test]
17303    fn json_get_lays_out_the_wrapper_it_builds() {
17304        let mut f = Fixture::new();
17305        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
17306
17307        assert_eq!(
17308            f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
17309            bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
17310        );
17311        // Legacy paths are not wrapped, even when there are several of them.
17312        assert_eq!(
17313            f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
17314            bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
17315        );
17316        let fmt: &[&[u8]] = &[b"INDENT", b"  ", b"NEWLINE", b"\n", b"SPACE", b" "];
17317        let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
17318        one.extend_from_slice(fmt);
17319        one.push(b"$.b");
17320        assert_eq!(
17321            f.run(&one),
17322            bulk("[\n  [\n    1,\n    {\n      \"c\": 2\n    }\n  ]\n]")
17323        );
17324        let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
17325        two.extend_from_slice(fmt);
17326        two.push(b"$.a");
17327        two.push(b"$.nope");
17328        assert_eq!(
17329            f.run(&two),
17330            bulk("{\n  \"$.a\": [\n    1\n  ],\n  \"$.nope\": []\n}")
17331        );
17332        // The options are read before the paths and in any order, and a
17333        // document with nothing to lay out is the same either way.
17334        let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
17335        root.push(b".a");
17336        assert_eq!(f.run(&root), bulk("1"));
17337    }
17338
17339    /// `JSON.MGET`, which is the only command here that reads more than one key
17340    /// and so the only one whose answer has holes in it.
17341    #[test]
17342    fn json_mget_answers_once_per_key_whatever_is_under_them() {
17343        let mut f = Fixture::new();
17344        f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
17345        f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
17346        f.run(&[b"SET", b"plain", b"x"]);
17347        assert_eq!(
17348            f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
17349            format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
17350        );
17351        // A key that is not there and a key holding something else are both a
17352        // hole rather than an error, the way MGET treats a hash.
17353        assert_eq!(
17354            f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
17355            format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
17356        );
17357        // A legacy path that matched nothing is a hole too, because one bad
17358        // answer should not lose the others.
17359        assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
17360    }
17361
17362    /// The four commands that ask how big something is, and the four different
17363    /// sets of answers they give for the same three failures.
17364    ///
17365    /// There is no pattern in this and there is no reading it off the
17366    /// documentation either. It was read off a running RedisJSON one line at a
17367    /// time, and it is written down here because the error text is what a client
17368    /// library branches on.
17369    #[test]
17370    fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
17371        let mut f = Fixture::new();
17372        let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
17373        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
17374
17375        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
17376        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
17377        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
17378        assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
17379        assert_eq!(
17380            f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
17381            format!("*2\r\n{}{}", bulk("x"), bulk("y"))
17382        );
17383        // A JSONPath answers one entry per match and a hole for a match of the
17384        // wrong kind, which is the one shape all four agree on.
17385        assert_eq!(
17386            f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
17387            "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
17388        );
17389
17390        // A legacy path that matched nothing. Two of them are an error and two
17391        // of them are a nil, and the two errors do not use the same sentence.
17392        assert_eq!(
17393            f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
17394            "-ERR Path does not exist\r\n"
17395        );
17396        assert_eq!(
17397            f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
17398            "-ERR Path does not exist\r\n"
17399        );
17400        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
17401        // A nil bulk and not an empty array, even though the answer would have
17402        // been an array, which is what RedisJSON sends here too.
17403        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
17404        // The JSONPath spelling of the same question is an empty array, since
17405        // no match is not a failure on that syntax.
17406        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
17407
17408        // A legacy path that matched the wrong kind of value. Now two of them
17409        // are an ERR and two of them are a WRONGTYPE, and it is not the same
17410        // two.
17411        assert_eq!(
17412            f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
17413            "-ERR Path does not exist or not an array\r\n"
17414        );
17415        assert_eq!(
17416            f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
17417            "-ERR Path does not exist or not an object\r\n"
17418        );
17419        assert_eq!(
17420            f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
17421            "-WRONGTYPE wrong type of path value - expected object\r\n"
17422        );
17423        assert_eq!(
17424            f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
17425            "-WRONGTYPE wrong type of path value - expected string\r\n"
17426        );
17427
17428        // A key that is not there, where the two syntaxes swap over: the legacy
17429        // path is the quiet answer and the JSONPath is the error.
17430        assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
17431        assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
17432        assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
17433        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
17434        assert_eq!(
17435            f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
17436            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17437        );
17438        // Except this one, which answers about the path instead.
17439        assert_eq!(
17440            f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
17441            "-ERR Path does not exist or not an object\r\n"
17442        );
17443    }
17444
17445    /// `JSON.ARRAPPEND`, `JSON.ARRINSERT`, `JSON.ARRTRIM` and `JSON.ARRPOP`.
17446    ///
17447    /// The four of them share one error line for a path that named something
17448    /// that is not an array, and they disagree about what an index outside the
17449    /// array means: insert refuses it and the other two clamp.
17450    #[test]
17451    fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
17452        let mut f = Fixture::new();
17453        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
17454
17455        assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
17456        assert_eq!(
17457            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
17458            "*1\r\n:6\r\n"
17459        );
17460        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
17461
17462        // A negative index counts back from the end, and the end itself is a
17463        // place to insert at, so an insert at the length is an append.
17464        assert_eq!(
17465            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
17466            ":7\r\n"
17467        );
17468        assert_eq!(
17469            f.run(&[b"JSON.GET", b"doc", b".a"]),
17470            bulk("[1,2,3,4,5,0,6]")
17471        );
17472        assert_eq!(
17473            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
17474            ":8\r\n"
17475        );
17476        // One past the end is not, and neither is one before the front.
17477        assert_eq!(
17478            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
17479            "-ERR index out of bounds\r\n"
17480        );
17481        assert_eq!(
17482            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
17483            "-ERR index out of bounds\r\n"
17484        );
17485
17486        // Trim takes both ends inclusive and clamps both of them, so a start
17487        // past the end leaves an empty array rather than an error.
17488        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
17489        assert_eq!(
17490            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
17491            ":3\r\n"
17492        );
17493        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
17494        assert_eq!(
17495            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
17496            ":2\r\n"
17497        );
17498        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
17499        assert_eq!(
17500            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
17501            ":0\r\n"
17502        );
17503        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
17504
17505        // Pop clamps as well, its default is the last element, and an empty
17506        // array pops a nil rather than failing.
17507        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
17508        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
17509        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
17510        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
17511        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
17512
17513        // One sentence covers a path that matched nothing and a path that
17514        // matched the wrong kind of value, for all four of them.
17515        for call in [
17516            &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
17517            &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
17518            &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
17519            &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
17520        ] {
17521            for path in [&b".n"[..], &b".nope"[..]] {
17522                let args: Vec<&[u8]> = call
17523                    .iter()
17524                    .map(|a| if *a == b"PATH" { path } else { *a })
17525                    .collect();
17526                assert_eq!(
17527                    f.run(&args),
17528                    "-ERR Path does not exist or not an array\r\n",
17529                    "{} {}",
17530                    String::from_utf8_lossy(call[0]),
17531                    String::from_utf8_lossy(path)
17532                );
17533            }
17534        }
17535
17536        // A key that is not there is the same sentence for all four, on either
17537        // syntax, and it is about the key and not about the path.
17538        assert_eq!(
17539            f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
17540            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17541        );
17542        assert_eq!(
17543            f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
17544            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17545        );
17546
17547        // The values are parsed before the key is touched, so text that is not
17548        // JSON leaves the document alone.
17549        // Text that is not JSON is refused before the key is touched, and
17550        // the line has no `ERR` in front of it, which is D-37.
17551        assert!(
17552            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
17553                .starts_with("-this is not the start of a value")
17554        );
17555        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
17556    }
17557
17558    /// `JSON.ARRINSERT` refuses the whole command when any one of the arrays a
17559    /// path matched cannot take the index, which is D-36.
17560    ///
17561    /// RedisJSON walks the matches, inserts into each one it can, and returns
17562    /// the error on the first one it cannot, leaving the earlier inserts in the
17563    /// document. A write here is one list of edits applied together, so either
17564    /// all of them happen or none of them do.
17565    #[test]
17566    fn json_arrinsert_is_all_or_nothing_across_the_matches() {
17567        let mut f = Fixture::new();
17568        let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
17569        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
17570        assert_eq!(
17571            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
17572            "-ERR index out of bounds\r\n"
17573        );
17574        assert_eq!(
17575            f.run(&[b"JSON.GET", b"doc"]),
17576            bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
17577        );
17578        // Every match can take the index, so every match gets it.
17579        assert_eq!(
17580            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
17581            "*3\r\n:4\r\n:3\r\n:2\r\n"
17582        );
17583        assert_eq!(
17584            f.run(&[b"JSON.GET", b"doc"]),
17585            bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
17586        );
17587    }
17588
17589    /// `JSON.ARRINDEX`, whose stop is exclusive and whose start clamps to the
17590    /// last element rather than to one past it.
17591    ///
17592    /// Both of those read like mistakes and both are what RedisJSON does. The
17593    /// start is the one that bites: a start of five into an array of four still
17594    /// looks at the fourth, so a search that should have run out of array comes
17595    /// back with an answer.
17596    #[test]
17597    fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
17598        let mut f = Fixture::new();
17599        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
17600
17601        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
17602        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
17603        assert_eq!(
17604            f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
17605            "*1\r\n:1\r\n"
17606        );
17607
17608        // Zero as the stop means the end rather than the front, so leaving it
17609        // off and passing it are the same thing.
17610        assert_eq!(
17611            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
17612            ":3\r\n"
17613        );
17614        // The stop is exclusive, so a stop of three does not look at index
17615        // three.
17616        assert_eq!(
17617            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
17618            ":-1\r\n"
17619        );
17620
17621        // The start clamps to the last element in both directions, which is why
17622        // a start of four, five or minus one all find the 1 at index three.
17623        for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
17624            assert_eq!(
17625                f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
17626                ":3\r\n",
17627                "{}",
17628                String::from_utf8_lossy(start)
17629            );
17630        }
17631        assert_eq!(
17632            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
17633            ":0\r\n"
17634        );
17635        // An empty array is the one case that comes back with nothing, since
17636        // the stop is zero and the loop never starts.
17637        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
17638        assert_eq!(
17639            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
17640            ":-1\r\n"
17641        );
17642
17643        // The comparison is structural rather than one of the encoded bytes,
17644        // because an object in a stored document holds its keys as intern table
17645        // ids where one parsed off the wire holds them as bytes.
17646        f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
17647        assert_eq!(
17648            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
17649            ":0\r\n"
17650        );
17651        assert_eq!(
17652            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
17653            ":1\r\n"
17654        );
17655        assert_eq!(
17656            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
17657            ":-1\r\n"
17658        );
17659
17660        // Its errors are a third set again: a missing legacy path is the short
17661        // sentence, the wrong kind of value is a WRONGTYPE, and a key that is
17662        // not there is about the path on either syntax.
17663        assert_eq!(
17664            f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
17665            "-ERR Path does not exist\r\n"
17666        );
17667        assert_eq!(
17668            f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
17669            "-WRONGTYPE wrong type of path value - expected array\r\n"
17670        );
17671        assert_eq!(
17672            f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
17673            "-ERR Path does not exist\r\n"
17674        );
17675        assert_eq!(
17676            f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
17677            "-ERR Path does not exist\r\n"
17678        );
17679    }
17680
17681    /// The number family answers text and keeps an integer an integer until
17682    /// something in the sum is not one.
17683    #[test]
17684    fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
17685        let mut f = Fixture::new();
17686        let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
17687        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
17688
17689        // A legacy path answers the new value as JSON text in a bulk string,
17690        // not as a number, which is the shape all three of them use.
17691        assert_eq!(
17692            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
17693            bulk("9").as_str()
17694        );
17695        // A JSONPath answers a bulk string holding a JSON array.
17696        assert_eq!(
17697            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
17698            bulk("[11]").as_str()
17699        );
17700        // Two integers stay an integer and a double anywhere in it makes the
17701        // answer a double, which the document then holds.
17702        assert_eq!(
17703            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
17704            bulk("13.0").as_str()
17705        );
17706        assert_eq!(
17707            f.run(&[b"JSON.TYPE", b"doc", b".i"]),
17708            bulk("number").as_str()
17709        );
17710        assert_eq!(
17711            f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
17712            bulk("3.0").as_str()
17713        );
17714        assert_eq!(
17715            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
17716            bulk("-8").as_str()
17717        );
17718        // A power of a half is a square root, and the square root of a negative
17719        // number is the error that says the answer is not a number.
17720        f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
17721        assert_eq!(
17722            f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
17723            bulk("1.224744871391589").as_str()
17724        );
17725        assert_eq!(
17726            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
17727            "-ERR result is not a number\r\n"
17728        );
17729        // An integer answer that does not fit is refused rather than promoted,
17730        // and a negative exponent lands in the same error because there is no
17731        // integer answer to two to the minus one.
17732        f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
17733        assert_eq!(
17734            f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
17735            "-ERR numeric overflow\r\n"
17736        );
17737        f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
17738        assert_eq!(
17739            f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
17740            "-ERR numeric overflow\r\n"
17741        );
17742        // A double that leaves the finite numbers is the other error.
17743        f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
17744        assert_eq!(
17745            f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
17746            "-ERR result is not a number\r\n"
17747        );
17748
17749        // A match that is not a number is a null inside the array on a
17750        // JSONPath, and a legacy path that found no number at all is the error
17751        // with the module's own typo in it.
17752        assert_eq!(
17753            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
17754            bulk("[null]").as_str()
17755        );
17756        assert_eq!(
17757            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
17758            bulk("[]").as_str()
17759        );
17760        assert_eq!(
17761            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
17762            "-ERR Path does not exist or does not contains a number\r\n"
17763        );
17764        assert_eq!(
17765            f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
17766            "-ERR Path does not exist or does not contains a number\r\n"
17767        );
17768        // The operand is JSON and has to be a number. Valid JSON that is not
17769        // one is a line of its own, and it goes out without a prefix.
17770        assert_eq!(
17771            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
17772            "-bad input number\r\n"
17773        );
17774        assert_eq!(
17775            f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
17776            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17777        );
17778        assert_eq!(
17779            f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
17780            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17781        );
17782    }
17783
17784    /// `JSON.STRAPPEND` puts its path in the middle and makes it optional,
17785    /// which nothing else in the group does.
17786    #[test]
17787    fn json_strappend_reads_its_shape_off_the_argument_count() {
17788        let mut f = Fixture::new();
17789        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
17790
17791        assert_eq!(
17792            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
17793            ":3\r\n"
17794        );
17795        assert_eq!(
17796            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
17797            "*1\r\n:4\r\n"
17798        );
17799        // The length is in bytes and not in characters, so one two byte letter
17800        // takes it up by two.
17801        assert_eq!(
17802            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
17803            ":6\r\n"
17804        );
17805        // Three arguments means the value is the last one and the path is the
17806        // root, so this appends to a document that is a string on its own.
17807        f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
17808        assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
17809        assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
17810
17811        // The value is JSON and has to be a JSON string. A number is a
17812        // WRONGTYPE about a path value even though it was the value that was
17813        // wrong, which is the module's wording and not a slip here.
17814        assert_eq!(
17815            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
17816            "-WRONGTYPE wrong type of path value - expected string\r\n"
17817        );
17818        assert_eq!(
17819            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
17820            "*1\r\n$-1\r\n"
17821        );
17822        assert_eq!(
17823            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
17824            "-ERR Path does not exist or not a string\r\n"
17825        );
17826        assert_eq!(
17827            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
17828            "*0\r\n"
17829        );
17830        assert_eq!(
17831            f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
17832            "-ERR could not perform this operation on a key that doesn't exist\r\n"
17833        );
17834    }
17835
17836    /// A legacy path can match more than one value, and which of them the one
17837    /// answer comes from is not the same choice twice.
17838    #[test]
17839    fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
17840        let mut f = Fixture::new();
17841        // Three arrays of one, two and three elements, which tells the first
17842        // match and the last match apart in a single command.
17843        let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
17844
17845        f.run(&[b"JSON.SET", b"doc", b"$", three]);
17846        assert_eq!(
17847            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
17848            ":4\r\n"
17849        );
17850        f.run(&[b"JSON.SET", b"doc", b"$", three]);
17851        assert_eq!(
17852            f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
17853            ":2\r\n"
17854        );
17855        f.run(&[b"JSON.SET", b"doc", b"$", three]);
17856        assert_eq!(
17857            f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
17858            ":1\r\n"
17859        );
17860        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
17861        assert_eq!(
17862            f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
17863            bulk("1").as_str()
17864        );
17865        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
17866        assert_eq!(
17867            f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
17868            bulk("13").as_str()
17869        );
17870        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
17871        assert_eq!(
17872            f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
17873            ":4\r\n"
17874        );
17875        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
17876        assert_eq!(
17877            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
17878            bulk("false").as_str()
17879        );
17880        // Every one of them wrote to all three matches, whichever one it chose
17881        // to answer about.
17882        assert_eq!(
17883            f.run(&[b"JSON.GET", b"doc", b".a"]),
17884            bulk("[false,true,false]").as_str()
17885        );
17886
17887        // A match of the wrong kind is skipped rather than being the answer, so
17888        // a path that found a string and then two arrays still answers.
17889        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
17890        assert_eq!(
17891            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
17892            ":3\r\n"
17893        );
17894        assert_eq!(
17895            f.run(&[b"JSON.GET", b"doc", b".a"]),
17896            bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
17897        );
17898        // Nothing of the right kind anywhere is the error, and that is the only
17899        // case that is.
17900        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
17901        assert_eq!(
17902            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
17903            "-ERR Path does not exist or not an array\r\n"
17904        );
17905        assert_eq!(
17906            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
17907            "-ERR Path does not exist or not a bool\r\n"
17908        );
17909        // The one array that was there and had nothing in it is an answer and
17910        // not a skip, so the pop answers about it rather than about the array
17911        // after it.
17912        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
17913        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
17914        assert_eq!(
17915            f.run(&[b"JSON.GET", b"doc", b".a"]),
17916            bulk("[[],[2]]").as_str()
17917        );
17918    }
17919
17920    /// A path that matched a value and something inside that value writes to
17921    /// both, which is what `$..` and a nested wildcard are for.
17922    #[test]
17923    fn a_write_reaches_a_match_that_sits_inside_another_match() {
17924        let mut f = Fixture::new();
17925        let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
17926
17927        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
17928        assert_eq!(
17929            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
17930            "*3\r\n:3\r\n:2\r\n:3\r\n"
17931        );
17932        assert_eq!(
17933            f.run(&[b"JSON.GET", b"doc", b"$"]),
17934            bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
17935        );
17936
17937        // The same for a trim, where the outer array keeps the two elements the
17938        // inner writes landed in.
17939        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
17940        assert_eq!(
17941            f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
17942            "*3\r\n:1\r\n:1\r\n:1\r\n"
17943        );
17944        assert_eq!(
17945            f.run(&[b"JSON.GET", b"doc", b"$"]),
17946            bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
17947        );
17948
17949        // And for a number, where the first match is the object the outer array
17950        // holds and only the two inside it are numbers.
17951        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
17952        assert_eq!(
17953            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
17954            bulk("[null,8,8]").as_str()
17955        );
17956    }
17957
17958    /// The value a write is given is looked at only once the path has found
17959    /// something of the right kind to use it on.
17960    #[test]
17961    fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
17962        let mut f = Fixture::new();
17963        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
17964
17965        // A string is not a number, so the path answers first and the `"x"` is
17966        // never looked at. Same for the value that is not JSON at all.
17967        assert_eq!(
17968            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
17969            bulk("[null]").as_str()
17970        );
17971        assert_eq!(
17972            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
17973            bulk("[null]").as_str()
17974        );
17975        assert_eq!(
17976            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
17977            bulk("[]").as_str()
17978        );
17979        assert_eq!(
17980            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
17981            "-ERR Path does not exist or does not contains a number\r\n"
17982        );
17983        // A number match anywhere and the value is looked at after all.
17984        assert_eq!(
17985            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
17986            "-bad input number\r\n"
17987        );
17988
17989        // JSON.STRAPPEND follows the same order with its own two answers.
17990        assert_eq!(
17991            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
17992            "*1\r\n$-1\r\n"
17993        );
17994        assert_eq!(
17995            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
17996            "-ERR Path does not exist or not a string\r\n"
17997        );
17998        assert_eq!(
17999            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
18000            "-WRONGTYPE wrong type of path value - expected string\r\n"
18001        );
18002
18003        // A key that is not there still comes before either of them.
18004        assert_eq!(
18005            f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
18006            "-ERR could not perform this operation on a key that doesn't exist\r\n"
18007        );
18008        assert_eq!(
18009            f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
18010            "-ERR could not perform this operation on a key that doesn't exist\r\n"
18011        );
18012    }
18013
18014    /// RFC 7386 in one test: a null deletes, everything else merges, and a
18015    /// patch that is not an object replaces what it lands on.
18016    #[test]
18017    fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
18018        let mut f = Fixture::new();
18019
18020        // A key that is not there is created at the root, nulls and all,
18021        // because a deletion with nothing to delete is still what the client
18022        // sent.
18023        assert_eq!(
18024            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
18025            "+OK\r\n"
18026        );
18027        assert_eq!(
18028            f.run(&[b"JSON.GET", b"doc", b"$"]),
18029            bulk(r#"[{"x":null,"y":1}]"#).as_str()
18030        );
18031
18032        // Onto something that is there, a null deletes the member of that name
18033        // and the rest is merged one level at a time.
18034        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
18035        assert_eq!(
18036            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
18037            "+OK\r\n"
18038        );
18039        assert_eq!(
18040            f.run(&[b"JSON.GET", b"doc", b"$"]),
18041            bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
18042        );
18043
18044        // A patch that is not an object replaces what it is merged onto.
18045        assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
18046        assert_eq!(
18047            f.run(&[b"JSON.GET", b"doc", b"$"]),
18048            bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
18049        );
18050
18051        // A patch object onto a value that is not an object starts from an
18052        // empty object, so this time the null has nothing to delete and is
18053        // dropped rather than stored.
18054        assert_eq!(
18055            f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
18056            "+OK\r\n"
18057        );
18058        assert_eq!(
18059            f.run(&[b"JSON.GET", b"doc", b"$"]),
18060            bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
18061        );
18062
18063        // A member one level past the end of the document is created and keeps
18064        // its nulls, two levels past it is a write that did not happen, and a
18065        // path that would have to invent where it goes is the unprefixed line.
18066        assert_eq!(
18067            f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
18068            "+OK\r\n"
18069        );
18070        assert_eq!(
18071            f.run(&[b"JSON.GET", b"doc", b"$.new"]),
18072            bulk(r#"[{"z":null}]"#).as_str()
18073        );
18074        assert_eq!(
18075            f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
18076            "$-1\r\n"
18077        );
18078        assert_eq!(
18079            f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
18080            "-Err wrong static path\r\n"
18081        );
18082
18083        // A wildcard merges every match.
18084        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
18085        assert_eq!(
18086            f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
18087            "+OK\r\n"
18088        );
18089        assert_eq!(
18090            f.run(&[b"JSON.GET", b"doc", b"$"]),
18091            bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
18092        );
18093
18094        // The three ways to get it wrong.
18095        assert_eq!(
18096            f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
18097            "-ERR syntax error\r\n"
18098        );
18099        assert_eq!(
18100            f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
18101            "-ERR new objects must be created at the root\r\n"
18102        );
18103        f.run(&[b"SET", b"str", b"x"]);
18104        assert_eq!(
18105            f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
18106            "-Existing key has wrong Redis type\r\n"
18107        );
18108    }
18109
18110    /// A descent is the one path that matches a value and something inside that
18111    /// same value, and the inner merge has to survive the outer one.
18112    #[test]
18113    fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
18114        let mut f = Fixture::new();
18115        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
18116        assert_eq!(
18117            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
18118            "+OK\r\n"
18119        );
18120        // `a`, `a.b`, `c` and `c[0]` all match. `a.b` is merged first and `a` is
18121        // merged onto the result, so the `{"m":1}` written into `a.b` is still
18122        // there. Doing it the other way round would leave `{"a":{"b":1,"m":1}}`.
18123        assert_eq!(
18124            f.run(&[b"JSON.GET", b"doc", b"$"]),
18125            bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
18126        );
18127
18128        // A deletion down the same path, which is the case where the inner
18129        // merge empties the object the outer one then copies.
18130        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
18131        assert_eq!(
18132            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
18133            "+OK\r\n"
18134        );
18135        assert_eq!(
18136            f.run(&[b"JSON.GET", b"doc", b"$"]),
18137            bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
18138        );
18139    }
18140
18141    /// A filter is a selector like any other, so every command that takes a path
18142    /// takes one, reads and writes alike.
18143    #[test]
18144    fn a_filter_path_reads_and_writes_the_members_it_keeps() {
18145        let mut f = Fixture::new();
18146        let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
18147        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
18148
18149        assert_eq!(
18150            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
18151            bulk(r#"["a","c"]"#).as_str()
18152        );
18153        // `$` inside the expression is the document, so a member can be measured
18154        // against something that is not inside it.
18155        assert_eq!(
18156            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
18157            bulk(r#"["a","c"]"#).as_str()
18158        );
18159        // The legacy syntax takes one too, and answers the first match.
18160        assert_eq!(
18161            f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
18162            bulk(r#""a""#).as_str()
18163        );
18164        assert_eq!(
18165            f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
18166            "*1\r\n$6\r\nobject\r\n"
18167        );
18168
18169        // A write goes through it as far as a value that is already there. A
18170        // field that is not there yet has nowhere definite to go, which is the
18171        // same refusal a wildcard gets.
18172        assert_eq!(
18173            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
18174            bulk("[9,10]").as_str()
18175        );
18176        assert_eq!(
18177            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
18178            "+OK\r\n"
18179        );
18180        assert_eq!(
18181            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
18182            "-Err wrong static path\r\n"
18183        );
18184        assert_eq!(
18185            f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
18186            ":2\r\n"
18187        );
18188        assert_eq!(
18189            f.run(&[b"JSON.GET", b"doc", b"$"]),
18190            bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
18191        );
18192
18193        // A path that does not parse is refused before the document is read, so
18194        // a key that is not there answers the same way.
18195        assert!(
18196            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
18197                .starts_with("-ERR")
18198        );
18199        assert!(
18200            f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
18201                .starts_with("-ERR")
18202        );
18203    }
18204
18205    /// The operators past the comparisons, over the wire rather than in the
18206    /// parser's own tests, so that a client can reach all of them.
18207    #[test]
18208    fn a_filter_takes_the_membership_operators_and_the_methods_too() {
18209        let mut f = Fixture::new();
18210        let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
18211        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
18212
18213        for (path, want) in [
18214            (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
18215            (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
18216            (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
18217            (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
18218            (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
18219            (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
18220            (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
18221            (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
18222            (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
18223            (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
18224            (b"$.box[?(@.n~)].t", "[]"),
18225            (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
18226            (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
18227            (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
18228            (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
18229        ] {
18230            assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
18231        }
18232
18233        // A write goes through one of these the same way it goes through a
18234        // comparison.
18235        assert_eq!(
18236            f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
18237            "+OK\r\n"
18238        );
18239        assert_eq!(
18240            f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
18241            bulk(r#"["b"]"#).as_str()
18242        );
18243    }
18244
18245    /// D-41. RedisJSON refuses this one, and which document it refuses is
18246    /// decided by how it happens to hold an array of numbers.
18247    #[test]
18248    fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
18249        let mut f = Fixture::new();
18250        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
18251        assert_eq!(
18252            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
18253            "+OK\r\n"
18254        );
18255        assert_eq!(
18256            f.run(&[b"JSON.GET", b"doc", b"$"]),
18257            bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
18258        );
18259        // The same document with one element that is not an integer is the one
18260        // RedisJSON is happy with, and it goes the same way here.
18261        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
18262        assert_eq!(
18263            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
18264            "+OK\r\n"
18265        );
18266        assert_eq!(
18267            f.run(&[b"JSON.GET", b"doc", b"$"]),
18268            bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
18269        );
18270    }
18271
18272    /// `JSON.MSET` checks what it can before it writes anything and skips the
18273    /// one thing it cannot, which is a path with nowhere to put its value.
18274    #[test]
18275    fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
18276        let mut f = Fixture::new();
18277        assert_eq!(
18278            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
18279            "+OK\r\n"
18280        );
18281        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
18282        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
18283
18284        // A repeated key takes the last write.
18285        assert_eq!(
18286            f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
18287            "+OK\r\n"
18288        );
18289        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
18290
18291        // A triple whose path names nowhere is skipped, the others are still
18292        // written and the reply turns into a nil. Both ways round, because a
18293        // loop that gave up at the first skip would agree with this on one
18294        // order and not on the other.
18295        f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
18296        assert_eq!(
18297            f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
18298            "$-1\r\n"
18299        );
18300        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
18301        assert_eq!(
18302            f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
18303            "$-1\r\n"
18304        );
18305        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
18306
18307        // A value that is not JSON, a key holding something else and a path
18308        // that would have to create a document below its own root are all
18309        // checked before anything is written, so the good triple next to them
18310        // does not happen either.
18311        f.run(&[b"SET", b"str", b"x"]);
18312        assert_eq!(
18313            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
18314            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
18315        );
18316        assert_eq!(
18317            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
18318            "-Existing key has wrong Redis type\r\n"
18319        );
18320        assert_eq!(
18321            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
18322            "-ERR new objects must be created at the root\r\n"
18323        );
18324        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
18325
18326        // The two errors a path can be are checked up front as well, so the
18327        // triple before them is not written either. A wildcard that matched
18328        // nothing has nowhere to invent, and an index that is not in the array
18329        // is out of range, and both of them stop the whole command.
18330        assert_eq!(
18331            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
18332            "-Err wrong static path\r\n"
18333        );
18334        assert_eq!(
18335            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
18336            "-ERR array index out of range\r\n"
18337        );
18338        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
18339
18340        // Every triple is worked out against the keyspace as the command found
18341        // it, so a second triple on the same key does not see the first one and
18342        // the last write is the one that stays.
18343        f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
18344        assert_eq!(
18345            f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
18346            "+OK\r\n"
18347        );
18348        assert_eq!(
18349            f.run(&[b"JSON.GET", b"c", b"$"]),
18350            bulk(r#"[{"n":3}]"#).as_str()
18351        );
18352
18353        // An argument count that is not a run of key, path and value is the
18354        // arity error rather than a syntax one.
18355        assert_eq!(
18356            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
18357            "-ERR wrong number of arguments for 'json.mset' command\r\n"
18358        );
18359    }
18360
18361    /// `JSON.RESP` hands back RESP types, and the marker element is what tells
18362    /// an empty array and an empty object apart.
18363    #[test]
18364    fn json_resp_answers_the_document_as_resp_types() {
18365        let mut f = Fixture::new();
18366        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
18367        assert_eq!(
18368            f.run(&[b"JSON.RESP", b"doc"]),
18369            "*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"
18370        );
18371        // A JSONPath wraps the same answer in one more array.
18372        assert_eq!(
18373            f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
18374            "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
18375        );
18376
18377        f.run(&[
18378            b"JSON.SET",
18379            b"doc",
18380            b"$",
18381            br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
18382        ]);
18383        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
18384        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
18385        // A double goes out as its text, so a client reads the same digits
18386        // `JSON.GET` would have given it.
18387        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
18388        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
18389        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
18390
18391        // A missing legacy path is an error, a missing JSONPath is an empty
18392        // array, and a key that is not there is a nil on either.
18393        assert_eq!(
18394            f.run(&[b"JSON.RESP", b"doc", b".nope"]),
18395            "-ERR Path does not exist\r\n"
18396        );
18397        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
18398        assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
18399        assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
18400    }
18401
18402    /// `JSON.DEBUG` answers a byte count that is this encoding's, so the test
18403    /// pins the shapes and that the two syntaxes agree rather than a number
18404    /// read off another server. That is D-42.
18405    #[test]
18406    fn json_debug_answers_a_byte_count_and_its_own_help() {
18407        let mut f = Fixture::new();
18408        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
18409        let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
18410        assert!(one.starts_with(':'), "{one}");
18411        assert_eq!(
18412            f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
18413            format!("*1\r\n{one}")
18414        );
18415        let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
18416        assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
18417
18418        // A key that is not there is a zero on a legacy path and an empty set
18419        // on a JSONPath, which is the one reader here that does not answer nil
18420        // for it.
18421        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
18422        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
18423        assert_eq!(
18424            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
18425            "-ERR Path does not exist\r\n"
18426        );
18427        assert_eq!(
18428            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
18429            "*0\r\n"
18430        );
18431
18432        assert_eq!(
18433            f.run(&[b"JSON.DEBUG", b"HELP"]),
18434            "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
18435             $34\r\nHELP                - this message\r\n"
18436        );
18437        assert_eq!(
18438            f.run(&[b"JSON.DEBUG", b"NOPE"]),
18439            "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
18440        );
18441        assert_eq!(
18442            f.run(&[b"JSON.DEBUG", b"MEMORY"]),
18443            "-ERR wrong number of arguments for 'json.debug' command\r\n"
18444        );
18445    }
18446
18447    // ---------------------------------------------------------------- vector
18448
18449    /// The first `VADD` fixes the dimension and every one after it has to
18450    /// agree, because there is no create command to say it earlier.
18451    #[test]
18452    fn the_first_vadd_decides_how_wide_the_set_is() {
18453        let mut f = Fixture::new();
18454        assert_eq!(
18455            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
18456            ":1\r\n"
18457        );
18458        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
18459        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
18460        // A second vector under the same name replaces it and says so with a
18461        // zero, so an ingest can count what it created.
18462        assert_eq!(
18463            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
18464            ":0\r\n"
18465        );
18466        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
18467        // Three dimensions into a two dimensional set names both numbers, since
18468        // a client that gets this wrong needs to know which end is which.
18469        assert_eq!(
18470            f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
18471            "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
18472        );
18473        // A vector of zeros has no direction, and it is taken anyway and comes
18474        // back as the origin, because that is what a real server does with it.
18475        assert_eq!(
18476            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
18477            ":1\r\n"
18478        );
18479        assert_eq!(
18480            f.run(&[b"VEMB", b"v", b"nowhere"]),
18481            "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
18482        );
18483        // A set is made with one quantisation and keeps it, and a `VADD` that
18484        // names another is refused. Naming none names `Q8`, which is why this
18485        // set is a `Q8` one.
18486        assert_eq!(
18487            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
18488            "-ERR asked quantization mismatch with existing vector set\r\n"
18489        );
18490        // Nothing above created a key, and a set that never took a vector has
18491        // no dimension to report.
18492        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
18493        assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
18494        assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
18495    }
18496
18497    /// What a client sent comes back out, and what a client asked for is a
18498    /// similarity and not the distance underneath it.
18499    #[test]
18500    fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
18501        let mut f = Fixture::new();
18502        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
18503        // The set stored the direction and the length is multiplied back on the
18504        // way out, so this is `3 4` and not `0.6 0.8`. It is not quite `3 4`
18505        // either, because nobody named a quantisation and that means `Q8`: the
18506        // wider coordinate lands on a code exactly and the other one does not.
18507        // Both numbers are a real server's answers for the same input.
18508        assert_eq!(
18509            f.run(&[b"VEMB", b"v", b"a"]),
18510            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
18511        );
18512        // NOQUANT is the way to ask for what went in to come back out.
18513        f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
18514        assert_eq!(
18515            f.run(&[b"VEMB", b"n", b"a"]),
18516            "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
18517        );
18518        // BIN keeps the signs and nothing else, and does not multiply the
18519        // length back on, since a sign has no length in it to scale.
18520        f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
18521        assert_eq!(
18522            f.run(&[b"VEMB", b"b", b"a"]),
18523            "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
18524        );
18525        assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
18526        assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
18527
18528        // On the axes, where the unit vector is exact and so is the dot
18529        // product, both ends of the scale come out exact: the same direction is
18530        // 1 and the opposite one is 0, with a right angle at a half.
18531        let mut f = Fixture::new();
18532        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
18533        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
18534        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
18535        assert_eq!(
18536            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
18537            "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
18538             $8\r\nopposite\r\n$1\r\n0\r\n"
18539        );
18540        // A search from an element leaves that element out, since it is always
18541        // its own nearest neighbour.
18542        assert_eq!(
18543            f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
18544            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
18545        );
18546        // An element that is not there is an empty answer and not an error,
18547        // which is what a missing key gives too.
18548        assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
18549        assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
18550        // COUNT bounds it and TRUTH reads every vector rather than the codes,
18551        // which has to agree with the index on a set this small.
18552        assert_eq!(
18553            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
18554            "*1\r\n$6\r\nacross\r\n"
18555        );
18556        assert_eq!(
18557            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
18558            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
18559        );
18560        // EF widens how much of the index is read and does not change how many
18561        // answers come back, so a wide search still returns what COUNT asked
18562        // for.
18563        assert_eq!(
18564            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
18565            "*1\r\n$6\r\nacross\r\n"
18566        );
18567
18568        // On RESP3 a scored search is a map, which is what the vector set
18569        // module replies and is not what ZRANGE does here.
18570        let mut g = Fixture::new();
18571        g.run(&[b"HELLO", b"3"]);
18572        g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
18573        assert_eq!(
18574            g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
18575            "%1\r\n$4\r\neast\r\n,1\r\n"
18576        );
18577    }
18578
18579    /// The attribute pair, and the one reply that means two things.
18580    #[test]
18581    fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
18582        let mut f = Fixture::new();
18583        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
18584        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
18585        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
18586        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
18587        // Not parsed as JSON, because nothing reads into it yet and refusing a
18588        // write for a rule nothing enforces would be the wrong trade.
18589        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
18590        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
18591        // An empty string clears it, which is Redis's spelling of the removal.
18592        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
18593        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
18594        // An element that is not there answers zero rather than being created,
18595        // since an attribute with no vector under it is not a thing this holds.
18596        assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
18597        assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
18598        assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
18599        // A null for an element with no attribute and a null for one that is
18600        // not there. VISMEMBER is how a client tells the two apart.
18601        assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
18602        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
18603        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
18604        assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
18605
18606        // WITHATTRIBS carries it alongside the answers.
18607        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
18608        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
18609        assert_eq!(
18610            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
18611            "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
18612        );
18613    }
18614
18615    /// The slot a removed element had is reused, and nothing that was beside it
18616    /// comes back with the next element to get it.
18617    #[test]
18618    fn vrem_takes_the_attribute_with_it() {
18619        let mut f = Fixture::new();
18620        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
18621        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
18622        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
18623        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
18624        assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
18625        // The key went with the last element, the way every other collection
18626        // here works.
18627        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
18628
18629        // The next element is given the slot the removed one had, and it comes
18630        // with no attribute on it.
18631        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
18632        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
18633        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
18634        f.run(&[b"VREM", b"v", b"east"]);
18635        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
18636        assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
18637    }
18638
18639    /// `VINFO` says what the index is before it says anything a client could
18640    /// mistake for a graph.
18641    #[test]
18642    fn vinfo_says_partition_first() {
18643        let mut f = Fixture::new();
18644        f.run(&[
18645            b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
18646        ]);
18647        f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
18648        let info = f.run(&[b"VINFO", b"v"]);
18649        assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
18650        // What the client asked for and not what happened to the tuning, which
18651        // is `10` section 7: M is recorded and changes nothing.
18652        assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
18653        assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
18654        assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
18655        // Nobody named a quantisation, so this set is a `Q8` one and every
18656        // element in it is stored that way.
18657        assert!(
18658            info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
18659            "{info}"
18660        );
18661        let mut f = Fixture::new();
18662        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
18663        assert!(
18664            f.run(&[b"VINFO", b"v"])
18665                .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
18666        );
18667        assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
18668    }
18669
18670    /// A set to read ranges of names out of.
18671    fn named() -> Fixture {
18672        let mut f = Fixture::new();
18673        for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
18674            .iter()
18675            .enumerate()
18676        {
18677            let x = (i + 1).to_string();
18678            f.run(&[
18679                b"VADD",
18680                b"r",
18681                b"VALUES",
18682                b"2",
18683                x.as_bytes(),
18684                b"1",
18685                name.as_bytes(),
18686            ]);
18687        }
18688        f
18689    }
18690
18691    /// `VRANGE` reads the names in the order bytes come in and pays no
18692    /// attention to where the vectors point.
18693    #[test]
18694    fn vrange_walks_the_names_and_not_the_vectors() {
18695        let mut f = named();
18696        assert_eq!(
18697            f.run(&[b"VRANGE", b"r", b"-", b"+"]),
18698            "*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"
18699        );
18700        assert_eq!(
18701            f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
18702            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
18703            "the high end is a name and not a prefix, so delta is past it"
18704        );
18705        assert_eq!(
18706            f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
18707            "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
18708        );
18709        assert_eq!(
18710            f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
18711            "*1\r\n$4\r\nbeta\r\n"
18712        );
18713        assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
18714        // Bytes and not letters, so an upper case name sorts before every lower
18715        // case one rather than beside its own spelling.
18716        f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
18717        assert_eq!(
18718            f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
18719            "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
18720        );
18721        assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
18722    }
18723
18724    /// The count cuts the answer after the range is decided, and zero is not
18725    /// the same as leaving it out.
18726    #[test]
18727    fn a_vrange_count_of_zero_asks_for_nothing() {
18728        let mut f = named();
18729        assert_eq!(
18730            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
18731            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
18732        );
18733        assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
18734        assert!(
18735            f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
18736                .starts_with("*5\r\n"),
18737            "a negative count is no limit at all"
18738        );
18739    }
18740
18741    /// Both ends are read before either is placed, and the count is read before
18742    /// either end.
18743    #[test]
18744    fn vrange_says_which_end_it_could_not_read() {
18745        let mut f = named();
18746        assert_eq!(
18747            f.run(&[b"VRANGE", b"r", b"x", b"y"]),
18748            "-ERR invalid start range format\r\n"
18749        );
18750        assert_eq!(
18751            f.run(&[b"VRANGE", b"r", b"+", b"x"]),
18752            "-ERR invalid end range format\r\n",
18753            "the high end is spelled wrong, which is worth saying before the \
18754             low end being on the wrong side"
18755        );
18756        assert_eq!(
18757            f.run(&[b"VRANGE", b"r", b"+", b"-"]),
18758            "-ERR '-' can only be used as first argument, '+' only as second\r\n"
18759        );
18760        // A bracket with nothing after it is not the empty name here, though an
18761        // element really can be called that.
18762        assert_eq!(
18763            f.run(&[b"VRANGE", b"r", b"[", b"+"]),
18764            "-ERR invalid start range format\r\n"
18765        );
18766        assert_eq!(
18767            f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
18768            "-ERR invalid COUNT value\r\n"
18769        );
18770        assert_eq!(
18771            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
18772            "-ERR wrong number of arguments for 'VRANGE' command\r\n"
18773        );
18774        f.run(&[b"SET", b"s", b"x"]);
18775        assert!(
18776            f.run(&[b"VRANGE", b"s", b"-", b"+"])
18777                .starts_with("-WRONGTYPE")
18778        );
18779    }
18780
18781    /// The option that asks for something this index does not have says so
18782    /// rather than doing something else quietly.
18783    #[test]
18784    fn reduce_is_refused_and_not_ignored() {
18785        let mut f = Fixture::new();
18786        let reduce = f.run(&[
18787            b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
18788        ]);
18789        assert!(
18790            reduce.starts_with("-ERR REDUCE is not supported."),
18791            "{reduce}"
18792        );
18793        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
18794    }
18795
18796    /// A filtered search answers with the nearest elements that match, and an
18797    /// expression that is not one is an error before the key is looked at.
18798    #[test]
18799    fn vsim_filter_reads_the_attributes() {
18800        let mut f = Fixture::new();
18801        for (name, x, y, attr) in [
18802            ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
18803            ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
18804            ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
18805            ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
18806        ] {
18807            f.run(&[
18808                b"VADD",
18809                b"v",
18810                b"VALUES",
18811                b"2",
18812                x.as_bytes(),
18813                y.as_bytes(),
18814                name.as_bytes(),
18815                b"SETATTR",
18816                attr.as_bytes(),
18817            ]);
18818        }
18819        // `b` is the nearest to the query and is the one the filter drops, so
18820        // this is the answer a filter applied afterwards would have got wrong.
18821        assert_eq!(
18822            f.run(&[
18823                b"VSIM",
18824                b"v",
18825                b"VALUES",
18826                b"2",
18827                b"9",
18828                b"1",
18829                b"COUNT",
18830                b"2",
18831                b"FILTER",
18832                b".lang == \"en\"",
18833            ]),
18834            "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
18835        );
18836        // A number is compared as a number, and the two halves of an `and` both
18837        // have to hold.
18838        assert_eq!(
18839            f.run(&[
18840                b"VSIM",
18841                b"v",
18842                b"VALUES",
18843                b"2",
18844                b"9",
18845                b"1",
18846                b"FILTER",
18847                b".lang == 'en' and .year > 1980",
18848            ]),
18849            "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
18850        );
18851        // A list, and a field an element does not have.
18852        assert_eq!(
18853            f.run(&[
18854                b"VSIM",
18855                b"v",
18856                b"VALUES",
18857                b"2",
18858                b"9",
18859                b"1",
18860                b"FILTER",
18861                b".lang in ['fr', 'de']",
18862            ]),
18863            "*1\r\n$1\r\nb\r\n"
18864        );
18865        assert_eq!(
18866            f.run(&[
18867                b"VSIM",
18868                b"v",
18869                b"VALUES",
18870                b"2",
18871                b"9",
18872                b"1",
18873                b"FILTER",
18874                b".rating > 3"
18875            ]),
18876            "*0\r\n"
18877        );
18878        // TRUTH measures every vector, and the filter still decides which ones
18879        // are measured.
18880        assert_eq!(
18881            f.run(&[
18882                b"VSIM",
18883                b"v",
18884                b"VALUES",
18885                b"2",
18886                b"9",
18887                b"1",
18888                b"TRUTH",
18889                b"FILTER",
18890                b".year < 1980",
18891            ]),
18892            "*1\r\n$1\r\nc\r\n"
18893        );
18894        // VSETATTR moves an element in and out of a filter, which means the tag
18895        // beside its code was rewritten and not just the string.
18896        f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
18897        assert_eq!(
18898            f.run(&[
18899                b"VSIM",
18900                b"v",
18901                b"VALUES",
18902                b"2",
18903                b"9",
18904                b"1",
18905                b"COUNT",
18906                b"1",
18907                b"FILTER",
18908                b".lang == \"en\"",
18909            ]),
18910            "*1\r\n$1\r\nb\r\n"
18911        );
18912        // And a VADD that replaces the vector keeps the attribute and the tag,
18913        // which is the same rewrite from the other end.
18914        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
18915        assert_eq!(
18916            f.run(&[
18917                b"VSIM",
18918                b"v",
18919                b"VALUES",
18920                b"2",
18921                b"9",
18922                b"1",
18923                b"COUNT",
18924                b"1",
18925                b"FILTER",
18926                b".lang == \"en\"",
18927            ]),
18928            "*1\r\n$1\r\nb\r\n"
18929        );
18930
18931        // The expression is parsed before the key is read, so a bad one is an
18932        // error whether or not the key is there.
18933        let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
18934        assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
18935        assert_eq!(
18936            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
18937            "-ERR invalid FILTER expression\r\n"
18938        );
18939        // FILTER-EF raises the effort rather than capping it, and zero is
18940        // Redis's word for no limit, so neither is an error.
18941        assert_eq!(
18942            f.run(&[
18943                b"VSIM",
18944                b"v",
18945                b"VALUES",
18946                b"2",
18947                b"9",
18948                b"1",
18949                b"COUNT",
18950                b"1",
18951                b"FILTER-EF",
18952                b"500",
18953                b"FILTER",
18954                b".lang == 'en'",
18955            ]),
18956            "*1\r\n$1\r\nb\r\n"
18957        );
18958        assert_eq!(
18959            f.run(&[
18960                b"VSIM",
18961                b"v",
18962                b"VALUES",
18963                b"2",
18964                b"9",
18965                b"1",
18966                b"COUNT",
18967                b"1",
18968                b"FILTER-EF",
18969                b"0"
18970            ]),
18971            "*1\r\n$1\r\nb\r\n"
18972        );
18973        assert_eq!(
18974            f.run(&[
18975                b"VSIM",
18976                b"v",
18977                b"VALUES",
18978                b"2",
18979                b"9",
18980                b"1",
18981                b"FILTER-EF",
18982                b"lots"
18983            ]),
18984            "-ERR EF must be a positive integer\r\n"
18985        );
18986    }
18987
18988    /// A vector set key is a key, so the keyspace owns it the way it owns every
18989    /// other one and none of those commands know what is inside it.
18990    #[test]
18991    fn the_keyspace_sees_a_vector_set_key_like_any_other() {
18992        let mut f = Fixture::new();
18993        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
18994        assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
18995        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
18996        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
18997        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
18998        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
18999        assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
19000        assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
19001        assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
19002        assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
19003        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
19004
19005        // And the wrong type is the wrong type in both directions.
19006        f.run(&[b"SET", b"s", b"1"]);
19007        assert_eq!(
19008            f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
19009            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19010        );
19011        assert_eq!(
19012            f.run(&[b"VCARD", b"s"]),
19013            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19014        );
19015        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
19016        assert_eq!(
19017            f.run(&[b"GET", b"v"]),
19018            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19019        );
19020        // A graph and a vector set share the escape in the record tag and are
19021        // still two different types, which is the case the tag alone cannot
19022        // decide.
19023        f.run(&[b"G.NADD", b"social", b"ada"]);
19024        assert_eq!(
19025            f.run(&[b"VCARD", b"social"]),
19026            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19027        );
19028        assert_eq!(
19029            f.run(&[b"G.NGET", b"v", b"ada"]),
19030            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19031        );
19032    }
19033
19034    /// `VRANDMEMBER` is `SRANDMEMBER` over the element names, in both of its
19035    /// shapes, off the database's own generator.
19036    #[test]
19037    fn vrandmember_has_the_two_shapes_srandmember_has() {
19038        let mut f = Fixture::new();
19039        for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
19040            let x = (i + 1).to_string();
19041            f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
19042        }
19043        // One element is a bulk string and not an array of one.
19044        let one = f.run(&[b"VRANDMEMBER", b"v"]);
19045        assert!(one.starts_with("$1\r\n"), "{one}");
19046        // A positive count is distinct and stops at the size of the set.
19047        let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
19048        assert!(all.starts_with("*3\r\n"), "{all}");
19049        for name in ["a", "b", "c"] {
19050            assert!(all.contains(name), "{all} is missing {name}");
19051        }
19052        all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
19053        assert!(all.starts_with("*2\r\n"), "{all}");
19054        // A negative one draws that many and allows repeats.
19055        let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
19056        assert!(many.starts_with("*5\r\n"), "{many}");
19057        // A key that is not there answers the shape that was asked for.
19058        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
19059        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
19060    }
19061
19062    /// `VLINKS` answers about the index that is here rather than the graph that
19063    /// is not, which is D-2.
19064    #[test]
19065    fn vlinks_reports_one_layer_of_partition_neighbours() {
19066        let mut f = Fixture::new();
19067        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
19068        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
19069        // One layer deep, because the index is one layer deep, so a client
19070        // walking layers gets a short list and not a shape it cannot parse.
19071        assert_eq!(
19072            f.run(&[b"VLINKS", b"v", b"east"]),
19073            "*1\r\n*1\r\n$5\r\nnorth\r\n"
19074        );
19075        assert_eq!(
19076            f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
19077            "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
19078        );
19079        assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
19080        assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
19081    }
19082
19083    /// A vector arrives either as digits or as bytes, and the two have to mean
19084    /// the same thing.
19085    #[test]
19086    fn fp32_and_values_are_the_same_vector() {
19087        let mut f = Fixture::new();
19088        let mut blob = Vec::new();
19089        for x in [3.0f32, 4.0] {
19090            blob.extend_from_slice(&x.to_le_bytes());
19091        }
19092        assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
19093        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
19094        assert_eq!(
19095            f.run(&[b"VEMB", b"v", b"a"]),
19096            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
19097        );
19098        // RAW is the stored bytes and the numbers that turn them back into the
19099        // client's vector, which for `Q8` is a code a coordinate, the length the
19100        // vector arrived with and the scale the codes are measured against. The
19101        // name of the form is a simple string, which is a real server's shape,
19102        // and all four of these are a real server's answers.
19103        assert_eq!(
19104            f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
19105            "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
19106        );
19107        // A blob that is not a whole number of floats is not a vector.
19108        assert_eq!(
19109            f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
19110            "-ERR invalid vector specification\r\n"
19111        );
19112        // Neither is a count that promises more than arrived.
19113        assert_eq!(
19114            f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
19115            "-ERR syntax error\r\n"
19116        );
19117        assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
19118    }
19119
19120    // ----------------------------------------------------------------- bloom
19121
19122    /// The filter a client gets when it does not describe one, and the two
19123    /// answers an add can give.
19124    #[test]
19125    fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
19126        let mut f = Fixture::new();
19127        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
19128        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
19129        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
19130        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
19131        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
19132        // The defaults are the module's configs and not anything the command
19133        // said, which is 100 entries at a hundredth and a growth of 2.
19134        assert_eq!(
19135            f.run(&[b"BF.INFO", b"b"]),
19136            "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
19137             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
19138             +Expansion rate\r\n:2\r\n"
19139        );
19140        assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
19141        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
19142        // A key that is not there has no filter to report on, and answers two
19143        // different ways about it depending on which command asked.
19144        assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
19145        assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
19146    }
19147
19148    /// `BF.EXISTS` on a key holding something else answers a miss, and
19149    /// everything else in the family answers `WRONGTYPE`.
19150    ///
19151    /// The two halves of a check and set disagree about what that key is, which
19152    /// is the module's behaviour and not a decision taken here.
19153    #[test]
19154    fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
19155        let mut f = Fixture::new();
19156        f.run(&[b"SET", b"s", b"text"]);
19157        assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
19158        assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
19159        for cmd in [
19160            vec![&b"BF.ADD"[..], b"s", b"x"],
19161            vec![&b"BF.MADD"[..], b"s", b"x"],
19162            vec![&b"BF.CARD"[..], b"s"],
19163            vec![&b"BF.INFO"[..], b"s"],
19164            vec![&b"BF.DEBUG"[..], b"s"],
19165            vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
19166        ] {
19167            let name = String::from_utf8_lossy(cmd[0]).into_owned();
19168            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
19169        }
19170        // The arguments are read before the key is, so a reserve with a bad
19171        // error rate complains about the rate and never learns about the string.
19172        assert_eq!(
19173            f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
19174            "-ERR bad error rate\r\n"
19175        );
19176        assert!(
19177            f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
19178                .starts_with("-WRONGTYPE")
19179        );
19180    }
19181
19182    /// A chain grows by its expansion factor and each link is half as wrong as
19183    /// the one before, which is what makes the whole filter hold its rate.
19184    #[test]
19185    fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
19186        let mut f = Fixture::new();
19187        assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
19188        for i in 0..10u32 {
19189            assert_eq!(
19190                f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
19191                ":1\r\n"
19192            );
19193        }
19194        assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
19195        assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
19196        assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
19197        // Capacity is the sum of every link and not the number that was asked
19198        // for, so it is 10 and then 10 plus 20.
19199        assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
19200        assert_eq!(
19201            f.run(&[b"BF.DEBUG", b"g"]),
19202            "*3\r\n$7\r\nsize:11\r\n\
19203             $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
19204             $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
19205        );
19206
19207        // The same filter told not to grow fills instead.
19208        assert_eq!(
19209            f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
19210            "+OK\r\n"
19211        );
19212        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
19213        assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
19214        assert_eq!(
19215            f.run(&[b"BF.ADD", b"n", b"c"]),
19216            "-ERR non scaling filter is full\r\n"
19217        );
19218        // And an item that is already in it still answers, because membership
19219        // is checked before fullness.
19220        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
19221        // A filter that will not grow has no expansion rate to report, in
19222        // either of the two spellings that make one.
19223        assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
19224        f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
19225        assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
19226        // Asking for both at once is refused, which is one of the module's
19227        // errors that carries no prefix at all.
19228        assert_eq!(
19229            f.run(&[
19230                b"BF.RESERVE",
19231                b"q",
19232                b"0.01",
19233                b"2",
19234                b"NONSCALING",
19235                b"EXPANSION",
19236                b"2"
19237            ]),
19238            "-Nonscaling filters cannot expand\r\n"
19239        );
19240    }
19241
19242    /// A multi add stops where the filter did, so the reply can be shorter than
19243    /// the argument list.
19244    #[test]
19245    fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
19246        let mut f = Fixture::new();
19247        f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
19248        assert_eq!(
19249            f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
19250            "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
19251        );
19252        assert_eq!(
19253            f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
19254            "*2\r\n:1\r\n:0\r\n"
19255        );
19256    }
19257
19258    /// `BF.INSERT` describes a filter and fills it in one command, with its own
19259    /// spelling of every complaint.
19260    #[test]
19261    fn insert_is_a_reserve_and_a_madd_with_different_errors() {
19262        let mut f = Fixture::new();
19263        assert_eq!(
19264            f.run(&[
19265                b"BF.INSERT",
19266                b"i",
19267                b"CAPACITY",
19268                b"50",
19269                b"ERROR",
19270                b"0.001",
19271                b"ITEMS",
19272                b"a",
19273                b"b"
19274            ]),
19275            "*2\r\n:1\r\n:1\r\n"
19276        );
19277        assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
19278        // NOCREATE is the only way to add without making the key.
19279        assert_eq!(
19280            f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
19281            "-ERR not found\r\n"
19282        );
19283        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
19284        // The same mistakes as BF.RESERVE, in the sentences this command uses
19285        // for them, and one sentence where BF.RESERVE has two.
19286        assert_eq!(
19287            f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
19288            "-Bad capacity\r\n"
19289        );
19290        assert_eq!(
19291            f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
19292            "-Bad error rate\r\n"
19293        );
19294        assert_eq!(
19295            f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
19296            "-Bad expansion\r\n"
19297        );
19298        // An option is matched on its first letter and not on the word, so a
19299        // token nobody meant as an option is one anyway if it starts with the
19300        // right letter. NOSUCHTHING is NONSCALING here, and the filter it
19301        // builds says so.
19302        assert_eq!(
19303            f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
19304            "*1\r\n:1\r\n"
19305        );
19306        assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
19307        // Only E and N need a second look, one for ERROR against EXPANSION and
19308        // the other for NOCREATE against NONSCALING, and both stop as soon as
19309        // they can tell the two apart.
19310        assert_eq!(
19311            f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
19312            "*1\r\n:1\r\n"
19313        );
19314        assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
19315        assert_eq!(
19316            f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
19317            "*1\r\n:1\r\n"
19318        );
19319        assert_eq!(
19320            f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
19321            "-ERR not found\r\n"
19322        );
19323        // A letter that starts nothing is the one case that is refused.
19324        assert_eq!(
19325            f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
19326            "-Unknown argument received\r\n"
19327        );
19328        // Everything after ITEMS is an item, even when it spells an option.
19329        assert_eq!(
19330            f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
19331            "*1\r\n:1\r\n"
19332        );
19333        // And ITEMS with nothing after it is the same as leaving it out.
19334        assert!(
19335            f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
19336                .contains("wrong number of arguments")
19337        );
19338    }
19339
19340    /// A filter dumped a chunk at a time and put back into another key is the
19341    /// same filter.
19342    #[test]
19343    fn a_dump_replays_into_a_filter_that_answers_the_same() {
19344        let mut f = Fixture::new();
19345        f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
19346        for i in 0..25u32 {
19347            f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
19348        }
19349        assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
19350
19351        // Iterator zero asks for the header and every one after it is a running
19352        // byte offset, and a chunk never spans two links.
19353        let mut iter = b"0".to_vec();
19354        let mut chunks = 0;
19355        loop {
19356            let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
19357            let text = String::from_utf8_lossy(&raw).into_owned();
19358            let next = text
19359                .split("\r\n")
19360                .nth(1)
19361                .and_then(|n| n.strip_prefix(':'))
19362                .expect("a two element reply of an iterator and a chunk")
19363                .to_owned();
19364            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
19365            let data = &body[body
19366                .windows(2)
19367                .position(|w| w == b"\r\n")
19368                .expect("a length line")
19369                + 2..body.len() - 2];
19370            if next == "0" {
19371                assert!(data.is_empty(), "the last chunk is empty");
19372                break;
19373            }
19374            let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
19375            assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
19376            iter = next.into_bytes();
19377            chunks += 1;
19378        }
19379        assert_eq!(chunks, 3, "a header and one chunk per link");
19380
19381        assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
19382        assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
19383        for i in 0..25u32 {
19384            assert_eq!(
19385                f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
19386                ":1\r\n"
19387            );
19388        }
19389
19390        // A header on top of a filter is refused rather than merged, and so is
19391        // one that no filter wrote.
19392        assert_eq!(
19393            f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
19394            "-ERR received bad data\r\n"
19395        );
19396        assert_eq!(
19397            f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
19398            "-ERR received bad data\r\n"
19399        );
19400        // An offset past the end of the filter names itself.
19401        assert_eq!(
19402            f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
19403            "-ERR invalid offset - no link found\r\n"
19404        );
19405        assert_eq!(
19406            f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
19407            "-ERR Second argument must be numeric\r\n"
19408        );
19409        // The same complaint without the prefix on the way out, which is the
19410        // module's inconsistency and not a slip here.
19411        assert_eq!(
19412            f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
19413            "-Second argument must be numeric\r\n"
19414        );
19415    }
19416
19417    /// The argument checks, which have a sentence each and read numbers the way
19418    /// Redis reads them everywhere else.
19419    #[test]
19420    fn reserve_reads_its_numbers_the_way_string2ll_does() {
19421        let mut f = Fixture::new();
19422        for (args, want) in [
19423            (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
19424            (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
19425            (
19426                vec![&b"0"[..], b"10"],
19427                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
19428            ),
19429            (
19430                vec![&b"1"[..], b"10"],
19431                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
19432            ),
19433            (
19434                vec![&b"inf"[..], b"10"],
19435                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
19436            ),
19437            (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
19438            (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
19439            (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
19440            (
19441                vec![&b"0.01"[..], b"0"],
19442                "-ERR capacity must be in the range [1, 1073741824]\r\n",
19443            ),
19444            (
19445                vec![&b"0.01"[..], b"1073741825"],
19446                "-ERR capacity must be in the range [1, 1073741824]\r\n",
19447            ),
19448        ] {
19449            let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
19450            cmd.extend(args.iter().copied());
19451            assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
19452        }
19453        assert_eq!(
19454            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
19455            "-ERR no expansion\r\n"
19456        );
19457        assert_eq!(
19458            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
19459            "-ERR bad expansion\r\n"
19460        );
19461        assert_eq!(
19462            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
19463            "-ERR expansion must be in the range [0, 32768]\r\n"
19464        );
19465        // Trailing rubbish after the capacity is ignored rather than refused.
19466        assert_eq!(
19467            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
19468            "+OK\r\n"
19469        );
19470        assert_eq!(
19471            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
19472            "-ERR item exists\r\n"
19473        );
19474        assert_eq!(
19475            f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
19476            "-Invalid information value\r\n"
19477        );
19478        assert!(
19479            f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
19480                .contains("wrong number of arguments")
19481        );
19482    }
19483
19484    /// The RESP3 shapes, which are where this family differs most from RESP2.
19485    #[test]
19486    fn the_bloom_family_answers_in_resp3_spelling_too() {
19487        let mut f = Fixture::new();
19488        f.out.set_proto(Proto::Resp3);
19489        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
19490        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
19491        assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
19492        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
19493        assert_eq!(
19494            f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
19495            "*2\r\n#t\r\n#f\r\n"
19496        );
19497        // The count stays an integer, because it counts rather than answers.
19498        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
19499        assert_eq!(
19500            f.run(&[b"BF.INFO", b"b"]),
19501            "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
19502             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
19503             +Expansion rate\r\n:2\r\n"
19504        );
19505        // One field is a map of one here and a bare array of one on RESP2, so
19506        // this is the reply where the two protocols carry different facts.
19507        assert_eq!(
19508            f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
19509            "%1\r\n+Capacity\r\n:100\r\n"
19510        );
19511    }
19512
19513    // ---------------------------------------------------------------- cuckoo
19514
19515    /// A dump header, which is the four counts and the three widths a filter
19516    /// writes in front of its fingerprints.
19517    ///
19518    /// Written by hand rather than taken from a `CF.SCANDUMP`, because what the
19519    /// tests below want out of it is the states a filter cannot be put into
19520    /// from the wire.
19521    fn cf_header(
19522        items: u64,
19523        buckets: u64,
19524        deletes: u64,
19525        filters: u64,
19526        geometry: [u16; 3],
19527    ) -> Vec<u8> {
19528        let mut out = Vec::with_capacity(38);
19529        for n in [items, buckets, deletes, filters] {
19530            out.extend_from_slice(&n.to_le_bytes());
19531        }
19532        for n in geometry {
19533            out.extend_from_slice(&n.to_le_bytes());
19534        }
19535        out
19536    }
19537
19538    /// The filter a client gets when it does not describe one, and the thing a
19539    /// cuckoo filter does that a Bloom filter cannot, which is count copies and
19540    /// take them out again.
19541    #[test]
19542    fn cf_add_makes_the_filter_and_counts_the_copies() {
19543        let mut f = Fixture::new();
19544        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
19545        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
19546        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
19547        // The NX form is the one that looks first, which is why it is a command
19548        // of its own rather than an option.
19549        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
19550        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
19551        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
19552        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
19553        assert_eq!(
19554            f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
19555            "*2\r\n:1\r\n:0\r\n"
19556        );
19557        // The defaults are the module's configs: 1024 entries over buckets of
19558        // two, twenty kicks and a chain that grows by one.
19559        assert_eq!(
19560            f.run(&[b"CF.INFO", b"d"]),
19561            "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
19562             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
19563             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
19564             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
19565        );
19566        assert_eq!(
19567            f.run(&[b"CF.DEBUG", b"d"]),
19568            "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
19569             max_iterations:20 expansion:1\r\n"
19570        );
19571        assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
19572        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
19573
19574        // A delete takes one copy, so the same item goes twice and then stops.
19575        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
19576        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
19577        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
19578        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
19579        assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
19580
19581        // A key with no filter under it gets three different sentences and one
19582        // plain miss, depending on which command asked.
19583        assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
19584        assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
19585        assert_eq!(
19586            f.run(&[b"CF.COMPACT", b"gone"]),
19587            "-Cuckoo filter was not found\r\n"
19588        );
19589        assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
19590        // And `CF.COMPACT` is declared as taking any number of keys and takes
19591        // exactly one, which is the module's own arity being wrong rather than
19592        // this table's.
19593        assert!(
19594            f.run(&[b"CF.COMPACT", b"a", b"b"])
19595                .contains("wrong number of arguments")
19596        );
19597    }
19598
19599    /// The four that only read fingerprints treat a key holding something else
19600    /// as a key with no filter, and everything else answers `WRONGTYPE`.
19601    #[test]
19602    fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
19603        let mut f = Fixture::new();
19604        f.run(&[b"SET", b"s", b"text"]);
19605        assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
19606        assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
19607        assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
19608        // `CF.DEL` writes and is still in that group, and `CF.COMPACT` writes
19609        // and is declared read only, so neither of the two halves of the family
19610        // is the same set as the flags say.
19611        assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
19612        assert_eq!(
19613            f.run(&[b"CF.COMPACT", b"s"]),
19614            "-Cuckoo filter was not found\r\n"
19615        );
19616        for cmd in [
19617            vec![&b"CF.ADD"[..], b"s", b"x"],
19618            vec![&b"CF.ADDNX"[..], b"s", b"x"],
19619            vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
19620            vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
19621            vec![&b"CF.INFO"[..], b"s"],
19622            vec![&b"CF.DEBUG"[..], b"s"],
19623            vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
19624            vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
19625            vec![&b"CF.RESERVE"[..], b"s", b"64"],
19626        ] {
19627            let name = String::from_utf8_lossy(cmd[0]).into_owned();
19628            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
19629        }
19630    }
19631
19632    /// `CF.RESERVE` reads its options by name in an order of its own, and the
19633    /// first pair with a given name is the only one it looks at.
19634    #[test]
19635    fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
19636        let mut f = Fixture::new();
19637        assert_eq!(
19638            f.run(&[
19639                b"CF.RESERVE",
19640                b"r",
19641                b"64",
19642                b"BUCKETSIZE",
19643                b"1",
19644                b"MAXITERATIONS",
19645                b"7",
19646                b"EXPANSION",
19647                b"4"
19648            ]),
19649            "+OK\r\n"
19650        );
19651        assert_eq!(
19652            f.run(&[b"CF.DEBUG", b"r"]),
19653            "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
19654             max_iterations:7 expansion:4\r\n"
19655        );
19656        assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
19657
19658        assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
19659        assert_eq!(
19660            f.run(&[b"CF.RESERVE", b"q", b"1"]),
19661            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
19662        );
19663        // The range is the bucket size's and not a constant, so a capacity that
19664        // was fine at two slots a bucket is not at four.
19665        assert_eq!(
19666            f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
19667            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
19668        );
19669        assert_eq!(
19670            f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
19671            "+OK\r\n"
19672        );
19673
19674        // The capacity is checked last, so a command that is wrong twice
19675        // answers about the option. Which option it answers about is the order
19676        // the module looks for them in and not the order they were written, so
19677        // a bad kick budget wins over a bad bucket size wherever the two sit.
19678        assert_eq!(
19679            f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
19680            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
19681        );
19682        assert_eq!(
19683            f.run(&[
19684                b"CF.RESERVE",
19685                b"q2",
19686                b"64",
19687                b"EXPANSION",
19688                b"xx",
19689                b"BUCKETSIZE",
19690                b"0"
19691            ]),
19692            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
19693        );
19694        assert_eq!(
19695            f.run(&[
19696                b"CF.RESERVE",
19697                b"q2",
19698                b"64",
19699                b"MAXITERATIONS",
19700                b"0",
19701                b"BUCKETSIZE",
19702                b"0"
19703            ]),
19704            "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
19705        );
19706        // A second pair with a name that has already been read is not looked at
19707        // at all, so this one is a filter with buckets of one rather than an
19708        // error about a bucket size of zero.
19709        assert_eq!(
19710            f.run(&[
19711                b"CF.RESERVE",
19712                b"q3",
19713                b"64",
19714                b"BUCKETSIZE",
19715                b"1",
19716                b"BUCKETSIZE",
19717                b"0"
19718            ]),
19719            "+OK\r\n"
19720        );
19721        // A pair nobody knows is dropped, which is the opposite of what
19722        // `CF.INSERT` does with the same mistake.
19723        assert_eq!(
19724            f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
19725            "+OK\r\n"
19726        );
19727        assert_eq!(
19728            f.run(&[b"CF.DEBUG", b"q4"]),
19729            "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
19730             max_iterations:20 expansion:1\r\n"
19731        );
19732        // And an option with nothing after it leaves an odd number of them,
19733        // which is an arity error rather than a complaint about the option.
19734        assert!(
19735            f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
19736                .contains("wrong number of arguments")
19737        );
19738    }
19739
19740    /// `CF.INSERT` is a reserve and a multi add, with a grammar that agrees
19741    /// with `CF.RESERVE` about nothing.
19742    #[test]
19743    fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
19744        let mut f = Fixture::new();
19745        assert_eq!(
19746            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
19747            "*2\r\n:1\r\n:1\r\n"
19748        );
19749        assert_eq!(
19750            f.run(&[b"CF.DEBUG", b"i"]),
19751            "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
19752             max_iterations:20 expansion:1\r\n"
19753        );
19754        // The NX form has three answers rather than two, which is why it stays
19755        // integers on both protocols.
19756        assert_eq!(
19757            f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
19758            "*2\r\n:0\r\n:1\r\n"
19759        );
19760        assert_eq!(
19761            f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
19762            "-ERR not found\r\n"
19763        );
19764        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
19765
19766        assert_eq!(
19767            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
19768            "-Bad capacity\r\n"
19769        );
19770        // The bucket size cannot be given here, so the range names the config
19771        // that holds it instead of the option `CF.RESERVE` names.
19772        assert_eq!(
19773            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
19774            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
19775        );
19776        // Every occurrence is checked, which is where this differs from
19777        // `CF.RESERVE`: the second `CAPACITY` is an error even though the first
19778        // one is the one that would have been used.
19779        assert_eq!(
19780            f.run(&[
19781                b"CF.INSERT",
19782                b"i",
19783                b"CAPACITY",
19784                b"8",
19785                b"CAPACITY",
19786                b"2",
19787                b"ITEMS",
19788                b"a"
19789            ]),
19790            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
19791        );
19792        // An option is one letter and not a word, so `NOSUCH` is `NOCREATE` and
19793        // `ITEMSXYZ` is `ITEMS`, and only a letter that starts nothing is
19794        // refused.
19795        assert_eq!(
19796            f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
19797            "*1\r\n:1\r\n"
19798        );
19799        assert_eq!(
19800            f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
19801            "*1\r\n:1\r\n"
19802        );
19803        assert_eq!(
19804            f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
19805            "-Unknown argument received\r\n"
19806        );
19807        // Everything after ITEMS is an item, even when it spells an option.
19808        assert_eq!(
19809            f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
19810            "*1\r\n:1\r\n"
19811        );
19812        // And the two ways of sending no items at all are the same complaint.
19813        assert!(
19814            f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
19815                .contains("wrong number of arguments")
19816        );
19817        assert!(
19818            f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
19819                .contains("wrong number of arguments")
19820        );
19821    }
19822
19823    /// The two walls a filter can hit, which say different things and are not
19824    /// the same wall.
19825    #[test]
19826    fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
19827        let mut f = Fixture::new();
19828        f.run(&[
19829            b"CF.RESERVE",
19830            b"s",
19831            b"4",
19832            b"BUCKETSIZE",
19833            b"1",
19834            b"EXPANSION",
19835            b"0",
19836        ]);
19837        for i in 0..4u32 {
19838            assert_eq!(
19839                f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
19840                ":1\r\n"
19841            );
19842        }
19843        assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
19844        assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
19845        // The add commands say it in a sentence and the insert commands say it
19846        // in the array, one value per item, and the array is never short.
19847        assert_eq!(
19848            f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
19849            "*2\r\n:-1\r\n:-1\r\n"
19850        );
19851        assert_eq!(
19852            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
19853            "*2\r\n:0\r\n:-1\r\n"
19854        );
19855
19856        // A chain that is allowed to grow stops for a different reason, and the
19857        // count it stops at is the filter limit rather than the room: this one
19858        // gives up with three slots free. Loading a chain that already has
19859        // every filter it is allowed shows why, since it refuses an item
19860        // straight into an empty one.
19861        let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
19862        assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
19863        assert_eq!(
19864            f.run(&[b"CF.ADD", b"g", b"q"]),
19865            "-Maximum expansions reached\r\n"
19866        );
19867        assert_eq!(
19868            f.run(&[b"CF.INFO", b"g"]),
19869            "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
19870             +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
19871             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
19872             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
19873        );
19874    }
19875
19876    /// A filter dumped a chunk at a time and put back under another key is the
19877    /// same filter, and the headers that describe one nobody could build are
19878    /// refused on the way in.
19879    #[test]
19880    fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
19881        let mut f = Fixture::new();
19882        f.run(&[
19883            b"CF.RESERVE",
19884            b"src",
19885            b"8",
19886            b"BUCKETSIZE",
19887            b"2",
19888            b"EXPANSION",
19889            b"2",
19890        ]);
19891        for i in 0..40u32 {
19892            f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
19893        }
19894        // Position zero asks for the header and every one after it is a byte
19895        // offset across every filter laid end to end, and the walk ends on a
19896        // zero and a nil rather than an empty chunk.
19897        let mut pos = b"0".to_vec();
19898        let mut chunks = 0;
19899        loop {
19900            let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
19901            let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
19902            let next = head
19903                .split("\r\n")
19904                .nth(1)
19905                .and_then(|n| n.strip_prefix(':'))
19906                .expect("a two element reply of a position and a chunk")
19907                .to_owned();
19908            if next == "0" {
19909                assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
19910                break;
19911            }
19912            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
19913            let at = body
19914                .windows(2)
19915                .position(|w| w == b"\r\n")
19916                .expect("a length line")
19917                + 2;
19918            let data = &body[at..body.len() - 2];
19919            assert_eq!(
19920                f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
19921                "+OK\r\n",
19922                "loading chunk {chunks}"
19923            );
19924            pos = next.into_bytes();
19925            chunks += 1;
19926        }
19927        assert!(chunks >= 2, "a header and at least one chunk");
19928
19929        assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
19930        assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
19931        for i in 0..40u32 {
19932            assert_eq!(
19933                f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
19934                ":1\r\n"
19935            );
19936        }
19937
19938        // A filter with nothing in it hands out no header at all, so a client
19939        // that dumps one has nothing to load back.
19940        f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
19941        assert_eq!(
19942            f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
19943            "*2\r\n:0\r\n$-1\r\n"
19944        );
19945
19946        // The positions this end will not take, which are not the same set at
19947        // both ends: a dump refuses a negative one and a load takes it as an
19948        // offset and fails to find anything there.
19949        assert_eq!(
19950            f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
19951            "-Invalid position\r\n"
19952        );
19953        assert_eq!(
19954            f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
19955            "-Invalid position\r\n"
19956        );
19957        assert_eq!(
19958            f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
19959            "-Invalid position\r\n"
19960        );
19961        assert_eq!(
19962            f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
19963            "-Couldn't load chunk!\r\n"
19964        );
19965        // A header on top of a filter is refused rather than merged.
19966        let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
19967        assert_eq!(
19968            f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
19969            "-ERR item exists\r\n"
19970        );
19971        // A chunk that is not the size of a header where a header should have
19972        // been is one sentence, and one that is the size of a header and
19973        // describes a filter nobody could build is another.
19974        assert_eq!(
19975            f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
19976            "-Invalid header\r\n"
19977        );
19978        for (why, bad) in [
19979            ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
19980            ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
19981            (
19982                "a bucket count that is not a power of two",
19983                cf_header(0, 3, 0, 1, [2, 20, 1]),
19984            ),
19985            ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
19986            ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
19987            (
19988                "a growth nobody could reach",
19989                cf_header(0, 8, 0, 1, [2, 20, 32769]),
19990            ),
19991            (
19992                "a chain that cannot grow and did",
19993                cf_header(0, 8, 0, 2, [2, 20, 0]),
19994            ),
19995            // The count is written in eight bytes and read into two, so a
19996            // number that is a multiple of the second arrives as none.
19997            (
19998                "a filter count that wraps",
19999                cf_header(0, 8, 0, 65_536, [2, 20, 1]),
20000            ),
20001        ] {
20002            assert_eq!(
20003                f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
20004                "-Couldn't create filter!\r\n",
20005                "{why}"
20006            );
20007        }
20008    }
20009
20010    /// The RESP3 shapes, which are where this family differs most from RESP2
20011    /// and where one of its answers stops being readable.
20012    #[test]
20013    fn the_cuckoo_family_answers_in_resp3_spelling_too() {
20014        let mut f = Fixture::new();
20015        f.out.set_proto(Proto::Resp3);
20016        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
20017        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
20018        assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
20019        assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
20020        assert_eq!(
20021            f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
20022            "*2\r\n#t\r\n#f\r\n"
20023        );
20024        assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
20025        assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
20026        // The count stays an integer, because it counts rather than answers.
20027        assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
20028        assert_eq!(
20029            f.run(&[b"CF.INFO", b"c"]),
20030            "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
20031             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
20032             +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
20033             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
20034        );
20035
20036        // `CF.INSERT` writes a boolean per item here and an integer per item on
20037        // RESP2, and minus one has nowhere to go in a boolean, so a RESP3
20038        // client cannot tell an item that did not fit from one that is already
20039        // there. `CF.INSERTNX` keeps its integers for exactly that reason.
20040        f.run(&[
20041            b"CF.RESERVE",
20042            b"s",
20043            b"4",
20044            b"BUCKETSIZE",
20045            b"1",
20046            b"EXPANSION",
20047            b"0",
20048        ]);
20049        assert_eq!(
20050            f.run(&[
20051                b"CF.INSERT",
20052                b"s",
20053                b"ITEMS",
20054                b"a",
20055                b"b",
20056                b"c",
20057                b"d",
20058                b"e",
20059                b"f"
20060            ]),
20061            "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
20062        );
20063        assert_eq!(
20064            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
20065            "*2\r\n:0\r\n:-1\r\n"
20066        );
20067        assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
20068        // The end of a dump is a nil and not an empty chunk, which is one
20069        // underscore here and a negative length on RESP2.
20070        assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
20071    }
20072
20073    // ------------------------------------------------------------------- cms
20074
20075    /// A sketch is made from either end, and both constructors look at the key
20076    /// before they look at their arguments.
20077    #[test]
20078    fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
20079        let mut f = Fixture::new();
20080        assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
20081        assert_eq!(
20082            f.run(&[b"CMS.INFO", b"d"]),
20083            "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
20084        );
20085        assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
20086        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
20087        // Two over the error rounded up, and the log of the probability over the
20088        // log of a half rounded up, which for these two is 200 by 6.
20089        assert_eq!(
20090            f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
20091            "+OK\r\n"
20092        );
20093        assert_eq!(
20094            f.run(&[b"CMS.INFO", b"p"]),
20095            "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
20096        );
20097        // The key is checked first, so a width of zero at a key that is already
20098        // there is about the key and not about the width.
20099        assert_eq!(
20100            f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
20101            "-CMS: key already exists\r\n"
20102        );
20103        assert_eq!(
20104            f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
20105            "-CMS: invalid width\r\n"
20106        );
20107        assert_eq!(
20108            f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
20109            "-CMS: invalid depth\r\n"
20110        );
20111        assert_eq!(
20112            f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
20113            "-CMS: invalid overestimation value\r\n"
20114        );
20115        assert_eq!(
20116            f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
20117            "-CMS: invalid prob value\r\n"
20118        );
20119        // A probability whose float conversion is zero has no depth, and a width
20120        // past a signed sixty four bit integer has no width, and both are the
20121        // same sentence.
20122        assert_eq!(
20123            f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
20124            "-CMS: invalid init arguments\r\n"
20125        );
20126        // And a sketch bigger than a gibibyte of counters is refused here where
20127        // the reference reserves address space nobody has touched, which is
20128        // D-47.
20129        assert_eq!(
20130            f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
20131            "-CMS: Insufficient memory to create the key\r\n"
20132        );
20133        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
20134    }
20135
20136    /// Every pair is parsed before any of them lands, the counters saturate,
20137    /// and the count is a signed total of what was asked for.
20138    #[test]
20139    fn increments_are_parsed_whole_and_the_counters_saturate() {
20140        let mut f = Fixture::new();
20141        f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
20142        assert_eq!(
20143            f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
20144            "*2\r\n:3\r\n:4\r\n"
20145        );
20146        // An item that is incremented twice in one call sees its own first
20147        // increment in the reply to the second.
20148        assert_eq!(
20149            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
20150            "*2\r\n:4\r\n:5\r\n"
20151        );
20152        // A bad number anywhere means nothing at all is applied.
20153        assert_eq!(
20154            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
20155            "-CMS: Cannot parse number\r\n"
20156        );
20157        assert_eq!(
20158            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
20159            "-CMS: Number cannot be negative\r\n"
20160        );
20161        assert_eq!(
20162            f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
20163            "*2\r\n:5\r\n:4\r\n"
20164        );
20165        // The counters stop at four billion and the item that stopped says so in
20166        // its own slot while the one beside it answers a number.
20167        f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
20168        assert_eq!(
20169            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
20170            "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
20171        );
20172        assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
20173        // The count is what was asked for rather than what landed, and it is
20174        // signed, so a big enough total comes back negative.
20175        f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
20176        f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
20177        f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
20178        assert_eq!(
20179            f.run(&[b"CMS.INFO", b"w"]),
20180            "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
20181        );
20182        // An odd number of arguments after the key is an arity error and not a
20183        // syntax one.
20184        assert!(
20185            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
20186                .contains("wrong number of arguments")
20187        );
20188        assert_eq!(
20189            f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
20190            "-CMS: key does not exist\r\n"
20191        );
20192        assert_eq!(
20193            f.run(&[b"CMS.QUERY", b"nope", b"a"]),
20194            "-CMS: key does not exist\r\n"
20195        );
20196    }
20197
20198    /// A merge overwrites its destination, and it is worked out in full before
20199    /// any of it is written.
20200    #[test]
20201    fn a_merge_lands_whole_or_not_at_all() {
20202        let mut f = Fixture::new();
20203        for name in [&b"m1"[..], b"m2", b"dst"] {
20204            f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
20205        }
20206        f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
20207        f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
20208        assert_eq!(
20209            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
20210            "+OK\r\n"
20211        );
20212        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
20213        // Overwritten and not added to, so the same merge twice is the same
20214        // answer twice.
20215        assert_eq!(
20216            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
20217            "+OK\r\n"
20218        );
20219        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
20220        assert_eq!(
20221            f.run(&[
20222                b"CMS.MERGE",
20223                b"dst",
20224                b"2",
20225                b"m1",
20226                b"m2",
20227                b"WEIGHTS",
20228                b"2",
20229                b"3"
20230            ]),
20231            "+OK\r\n"
20232        );
20233        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
20234        // A cell times a weight is checked wide rather than wrapped, so this is
20235        // a refusal and the destination is left exactly as it was.
20236        assert_eq!(
20237            f.run(&[
20238                b"CMS.MERGE",
20239                b"dst",
20240                b"1",
20241                b"m1",
20242                b"WEIGHTS",
20243                b"4611686018427387904"
20244            ]),
20245            "-CMS: MERGE overflow\r\n"
20246        );
20247        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
20248        // The destination comes first, then the count, then the layout, then the
20249        // weights, then the sources one at a time.
20250        f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
20251        assert_eq!(
20252            f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
20253            "-CMS: key does not exist\r\n"
20254        );
20255        assert_eq!(
20256            f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
20257            "-CMS: Number of keys must be positive\r\n"
20258        );
20259        assert_eq!(
20260            f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
20261            "-CMS: wrong number of keys\r\n"
20262        );
20263        assert_eq!(
20264            f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
20265            "-CMS: wrong number of keys/weights\r\n"
20266        );
20267        assert_eq!(
20268            f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
20269            "-CMS: width/depth is not equal\r\n"
20270        );
20271        assert_eq!(
20272            f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
20273            "-CMS: key does not exist\r\n"
20274        );
20275    }
20276
20277    /// A key holding anything else is `WRONGTYPE` to all six, and a key holding
20278    /// a sketch is refused by the two commands that would have to serialise it.
20279    #[test]
20280    fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
20281        let mut f = Fixture::new();
20282        f.run(&[b"SET", b"s", b"text"]);
20283        for cmd in [
20284            vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
20285            vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
20286            vec![&b"CMS.QUERY"[..], b"s", b"a"],
20287            vec![&b"CMS.INFO"[..], b"s"],
20288            vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
20289        ] {
20290            let name = String::from_utf8_lossy(cmd[0]).into_owned();
20291            let reply = f.run(&cmd);
20292            // The two constructors see the key before anything else and say so
20293            // in the module's own words, and the rest are `WRONGTYPE`.
20294            assert!(
20295                reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
20296                "{name}: {reply}"
20297            );
20298        }
20299        f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
20300        // Redis refuses to copy a module key that has no copy callback, and
20301        // these are its words rather than ours. `DUMP` is the other half of
20302        // D-48: the reference has a payload for one of these and we do not.
20303        assert_eq!(
20304            f.run(&[b"COPY", b"c", b"c2"]),
20305            "-ERR not supported for this module key\r\n"
20306        );
20307        assert_eq!(
20308            f.run(&[b"DUMP", b"c"]),
20309            "-ERR DUMP is not supported for this module key\r\n"
20310        );
20311        // A graph is nobody's module and keeps its own sentence.
20312        f.run(&[b"G.NADD", b"g", b"a"]);
20313        assert_eq!(
20314            f.run(&[b"COPY", b"g", b"g2"]),
20315            "-ERR COPY is not supported for a graph\r\n"
20316        );
20317        assert_eq!(
20318            f.run(&[b"DUMP", b"g"]),
20319            "-ERR DUMP is not supported for a graph\r\n"
20320        );
20321        // Everything that does not need a byte shape works on a sketch key the
20322        // way it works on any other.
20323        assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
20324        assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
20325        assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
20326        assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
20327        assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
20328    }
20329
20330    // ------------------------------------------------------------------ topk
20331
20332    /// `TOPK.RESERVE` takes three arguments or six, and looks at the key before
20333    /// it looks at any of them.
20334    #[test]
20335    fn a_reserve_takes_three_arguments_or_six() {
20336        let mut f = Fixture::new();
20337        assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
20338        assert_eq!(
20339            f.run(&[b"TOPK.INFO", b"t"]),
20340            "*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"
20341        );
20342        // Four arguments and five are an arity error rather than a defaulted
20343        // depth or decay.
20344        for cmd in [
20345            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
20346            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
20347        ] {
20348            assert!(f.run(&cmd).contains("wrong number of arguments"));
20349        }
20350        assert_eq!(
20351            f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
20352            "+OK\r\n"
20353        );
20354        // The key is checked first, so a reserve with nothing else right at a
20355        // key that is taken still says the key is taken.
20356        assert_eq!(
20357            f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
20358            "-TopK: key already exists\r\n"
20359        );
20360        assert_eq!(
20361            f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
20362            "-TopK: invalid k\r\n"
20363        );
20364        assert_eq!(
20365            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
20366            "-TopK: invalid width\r\n"
20367        );
20368        assert_eq!(
20369            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
20370            "-TopK: invalid depth\r\n"
20371        );
20372        // Zero is out and one is in, which is the module's `> 0` and `<= 1`.
20373        assert_eq!(
20374            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
20375            "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
20376        );
20377        assert_eq!(
20378            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
20379            "+OK\r\n"
20380        );
20381        // Past the cap, with the one sentence in the family that has a prefix.
20382        assert_eq!(
20383            f.run(&[
20384                b"TOPK.RESERVE",
20385                b"w",
20386                b"1",
20387                b"4294967295",
20388                b"4294967295",
20389                b"0.9"
20390            ]),
20391            "-ERR Insufficient memory to create topk data structure\r\n"
20392        );
20393    }
20394
20395    /// What the sketch keeps, and the three ways of asking about it.
20396    #[test]
20397    fn the_kept_set_is_what_query_and_list_answer_from() {
20398        let mut f = Fixture::new();
20399        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
20400        // A null an item while there is room, then the name of whatever was
20401        // pushed out.
20402        assert_eq!(
20403            f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
20404            "*2\r\n$-1\r\n$-1\r\n"
20405        );
20406        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
20407        // Two slots are full and `c` arrives with a count of one, which is not
20408        // under the smallest kept count, so it takes that slot straight away.
20409        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
20410        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
20411        assert_eq!(
20412            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
20413            "*3\r\n:1\r\n:0\r\n:1\r\n"
20414        );
20415        // The table still counts what the kept set let go of.
20416        assert_eq!(
20417            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
20418            "*3\r\n:11\r\n:1\r\n:6\r\n"
20419        );
20420        assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
20421        assert_eq!(
20422            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
20423            "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
20424        );
20425        // Any prefix of the keyword turns the counts on, the empty string
20426        // included, and only a longer word or a different one is refused.
20427        assert_eq!(
20428            f.run(&[b"TOPK.LIST", b"t", b"w"]),
20429            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
20430        );
20431        assert_eq!(
20432            f.run(&[b"TOPK.LIST", b"t", b""]),
20433            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
20434        );
20435        assert_eq!(
20436            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
20437            "-WITHCOUNT keyword expected\r\n"
20438        );
20439        // And the keyword is looked at before the key, so a missing key with a
20440        // bad keyword complains about the keyword.
20441        assert_eq!(
20442            f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
20443            "-WITHCOUNT keyword expected\r\n"
20444        );
20445        assert_eq!(
20446            f.run(&[b"TOPK.LIST", b"missing"]),
20447            "-TopK: key does not exist\r\n"
20448        );
20449        // An item counted zero times is kept and not listed.
20450        f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
20451        assert_eq!(
20452            f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
20453            "*1\r\n$-1\r\n"
20454        );
20455        assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
20456        assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
20457    }
20458
20459    /// `TOPK.INCRBY` applies as it goes, so a bad increment leaves everything
20460    /// before it counted, and the reply counts what it wrote.
20461    #[test]
20462    fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
20463        let mut f = Fixture::new();
20464        f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
20465        // Three pairs, the middle one bad: two elements come back, one of them
20466        // the error, and the array header says two rather than three. That last
20467        // part is D-51 and it is why a client here stays in step.
20468        assert_eq!(
20469            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
20470            format!(
20471                "*2\r\n$-1\r\n-{}\r\n",
20472                "TopK: increment must be an integer greater or equal to 0                            and smaller or equal to 100,000"
20473            )
20474        );
20475        assert_eq!(
20476            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
20477            "*3\r\n:3\r\n:0\r\n:0\r\n"
20478        );
20479        // A hundred thousand is in and one more is out.
20480        assert_eq!(
20481            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
20482            "*1\r\n$-1\r\n"
20483        );
20484        assert!(
20485            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
20486                .contains("smaller or equal to 100,000")
20487        );
20488        // Pairs have to be pairs.
20489        assert!(
20490            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
20491                .contains("wrong number of arguments")
20492        );
20493        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
20494    }
20495
20496    /// The RESP3 shapes, which are the two the protocols disagree about.
20497    #[test]
20498    fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
20499        let mut f = Fixture::new();
20500        f.run(&[b"HELLO", b"3"]);
20501        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
20502        f.run(&[b"TOPK.ADD", b"t", b"a"]);
20503        assert_eq!(
20504            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
20505            "*2\r\n#t\r\n#f\r\n"
20506        );
20507        // The count stays an integer on both protocols.
20508        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
20509        assert_eq!(
20510            f.run(&[b"TOPK.INFO", b"t"]),
20511            "%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"
20512        );
20513        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
20514    }
20515
20516    /// A top k key answers the module sentences the other sketch families
20517    /// answer, and its own word for its type.
20518    #[test]
20519    fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
20520        let mut f = Fixture::new();
20521        f.run(&[b"SET", b"s", b"text"]);
20522        for cmd in [
20523            vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
20524            vec![&b"TOPK.ADD"[..], b"s", b"a"],
20525            vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
20526            vec![&b"TOPK.QUERY"[..], b"s", b"a"],
20527            vec![&b"TOPK.COUNT"[..], b"s", b"a"],
20528            vec![&b"TOPK.LIST"[..], b"s"],
20529            vec![&b"TOPK.INFO"[..], b"s"],
20530        ] {
20531            let name = String::from_utf8_lossy(cmd[0]).into_owned();
20532            let reply = f.run(&cmd);
20533            assert!(
20534                reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
20535                "{name}: {reply}"
20536            );
20537        }
20538        f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
20539        assert_eq!(
20540            f.run(&[b"COPY", b"t", b"t2"]),
20541            "-ERR not supported for this module key\r\n"
20542        );
20543        assert_eq!(
20544            f.run(&[b"DUMP", b"t"]),
20545            "-ERR DUMP is not supported for this module key\r\n"
20546        );
20547        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
20548        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
20549        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
20550        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
20551        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
20552        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
20553        // Every one of the six that is not the constructor says the same thing
20554        // about a key that is not there.
20555        assert_eq!(
20556            f.run(&[b"TOPK.INFO", b"t3"]),
20557            "-TopK: key does not exist\r\n"
20558        );
20559    }
20560
20561    // --------------------------------------------------------------- tdigest
20562
20563    /// `TDIGEST.CREATE` takes two arguments or four, and the keyword search is a
20564    /// search rather than a lookup.
20565    #[test]
20566    fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
20567        let mut f = Fixture::new();
20568        assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
20569        // A hundred is the default and the capacity is six times it plus ten.
20570        assert_eq!(
20571            f.run(&[b"TDIGEST.INFO", b"t"]),
20572            "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
20573             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
20574             +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
20575        );
20576        assert_eq!(
20577            f.run(&[b"TDIGEST.CREATE", b"t"]),
20578            "-ERR T-Digest: key already exists\r\n"
20579        );
20580        // Three arguments is an arity error and not a missing keyword.
20581        assert!(
20582            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
20583                .contains("wrong number of arguments")
20584        );
20585        assert_eq!(
20586            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
20587            "+OK\r\n"
20588        );
20589        assert_eq!(
20590            f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
20591            "+OK\r\n"
20592        );
20593        // The word is looked for across both trailing arguments and the number
20594        // is then read out of the last one whatever was found, so this looks for
20595        // a number inside the word `COMPRESSION` and does not find one.
20596        assert_eq!(
20597            f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
20598            "-ERR T-Digest: error parsing compression parameter\r\n"
20599        );
20600        assert_eq!(
20601            f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
20602            "-ERR T-Digest: wrong keyword\r\n"
20603        );
20604        assert_eq!(
20605            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
20606            "-ERR T-Digest: error parsing compression parameter\r\n"
20607        );
20608        assert_eq!(
20609            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
20610            "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
20611        );
20612        // The reference's own ceiling, which is where the capacity stops fitting
20613        // in an int, and one past it.
20614        assert_eq!(
20615            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
20616            "-ERR T-Digest: allocation failed\r\n"
20617        );
20618        // And ours, which is a gibibyte of centroids and is D-52.
20619        assert_eq!(
20620            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
20621            "-ERR T-Digest: allocation failed\r\n"
20622        );
20623        // The key is checked before the arguments, so a bad compression at a key
20624        // that is already a digest still says the key is taken.
20625        assert_eq!(
20626            f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
20627            "-ERR T-Digest: key already exists\r\n"
20628        );
20629    }
20630
20631    /// The four samples every note about this family is written against, and the
20632    /// answers a real 8.10.1 gives for them.
20633    #[test]
20634    fn the_quantile_family_answers_what_the_module_answers() {
20635        let mut f = Fixture::new();
20636        f.run(&[b"TDIGEST.CREATE", b"s"]);
20637        assert_eq!(
20638            f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
20639            "+OK\r\n"
20640        );
20641        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
20642        assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
20643        // The cdf of a sample is the weight below it plus half its own.
20644        assert_eq!(
20645            f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
20646            "*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"
20647        );
20648        assert_eq!(
20649            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
20650            "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
20651        );
20652        // Out of order, the walk restarts, and 0.5 answers 3 either way while
20653        // the two after it are read from the front again.
20654        assert_eq!(
20655            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
20656            "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
20657        );
20658        assert_eq!(
20659            f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
20660            "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
20661        );
20662        assert_eq!(
20663            f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
20664            "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
20665        );
20666        assert_eq!(
20667            f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
20668            "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
20669        );
20670        assert_eq!(
20671            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
20672            "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
20673        );
20674        assert_eq!(
20675            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
20676            "$3\r\n2.5\r\n"
20677        );
20678        assert_eq!(
20679            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
20680            "$3\r\n2.5\r\n"
20681        );
20682        // The ranges, which are separate sentences from the parse failures.
20683        assert_eq!(
20684            f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
20685            "-ERR T-Digest: quantile should be in [0,1]\r\n"
20686        );
20687        assert_eq!(
20688            f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
20689            "-ERR T-Digest: error parsing quantile\r\n"
20690        );
20691        assert_eq!(
20692            f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
20693            "-ERR T-Digest: error parsing cdf\r\n"
20694        );
20695        assert_eq!(
20696            f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
20697            "-ERR T-Digest: error parsing value\r\n"
20698        );
20699        assert_eq!(
20700            f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
20701            "-ERR T-Digest: rank needs to be non negative\r\n"
20702        );
20703        assert_eq!(
20704            f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
20705            "-ERR T-Digest: error parsing rank\r\n"
20706        );
20707        // Both cuts have their own parse sentence and share the range one, and
20708        // equal cuts are refused rather than answering nothing.
20709        assert_eq!(
20710            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
20711            "-ERR T-Digest: error parsing low_cut_percentile\r\n"
20712        );
20713        assert_eq!(
20714            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
20715            "-ERR T-Digest: error parsing high_cut_percentile\r\n"
20716        );
20717        assert_eq!(
20718            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
20719            "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
20720        );
20721        assert_eq!(
20722            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
20723            "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
20724        );
20725    }
20726
20727    /// An empty digest answers every question, and answers most of them with
20728    /// something that is not a number.
20729    #[test]
20730    fn an_empty_digest_has_an_answer_for_everything() {
20731        let mut f = Fixture::new();
20732        f.run(&[b"TDIGEST.CREATE", b"e"]);
20733        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
20734        assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
20735        assert_eq!(
20736            f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
20737            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
20738        );
20739        assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
20740        assert_eq!(
20741            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
20742            "$3\r\nnan\r\n"
20743        );
20744        // Minus two, which is a number no rank on a digest with samples in it
20745        // can ever be.
20746        assert_eq!(
20747            f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
20748            "*2\r\n:-2\r\n:-2\r\n"
20749        );
20750        assert_eq!(
20751            f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
20752            "*2\r\n:-2\r\n:-2\r\n"
20753        );
20754        assert_eq!(
20755            f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
20756            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
20757        );
20758        // A reset puts a digest with samples back into exactly this state.
20759        f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
20760        assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
20761        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
20762        // Down to the compression count, so a reset digest and a fresh one of
20763        // the same compression report the same nine numbers.
20764        f.run(&[b"TDIGEST.CREATE", b"e2"]);
20765        assert_eq!(
20766            f.run(&[b"TDIGEST.INFO", b"e"]),
20767            f.run(&[b"TDIGEST.INFO", b"e2"])
20768        );
20769    }
20770
20771    /// The double parser is Redis's and not this engine's, and the two disagree
20772    /// at both ends of the range.
20773    #[test]
20774    fn a_sample_is_read_the_way_redis_reads_a_double() {
20775        let mut f = Fixture::new();
20776        f.run(&[b"TDIGEST.CREATE", b"a"]);
20777        // Overflow and underflow are parse failures rather than an infinity and
20778        // a zero, which is where this parts company with the rest of the engine.
20779        for bad in [
20780            &b"nan"[..],
20781            b"1e400",
20782            b"-1e400",
20783            b"1e309",
20784            b"1e-400",
20785            b"",
20786            b" 1",
20787            b"1 ",
20788            b"1e",
20789            b"--1",
20790        ] {
20791            assert_eq!(
20792                f.run(&[b"TDIGEST.ADD", b"a", bad]),
20793                "-ERR T-Digest: error parsing val parameter\r\n",
20794                "{}",
20795                String::from_utf8_lossy(bad)
20796            );
20797        }
20798        // An infinity spelled out parses and is then refused for being one, with
20799        // a different sentence.
20800        for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
20801            assert_eq!(
20802                f.run(&[b"TDIGEST.ADD", b"a", word]),
20803                "-ERR T-Digest: val parameter needs to be a finite number\r\n",
20804                "{}",
20805                String::from_utf8_lossy(word)
20806            );
20807        }
20808        // These all parse: hex, a bare point either side, and the smallest
20809        // subnormal the reference will take.
20810        for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
20811            assert_eq!(
20812                f.run(&[b"TDIGEST.ADD", b"a", good]),
20813                "+OK\r\n",
20814                "{}",
20815                String::from_utf8_lossy(good)
20816            );
20817        }
20818        // Nothing landed from the failures, so six samples is what there is.
20819        assert!(
20820            f.run(&[b"TDIGEST.INFO", b"a"])
20821                .contains("Observations\r\n:6\r\n")
20822        );
20823        // Every value is parsed before any is added, so this whole command is a
20824        // no op.
20825        assert_eq!(
20826            f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
20827            "-ERR T-Digest: error parsing val parameter\r\n"
20828        );
20829        assert!(
20830            f.run(&[b"TDIGEST.INFO", b"a"])
20831                .contains("Observations\r\n:6\r\n")
20832        );
20833    }
20834
20835    /// What a merge does to its destination, to its inputs and to the buffer
20836    /// split `TDIGEST.INFO` reports.
20837    #[test]
20838    fn a_merge_sweeps_the_destination_between_its_inputs() {
20839        let mut f = Fixture::new();
20840        f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
20841        f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
20842        f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
20843        f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
20844        assert_eq!(
20845            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
20846            "+OK\r\n"
20847        );
20848        // The destination did not exist, so the compression is the largest of
20849        // the inputs. The three from the first input were swept in before the
20850        // three from the second arrived, which is the one visible effect of the
20851        // reference folding one input at a time.
20852        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
20853        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
20854        assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
20855        assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
20856        assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
20857        assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
20858        assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
20859        // Reading a source sweeps it too, so a merge writes to keys it only
20860        // reads from.
20861        assert!(
20862            f.run(&[b"TDIGEST.INFO", b"m1"])
20863                .contains("Merged nodes\r\n:3\r\n")
20864        );
20865        // Without OVERRIDE the destination joins its own inputs, so this takes
20866        // it to nine observations and keeps its own compression.
20867        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
20868        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
20869        assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
20870        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
20871        // With OVERRIDE the old destination is dropped and the compression goes
20872        // back to the largest of the inputs.
20873        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
20874        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
20875        assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
20876        assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
20877        // And COMPRESSION beats both.
20878        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
20879        assert!(
20880            f.run(&[b"TDIGEST.INFO", b"d"])
20881                .contains("Compression\r\n:500\r\n")
20882        );
20883        // Naming the destination as a source folds it in twice.
20884        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
20885        assert!(
20886            f.run(&[b"TDIGEST.INFO", b"d"])
20887                .contains("Observations\r\n:12\r\n")
20888        );
20889        // The arguments, in the order the reference checks them.
20890        assert_eq!(
20891            f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
20892            "-ERR T-Digest: error parsing numkeys\r\n"
20893        );
20894        assert_eq!(
20895            f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
20896            "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
20897        );
20898        assert!(
20899            f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
20900                .contains("wrong number of arguments")
20901        );
20902        assert!(
20903            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
20904                .contains("wrong number of arguments")
20905        );
20906        assert_eq!(
20907            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
20908            "-ERR T-Digest: wrong keyword\r\n"
20909        );
20910        // A source that is not there stops the whole thing, and the destination
20911        // is left as it was.
20912        assert_eq!(
20913            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
20914            "-ERR T-Digest: key does not exist\r\n"
20915        );
20916        assert!(
20917            f.run(&[b"TDIGEST.INFO", b"d"])
20918                .contains("Observations\r\n:12\r\n")
20919        );
20920        // A destination that is not there and is also named as a source is the
20921        // same sentence rather than an empty merge.
20922        assert_eq!(
20923            f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
20924            "-ERR T-Digest: key does not exist\r\n"
20925        );
20926    }
20927
20928    /// The RESP3 shapes, which are the two the protocols disagree about.
20929    #[test]
20930    fn a_digest_answers_doubles_and_a_map_on_resp3() {
20931        let mut f = Fixture::new();
20932        f.run(&[b"HELLO", b"3"]);
20933        f.run(&[b"TDIGEST.CREATE", b"s"]);
20934        f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
20935        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
20936        assert_eq!(
20937            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
20938            "*2\r\n,1\r\n,4\r\n"
20939        );
20940        assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
20941        // The two infinities and the NaN go out as the bare words.
20942        assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
20943        assert_eq!(
20944            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
20945            "*1\r\n,-inf\r\n"
20946        );
20947        f.run(&[b"TDIGEST.CREATE", b"e"]);
20948        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
20949        // The ranks stay integers on both protocols.
20950        assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
20951        // Every question above swept the buffer in, so the four samples are all
20952        // merged by now and the compression count says it happened once.
20953        assert_eq!(
20954            f.run(&[b"TDIGEST.INFO", b"s"]),
20955            "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
20956             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
20957             +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
20958        );
20959    }
20960
20961    /// A t digest key answers the module sentences the other sketch families
20962    /// answer, and its own word for its type.
20963    #[test]
20964    fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
20965        let mut f = Fixture::new();
20966        f.run(&[b"SET", b"s", b"text"]);
20967        for cmd in [
20968            vec![&b"TDIGEST.CREATE"[..], b"s"],
20969            vec![&b"TDIGEST.RESET"[..], b"s"],
20970            vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
20971            vec![&b"TDIGEST.MIN"[..], b"s"],
20972            vec![&b"TDIGEST.MAX"[..], b"s"],
20973            vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
20974            vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
20975            vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
20976            vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
20977            vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
20978            vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
20979            vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
20980            vec![&b"TDIGEST.INFO"[..], b"s"],
20981        ] {
20982            let name = String::from_utf8_lossy(cmd[0]).into_owned();
20983            let reply = f.run(&cmd);
20984            assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
20985        }
20986        // The merge checks its destination the same way, and its sources too.
20987        f.run(&[b"TDIGEST.CREATE", b"t"]);
20988        assert!(
20989            f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
20990                .starts_with("-WRONGTYPE")
20991        );
20992        assert!(
20993            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
20994                .starts_with("-WRONGTYPE")
20995        );
20996        assert_eq!(
20997            f.run(&[b"COPY", b"t", b"t2"]),
20998            "-ERR not supported for this module key\r\n"
20999        );
21000        assert_eq!(
21001            f.run(&[b"DUMP", b"t"]),
21002            "-ERR DUMP is not supported for this module key\r\n"
21003        );
21004        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
21005        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
21006        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
21007        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
21008        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
21009        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
21010        // An empty digest is still a key, so the twelve that are not the
21011        // constructor all say the same thing once it is gone.
21012        assert_eq!(
21013            f.run(&[b"TDIGEST.INFO", b"t3"]),
21014            "-ERR T-Digest: key does not exist\r\n"
21015        );
21016        // The key is looked at before the arguments, so a bad argument at a key
21017        // that is not there still says the key is not there.
21018        assert_eq!(
21019            f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
21020            "-ERR T-Digest: key does not exist\r\n"
21021        );
21022    }
21023
21024    // -------------------------------------------------------------------- ts
21025
21026    /// A `TS.INFO` reply with the memory usage taken out of it.
21027    ///
21028    /// That number is what a series costs here rather than what one costs in the
21029    /// module, which is D-53, and it moves whenever the layout of a chunk does.
21030    /// Everything either side of it is the wire contract and is worth pinning
21031    /// down exactly, so the tests below check the whole reply with the one
21032    /// number lifted out.
21033    fn without_memory(reply: &str) -> String {
21034        let head = "+memoryUsage\r\n:";
21035        let at = reply.find(head).expect("every TS.INFO reports memory");
21036        let rest = &reply[at + head.len()..];
21037        let end = rest.find("\r\n").expect("and it is a whole number");
21038        format!("{}{}", &reply[..at + head.len()], &rest[end..])
21039    }
21040
21041    /// A series is made empty and still says it has a chunk, and the options are
21042    /// read before the key is looked at.
21043    #[test]
21044    fn a_series_is_made_empty_and_reports_on_itself() {
21045        let mut f = Fixture::new();
21046        assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
21047        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
21048        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
21049        // Fourteen fields, so twenty eight elements. An empty series reports one
21050        // chunk and zero at both ends, and neither the chunk type nor the
21051        // duplicate policy is ever a nil.
21052        assert_eq!(
21053            without_memory(&f.run(&[b"TS.INFO", b"t"])),
21054            "*28\r\n\
21055             +totalSamples\r\n:0\r\n\
21056             +memoryUsage\r\n:\r\n\
21057             +firstTimestamp\r\n:0\r\n\
21058             +lastTimestamp\r\n:0\r\n\
21059             +retentionTime\r\n:0\r\n\
21060             +chunkCount\r\n:1\r\n\
21061             +chunkSize\r\n:4096\r\n\
21062             +chunkType\r\n+compressed\r\n\
21063             +duplicatePolicy\r\n+block\r\n\
21064             +labels\r\n*0\r\n\
21065             +sourceKey\r\n$-1\r\n\
21066             +rules\r\n*0\r\n\
21067             +ignoreMaxTimeDiff\r\n:0\r\n\
21068             +ignoreMaxValDiff\r\n$1\r\n0\r\n"
21069        );
21070        // A key that is already there is about the key whatever it holds, and
21071        // the existence is what is checked rather than the type.
21072        assert_eq!(
21073            f.run(&[b"TS.CREATE", b"t"]),
21074            "-ERR TSDB: key already exists\r\n"
21075        );
21076        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
21077        assert_eq!(
21078            f.run(&[b"TS.CREATE", b"str"]),
21079            "-ERR TSDB: key already exists\r\n"
21080        );
21081        // But the arguments are read first, so a bad one at a key that is there
21082        // answers about the argument.
21083        assert_eq!(
21084            f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
21085            "-ERR TSDB: Couldn't parse RETENTION\r\n"
21086        );
21087        // The seven that will not make a series say WRONGTYPE about a key
21088        // holding something else, where the two that would say a sentence.
21089        // The word is inside the sentence and not in front of it, because the
21090        // module writes its own error text and Redis puts ERR on the front of
21091        // anything a module writes.
21092        let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
21093        assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
21094        assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
21095        assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
21096        assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
21097        assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
21098        assert_eq!(
21099            f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
21100            "-ERR TSDB: the key is not a TSDB key\r\n"
21101        );
21102        // And the ones that will not make one say so about a key that is gone.
21103        assert_eq!(
21104            f.run(&[b"TS.INFO", b"nope"]),
21105            "-ERR TSDB: the key does not exist\r\n"
21106        );
21107        assert_eq!(
21108            f.run(&[b"TS.GET", b"nope"]),
21109            "-ERR TSDB: the key does not exist\r\n"
21110        );
21111        assert_eq!(
21112            f.run(&[b"TS.ALTER", b"nope"]),
21113            "-ERR TSDB: the key does not exist\r\n"
21114        );
21115        assert_eq!(
21116            f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
21117            "-ERR TSDB: the key does not exist\r\n"
21118        );
21119    }
21120
21121    /// Every option word, including the ones that are wrong, and the scan that
21122    /// finds them.
21123    #[test]
21124    fn the_options_are_a_keyword_scan_and_not_a_grammar() {
21125        let mut f = Fixture::new();
21126        assert_eq!(
21127            f.run(&[
21128                b"TS.CREATE",
21129                b"t",
21130                b"RETENTION",
21131                b"5000",
21132                b"ENCODING",
21133                b"UNCOMPRESSED",
21134                b"CHUNK_SIZE",
21135                b"128",
21136                b"DUPLICATE_POLICY",
21137                b"LAST",
21138                b"IGNORE",
21139                b"10",
21140                b"0.5",
21141                b"LABELS",
21142                b"room",
21143                b"kitchen"
21144            ]),
21145            "+OK\r\n"
21146        );
21147        let info = f.run(&[b"TS.INFO", b"t"]);
21148        assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
21149        assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
21150        assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
21151        assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
21152        assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
21153        // A plain double here, where a sample value out of TS.GET is the
21154        // shortest digits that read back as the same number.
21155        assert!(
21156            info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
21157            "{info}"
21158        );
21159        assert!(
21160            info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
21161            "{info}"
21162        );
21163
21164        // A word that is not an option is read past rather than refused.
21165        assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
21166        // LABELS eats everything after it in pairs, and the later scans still
21167        // look inside what it ate, so this sets a retention and stores a label
21168        // called RETENTION at the same time.
21169        assert_eq!(
21170            f.run(&[
21171                b"TS.CREATE",
21172                b"g",
21173                b"LABELS",
21174                b"a",
21175                b"b",
21176                b"RETENTION",
21177                b"5"
21178            ]),
21179            "+OK\r\n"
21180        );
21181        let greedy = f.run(&[b"TS.INFO", b"g"]);
21182        assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
21183        assert!(
21184            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"),
21185            "{greedy}"
21186        );
21187
21188        // Every way an option can be wrong, in the order the module reads them.
21189        assert_eq!(
21190            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
21191            "-ERR TSDB: Couldn't parse LABELS\r\n"
21192        );
21193        assert_eq!(
21194            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
21195            "-ERR TSDB: Couldn't parse LABELS\r\n"
21196        );
21197        assert_eq!(
21198            f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
21199            "-ERR TSDB: Couldn't parse RETENTION\r\n"
21200        );
21201        // A retention below zero is one of the two the module writes with no
21202        // ERR in front of it, where one that is not a number gets one.
21203        assert_eq!(
21204            f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
21205            "-TSDB: Couldn't parse RETENTION\r\n"
21206        );
21207        assert_eq!(
21208            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
21209            "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
21210        );
21211        assert_eq!(
21212            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
21213            "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
21214        );
21215        assert_eq!(
21216            f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
21217            "-ERR TSDB: unknown ENCODING parameter\r\n"
21218        );
21219        // And an ENCODING with nothing behind it is an arity error where every
21220        // other keyword in the same spot is a sentence.
21221        assert!(
21222            f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
21223                .contains("wrong number of arguments for 'ts.create' command")
21224        );
21225        assert_eq!(
21226            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
21227            "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
21228        );
21229        assert_eq!(
21230            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
21231            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
21232        );
21233        assert_eq!(
21234            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
21235            "-ERR TSDB: Couldn't parse IGNORE\r\n"
21236        );
21237        assert_eq!(
21238            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
21239            "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
21240        );
21241        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
21242
21243        // An alter changes what was named and leaves the rest alone, and reads
21244        // an encoding only far enough to refuse a bad one.
21245        assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
21246        let after = f.run(&[b"TS.INFO", b"t"]);
21247        assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
21248        assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
21249        assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
21250        assert_eq!(
21251            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
21252            "-ERR TSDB: unknown ENCODING parameter\r\n"
21253        );
21254        // An encoding it does take is still not applied.
21255        assert_eq!(
21256            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
21257            "+OK\r\n"
21258        );
21259        assert!(
21260            f.run(&[b"TS.INFO", b"t"])
21261                .contains("+chunkType\r\n+uncompressed\r\n")
21262        );
21263    }
21264
21265    /// Samples go in, come back out and are refused for the reasons the module
21266    /// refuses them.
21267    #[test]
21268    fn samples_land_where_they_are_put_and_the_newest_comes_back() {
21269        let mut f = Fixture::new();
21270        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
21271        // The series was made on the way in.
21272        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
21273        assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
21274        // A sample value goes out as a simple string of the shortest digits
21275        // that read back as the same number.
21276        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
21277        assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
21278        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
21279        // An empty series has no newest sample and answers an empty array
21280        // rather than a nil.
21281        assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
21282        assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
21283
21284        // The value is read before the key, so a bad one against a key holding
21285        // a string is about the value.
21286        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
21287        assert_eq!(
21288            f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
21289            "-ERR TSDB: invalid value\r\n"
21290        );
21291        // The grammar is tighter than the one a number argument usually gets:
21292        // no leading plus, no bare fraction, no infinity and nothing that does
21293        // not fit.
21294        for bad in [
21295            &b".5"[..],
21296            b"1.",
21297            b"+1",
21298            b" 1",
21299            b"0x10",
21300            b"inf",
21301            b"1e400",
21302            b"--1",
21303            b"1e",
21304        ] {
21305            assert_eq!(
21306                f.run(&[b"TS.ADD", b"v", b"1", bad]),
21307                "-ERR TSDB: invalid value\r\n",
21308                "{}",
21309                String::from_utf8_lossy(bad)
21310            );
21311        }
21312        // And a reading that is not a number is one of three words.
21313        assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
21314
21315        // A timestamp that is not a number, and one that is and is below zero,
21316        // are two different sentences.
21317        assert_eq!(
21318            f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
21319            "-ERR TSDB: invalid timestamp\r\n"
21320        );
21321        assert_eq!(
21322            f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
21323            "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
21324        );
21325
21326        // A repeated timestamp is blocked by default, and ON_DUPLICATE on the
21327        // command beats what the series was told.
21328        assert_eq!(
21329            f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
21330            "-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"
21331        );
21332        assert_eq!(
21333            f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
21334            ":300\r\n"
21335        );
21336        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
21337        // ON_DUPLICATE is only read when the key was already there, which is
21338        // why a policy word that is not a policy passes on a fresh key.
21339        assert_eq!(
21340            f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
21341            ":1\r\n"
21342        );
21343        assert_eq!(
21344            f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
21345            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
21346        );
21347
21348        // Retention is exact and it is checked before anything else happens, so
21349        // a sample landing behind the window is refused rather than trimmed.
21350        assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
21351        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
21352        assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
21353        assert_eq!(
21354            f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
21355            "-ERR TSDB: Timestamp is older than retention\r\n"
21356        );
21357        // And the window trims as it moves.
21358        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
21359        assert!(
21360            f.run(&[b"TS.INFO", b"r"])
21361                .contains("+totalSamples\r\n:1\r\n")
21362        );
21363
21364        // An ignore window drops a sample close enough to the newest one to be
21365        // uninteresting, and answers the newest timestamp so a client can tell.
21366        assert_eq!(
21367            f.run(&[
21368                b"TS.CREATE",
21369                b"i",
21370                b"DUPLICATE_POLICY",
21371                b"LAST",
21372                b"IGNORE",
21373                b"10",
21374                b"0.5"
21375            ]),
21376            "+OK\r\n"
21377        );
21378        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
21379        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
21380        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
21381    }
21382
21383    /// Every triple in a `TS.MADD` is answered on its own, and none of them
21384    /// makes a series.
21385    #[test]
21386    fn a_madd_answers_each_triple_and_creates_nothing() {
21387        let mut f = Fixture::new();
21388        assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
21389        assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
21390        assert_eq!(
21391            f.run(&[
21392                b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
21393            ]),
21394            "*3\r\n:100\r\n:100\r\n:200\r\n"
21395        );
21396        // A key that is not a series is an error in its own slot and the ones
21397        // after it still land.
21398        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
21399        assert_eq!(
21400            f.run(&[
21401                b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
21402            ]),
21403            "*3\r\n\
21404             -ERR TSDB: the key is not a TSDB key\r\n\
21405             -ERR TSDB: the key is not a TSDB key\r\n\
21406             :300\r\n"
21407        );
21408        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
21409        // A bad value and a bad timestamp are answered in their slots too.
21410        assert_eq!(
21411            f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
21412            "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
21413        );
21414        // And a list that is not made of triples is an arity error.
21415        assert!(
21416            f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
21417                .contains("wrong number of arguments for 'ts.madd' command")
21418        );
21419    }
21420
21421    /// The two increments, which only ever write forwards.
21422    #[test]
21423    fn an_increment_walks_the_newest_value_up_and_down() {
21424        let mut f = Fixture::new();
21425        assert_eq!(
21426            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
21427            ":100\r\n"
21428        );
21429        assert_eq!(
21430            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
21431            ":100\r\n"
21432        );
21433        // Two on one timestamp add up rather than collide, because the sample
21434        // goes in under the last policy whatever the series says.
21435        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
21436        assert_eq!(
21437            f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
21438            ":200\r\n"
21439        );
21440        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
21441        // A timestamp behind the newest sample is the other of the two errors
21442        // the module writes with no ERR in front of it.
21443        assert_eq!(
21444            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
21445            "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
21446        );
21447        // The increment goes through the ordinary number reader, so it takes
21448        // what a sample value will not and refuses a NaN that a sample value
21449        // takes.
21450        assert_eq!(
21451            f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
21452            ":1\r\n"
21453        );
21454        assert_eq!(
21455            f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
21456            ":1\r\n"
21457        );
21458        assert_eq!(
21459            f.run(&[b"TS.INCRBY", b"t", b"nan"]),
21460            "-ERR TSDB: invalid increase/decrease value\r\n"
21461        );
21462        assert_eq!(
21463            f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
21464            "-ERR TSDB: invalid increase/decrease value\r\n"
21465        );
21466        // A key holding something else is WRONGTYPE and is answered before the
21467        // number is looked at.
21468        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
21469        assert_eq!(
21470            f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
21471            "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
21472        );
21473        // A TIMESTAMP keyword with nothing behind it is about the timestamp.
21474        // The reference reads one past the end of its own arguments here and
21475        // answers whatever was in that memory, so there is nothing to copy and
21476        // this answers the same thing every time.
21477        assert_eq!(
21478            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
21479            "-ERR TSDB: invalid timestamp\r\n"
21480        );
21481        // And one behind a LABELS is a label name rather than the keyword, so
21482        // this lands at the clock rather than at 5.
21483        assert_eq!(
21484            f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
21485            format!(":{}\r\n", f.server.now_ms())
21486        );
21487        // Adding to a series whose newest value is not a number has no answer.
21488        assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
21489        assert_eq!(
21490            f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
21491            "-ERR TSDB: cannot increment/decrement NaN value\r\n"
21492        );
21493    }
21494
21495    /// Deleting a span, both ends included.
21496    #[test]
21497    fn deleting_takes_out_a_span_and_answers_how_many_went() {
21498        let mut f = Fixture::new();
21499        for at in [b"100".as_slice(), b"200", b"300", b"400"] {
21500            f.run(&[b"TS.ADD", b"t", at, b"1"]);
21501        }
21502        assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
21503        assert!(
21504            f.run(&[b"TS.INFO", b"t"])
21505                .contains("+totalSamples\r\n:2\r\n")
21506        );
21507        // Ends the wrong way round take nothing out rather than being an error.
21508        assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
21509        // The two open ends.
21510        assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
21511        // A series everything has been deleted from keeps its chunk and reports
21512        // zero at both ends again.
21513        let empty = f.run(&[b"TS.INFO", b"t"]);
21514        assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
21515        assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
21516        assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
21517        assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
21518        assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
21519        // The two ends have their own sentences.
21520        assert_eq!(
21521            f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
21522            "-ERR TSDB: wrong fromTimestamp\r\n"
21523        );
21524        assert_eq!(
21525            f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
21526            "-ERR TSDB: wrong toTimestamp\r\n"
21527        );
21528        assert_eq!(
21529            f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
21530            "-ERR TSDB: wrong fromTimestamp\r\n"
21531        );
21532    }
21533
21534    /// What RESP3 changes, which is the two places a number is written and the
21535    /// shape of `TS.INFO`.
21536    #[test]
21537    fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
21538        let mut f = Fixture::new();
21539        f.out = Out::new(Proto::Resp3);
21540        assert_eq!(
21541            f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
21542            "+OK\r\n"
21543        );
21544        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
21545        // A double rather than the simple string RESP2 gets.
21546        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
21547        assert_eq!(
21548            without_memory(&f.run(&[b"TS.INFO", b"t"])),
21549            "%14\r\n\
21550             +totalSamples\r\n:1\r\n\
21551             +memoryUsage\r\n:\r\n\
21552             +firstTimestamp\r\n:100\r\n\
21553             +lastTimestamp\r\n:100\r\n\
21554             +retentionTime\r\n:0\r\n\
21555             +chunkCount\r\n:1\r\n\
21556             +chunkSize\r\n:4096\r\n\
21557             +chunkType\r\n+compressed\r\n\
21558             +duplicatePolicy\r\n+block\r\n\
21559             +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
21560             +sourceKey\r\n_\r\n\
21561             +rules\r\n%0\r\n\
21562             +ignoreMaxTimeDiff\r\n:0\r\n\
21563             +ignoreMaxValDiff\r\n,0\r\n"
21564        );
21565    }
21566
21567    /// Reading a span back, both ways round, with the two ends and the three
21568    /// things that trim what comes out.
21569    #[test]
21570    fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
21571        let mut f = Fixture::new();
21572        for (at, v) in [
21573            (b"100".as_slice(), b"1".as_slice()),
21574            (b"200", b"2"),
21575            (b"300", b"3"),
21576            (b"400", b"4"),
21577        ] {
21578            f.run(&[b"TS.ADD", b"t", at, v]);
21579        }
21580        assert_eq!(
21581            f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
21582            "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
21583             *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
21584        );
21585        // Both ends are included.
21586        assert_eq!(
21587            f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
21588            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
21589        );
21590        // Backwards, and the count takes from the front of what comes out, so
21591        // backwards it takes the newest.
21592        assert_eq!(
21593            f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
21594            "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
21595        );
21596        // Ends the wrong way round are empty rather than an error.
21597        assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
21598        // The two filters.
21599        assert_eq!(
21600            f.run(&[
21601                b"TS.RANGE",
21602                b"t",
21603                b"-",
21604                b"+",
21605                b"FILTER_BY_VALUE",
21606                b"2",
21607                b"3"
21608            ]),
21609            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
21610        );
21611        assert_eq!(
21612            f.run(&[
21613                b"TS.RANGE",
21614                b"t",
21615                b"-",
21616                b"+",
21617                b"FILTER_BY_TS",
21618                b"100",
21619                b"400"
21620            ]),
21621            "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
21622        );
21623        // A word that is not an option is ignored wherever it sits.
21624        assert_eq!(
21625            f.run(&[
21626                b"TS.RANGE",
21627                b"t",
21628                b"-",
21629                b"+",
21630                b"ZZZ",
21631                b"FILTER_BY_TS",
21632                b"400"
21633            ]),
21634            "*1\r\n*2\r\n:400\r\n+4\r\n"
21635        );
21636        // `LATEST` means nothing until there is a compaction rule to follow.
21637        assert_eq!(
21638            f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
21639            "*1\r\n*2\r\n:100\r\n+1\r\n"
21640        );
21641    }
21642
21643    /// The bucketing, which is one column a reduction and a flat row.
21644    #[test]
21645    fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
21646        let mut f = Fixture::new();
21647        for (at, v) in [
21648            (b"100".as_slice(), b"1".as_slice()),
21649            (b"200", b"2"),
21650            (b"300", b"3"),
21651            (b"400", b"4"),
21652        ] {
21653            f.run(&[b"TS.ADD", b"t", at, v]);
21654        }
21655        assert_eq!(
21656            f.run(&[
21657                b"TS.RANGE",
21658                b"t",
21659                b"-",
21660                b"+",
21661                b"AGGREGATION",
21662                b"avg",
21663                b"200"
21664            ]),
21665            "*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"
21666        );
21667        // Three reductions is a row of four and not a row of two with a nested
21668        // three in it.
21669        assert_eq!(
21670            f.run(&[
21671                b"TS.RANGE",
21672                b"t",
21673                b"-",
21674                b"+",
21675                b"AGGREGATION",
21676                b"min,max,count",
21677                b"200"
21678            ]),
21679            "*3\r\n\
21680             *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
21681             *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
21682             *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
21683        );
21684        // The timestamp a bucket is reported under.
21685        assert_eq!(
21686            f.run(&[
21687                b"TS.RANGE",
21688                b"t",
21689                b"-",
21690                b"+",
21691                b"AGGREGATION",
21692                b"avg",
21693                b"200",
21694                b"BUCKETTIMESTAMP",
21695                b"+"
21696            ]),
21697            "*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"
21698        );
21699        // An alignment moves where the bucket edges land.
21700        assert_eq!(
21701            f.run(&[
21702                b"TS.RANGE",
21703                b"t",
21704                b"100",
21705                b"400",
21706                b"ALIGN",
21707                b"100",
21708                b"AGGREGATION",
21709                b"sum",
21710                b"200"
21711            ]),
21712            "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
21713        );
21714        // A `COUNT` sitting where the reduction name belongs is that name, and
21715        // the scan for a real one starts again two words later.
21716        assert_eq!(
21717            f.run(&[
21718                b"TS.RANGE",
21719                b"t",
21720                b"-",
21721                b"+",
21722                b"AGGREGATION",
21723                b"count",
21724                b"200"
21725            ]),
21726            "*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"
21727        );
21728        assert_eq!(
21729            f.run(&[
21730                b"TS.RANGE",
21731                b"t",
21732                b"-",
21733                b"+",
21734                b"AGGREGATION",
21735                b"count",
21736                b"200",
21737                b"COUNT",
21738                b"1"
21739            ]),
21740            "*1\r\n*2\r\n:0\r\n+1\r\n"
21741        );
21742    }
21743
21744    /// `EMPTY` fills the gaps between readings and nothing else, and `last`
21745    /// carries two different things depending on which kind of empty it is.
21746    #[test]
21747    fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
21748        let mut f = Fixture::new();
21749        for (at, v) in [
21750            (b"0".as_slice(), b"1".as_slice()),
21751            (b"100", b"2"),
21752            (b"500", b"nan"),
21753            (b"600", b"3"),
21754        ] {
21755            f.run(&[b"TS.ADD", b"g", at, v]);
21756        }
21757        // Without `EMPTY` the buckets with nothing in them are not there at all,
21758        // and neither is the one holding only a reading that is not a number.
21759        assert_eq!(
21760            f.run(&[
21761                b"TS.RANGE",
21762                b"g",
21763                b"-",
21764                b"+",
21765                b"AGGREGATION",
21766                b"avg",
21767                b"100"
21768            ]),
21769            "*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"
21770        );
21771        // The sum of nothing is zero rather than not a number.
21772        assert_eq!(
21773            f.run(&[
21774                b"TS.RANGE",
21775                b"g",
21776                b"-",
21777                b"+",
21778                b"AGGREGATION",
21779                b"sum",
21780                b"100",
21781                b"EMPTY"
21782            ]),
21783            "*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\
21784             *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\
21785             *2\r\n:600\r\n+3\r\n"
21786        );
21787        // Buckets 200 through 400 have no readings at all and carry the reading
21788        // before the gap either way round. Bucket 500 has a reading that is not
21789        // a number, so it carries whatever the bucket before it in the reading
21790        // direction answered, which is 2 forwards and 3 backwards.
21791        assert_eq!(
21792            f.run(&[
21793                b"TS.RANGE",
21794                b"g",
21795                b"-",
21796                b"+",
21797                b"AGGREGATION",
21798                b"last",
21799                b"100",
21800                b"EMPTY"
21801            ]),
21802            "*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\
21803             *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\
21804             *2\r\n:600\r\n+3\r\n"
21805        );
21806        assert_eq!(
21807            f.run(&[
21808                b"TS.REVRANGE",
21809                b"g",
21810                b"-",
21811                b"+",
21812                b"AGGREGATION",
21813                b"last",
21814                b"100",
21815                b"EMPTY"
21816            ]),
21817            "*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\
21818             *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\
21819             *2\r\n:0\r\n+1\r\n"
21820        );
21821        // And a window that opens on that bucket has nothing in range before it
21822        // to carry, so it answers not a number.
21823        assert_eq!(
21824            f.run(&[
21825                b"TS.RANGE",
21826                b"g",
21827                b"500",
21828                b"600",
21829                b"AGGREGATION",
21830                b"last",
21831                b"100",
21832                b"EMPTY"
21833            ]),
21834            "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
21835        );
21836    }
21837
21838    /// The sentences a read answers when its options do not add up, which are
21839    /// the module's own word for word.
21840    #[test]
21841    fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
21842        let mut f = Fixture::new();
21843        f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
21844        f.run(&[b"SET", b"str", b"x"]);
21845        let cases: &[(&[&[u8]], &str)] = &[
21846            (
21847                &[b"TS.RANGE", b"t"],
21848                "-ERR wrong number of arguments for 'ts.range' command\r\n",
21849            ),
21850            // The key is resolved before a single option is read.
21851            (
21852                &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
21853                "-ERR TSDB: the key does not exist\r\n",
21854            ),
21855            (
21856                &[b"TS.RANGE", b"str", b"-", b"+"],
21857                "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
21858            ),
21859            (
21860                &[b"TS.RANGE", b"t", b"abc", b"+"],
21861                "-ERR TSDB: wrong fromTimestamp\r\n",
21862            ),
21863            (
21864                &[b"TS.RANGE", b"t", b"-", b"abc"],
21865                "-ERR TSDB: wrong toTimestamp\r\n",
21866            ),
21867            (
21868                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
21869                "-ERR TSDB: COUNT argument is missing\r\n",
21870            ),
21871            (
21872                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
21873                "-ERR TSDB: Couldn't parse COUNT\r\n",
21874            ),
21875            (
21876                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
21877                "-ERR TSDB: Invalid COUNT value\r\n",
21878            ),
21879            (
21880                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
21881                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
21882            ),
21883            (
21884                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
21885                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
21886            ),
21887            (
21888                &[
21889                    b"TS.RANGE",
21890                    b"t",
21891                    b"-",
21892                    b"+",
21893                    b"AGGREGATION",
21894                    b"nope",
21895                    b"100",
21896                ],
21897                "-ERR TSDB: Unknown aggregation type\r\n",
21898            ),
21899            (
21900                &[
21901                    b"TS.RANGE",
21902                    b"t",
21903                    b"-",
21904                    b"+",
21905                    b"AGGREGATION",
21906                    b"avg,,min",
21907                    b"100",
21908                ],
21909                "-ERR TSDB: Empty aggregation type in list\r\n",
21910            ),
21911            // The list of names is read before the width is looked at.
21912            (
21913                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
21914                "-ERR TSDB: Unknown aggregation type\r\n",
21915            ),
21916            (
21917                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
21918                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
21919            ),
21920            (
21921                &[
21922                    b"TS.RANGE",
21923                    b"t",
21924                    b"-",
21925                    b"+",
21926                    b"AGGREGATION",
21927                    b"avg",
21928                    b"100",
21929                    b"X",
21930                    b"EMPTY",
21931                ],
21932                "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
21933            ),
21934            (
21935                &[
21936                    b"TS.RANGE",
21937                    b"t",
21938                    b"-",
21939                    b"+",
21940                    b"AGGREGATION",
21941                    b"avg",
21942                    b"100",
21943                    b"BUCKETTIMESTAMP",
21944                    b"z",
21945                ],
21946                "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
21947            ),
21948            (
21949                &[
21950                    b"TS.RANGE",
21951                    b"t",
21952                    b"-",
21953                    b"+",
21954                    b"AGGREGATION",
21955                    b"avg",
21956                    b"100",
21957                    b"X",
21958                    b"Y",
21959                    b"BUCKETTIMESTAMP",
21960                    b"-",
21961                ],
21962                "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
21963                 AGGREGATION flag\r\n",
21964            ),
21965            (
21966                &[
21967                    b"TS.RANGE",
21968                    b"t",
21969                    b"-",
21970                    b"+",
21971                    b"ALIGN",
21972                    b"z",
21973                    b"AGGREGATION",
21974                    b"avg",
21975                    b"100",
21976                ],
21977                "-ERR TSDB: unknown ALIGN parameter\r\n",
21978            ),
21979            (
21980                &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
21981                "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
21982            ),
21983            (
21984                &[
21985                    b"TS.RANGE",
21986                    b"t",
21987                    b"-",
21988                    b"+",
21989                    b"ALIGN",
21990                    b"-",
21991                    b"AGGREGATION",
21992                    b"avg",
21993                    b"100",
21994                ],
21995                "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
21996            ),
21997            (
21998                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
21999                "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
22000            ),
22001            (
22002                &[
22003                    b"TS.RANGE",
22004                    b"t",
22005                    b"-",
22006                    b"+",
22007                    b"FILTER_BY_VALUE",
22008                    b"x",
22009                    b"2",
22010                ],
22011                "-ERR TSDB: Couldn't parse MIN\r\n",
22012            ),
22013            (
22014                &[
22015                    b"TS.RANGE",
22016                    b"t",
22017                    b"-",
22018                    b"+",
22019                    b"FILTER_BY_VALUE",
22020                    b"1",
22021                    b"y",
22022                ],
22023                "-ERR TSDB: Couldn't parse MAX\r\n",
22024            ),
22025            (
22026                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
22027                "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
22028            ),
22029        ];
22030        for (argv, want) in cases {
22031            let got = f.run(argv);
22032            assert_eq!(&got, want, "{:?}", argv.last());
22033        }
22034        // The one sentence here that is yo's own rather than the module's, which
22035        // is D-54. A read that would build more rows than yo will build is
22036        // refused instead of attempted.
22037        f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
22038        f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
22039        assert_eq!(
22040            f.run(&[
22041                b"TS.RANGE",
22042                b"wide",
22043                b"-",
22044                b"+",
22045                b"AGGREGATION",
22046                b"avg",
22047                b"1",
22048                b"EMPTY"
22049            ]),
22050            "-ERR TSDB: the requested range holds too many empty buckets\r\n"
22051        );
22052    }
22053
22054    /// What RESP3 changes on a read, which is only how a number is written.
22055    #[test]
22056    fn resp3_writes_a_read_value_as_a_double() {
22057        let mut f = Fixture::new();
22058        f.out = Out::new(Proto::Resp3);
22059        for (at, v) in [
22060            (b"0".as_slice(), b"1".as_slice()),
22061            (b"100", b"2"),
22062            (b"500", b"nan"),
22063            (b"600", b"3"),
22064        ] {
22065            f.run(&[b"TS.ADD", b"g", at, v]);
22066        }
22067        assert_eq!(
22068            f.run(&[
22069                b"TS.RANGE",
22070                b"g",
22071                b"0",
22072                b"100",
22073                b"AGGREGATION",
22074                b"avg,min",
22075                b"200"
22076            ]),
22077            "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
22078        );
22079        assert_eq!(
22080            f.run(&[
22081                b"TS.RANGE",
22082                b"g",
22083                b"500",
22084                b"600",
22085                b"AGGREGATION",
22086                b"last",
22087                b"100",
22088                b"EMPTY"
22089            ]),
22090            "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
22091        );
22092    }
22093
22094    /// Two series with an overlap and a gap each, plus a third holding nothing,
22095    /// which is what the joined reads are measured against.
22096    fn joined() -> Fixture {
22097        let mut f = Fixture::new();
22098        f.run(&[b"TS.CREATE", b"z"]);
22099        for (at, v) in [
22100            (b"10".as_slice(), b"1".as_slice()),
22101            (b"20", b"2"),
22102            (b"40", b"4"),
22103            (b"50", b"5"),
22104        ] {
22105            f.run(&[b"TS.ADD", b"x", at, v]);
22106        }
22107        for (at, v) in [
22108            (b"20".as_slice(), b"20".as_slice()),
22109            (b"30", b"30"),
22110            (b"50", b"50"),
22111            (b"60", b"60"),
22112        ] {
22113            f.run(&[b"TS.ADD", b"y", at, v]);
22114        }
22115        f
22116    }
22117
22118    /// The joined read lines its keys up on the timestamp and writes a row as
22119    /// the timestamp and then a nested array of the columns, which is the one
22120    /// shape in the family that is not the flat pair.
22121    #[test]
22122    fn an_nrange_joins_its_keys_on_the_timestamp() {
22123        let mut f = joined();
22124        // One key still nests, so the shape does not depend on the count.
22125        assert_eq!(
22126            f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
22127            "*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\
22128             *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
22129        );
22130        // A key with no reading where another key has one writes NaN there.
22131        assert_eq!(
22132            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
22133            "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
22134             *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
22135             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
22136             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
22137             *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
22138             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
22139        );
22140        // A series holding nothing is a column of NaN and never a row of its
22141        // own, and the same key twice answers twice.
22142        assert_eq!(
22143            f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
22144            "*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"
22145        );
22146        assert_eq!(
22147            f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
22148            "*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"
22149        );
22150        // COUNT is applied to the joined rows and not to each key, so backwards
22151        // it gives the newest joined row rather than the newest of each.
22152        assert_eq!(
22153            f.run(&[
22154                b"TS.NREVRANGE",
22155                b"2",
22156                b"x",
22157                b"y",
22158                b"-",
22159                b"+",
22160                b"COUNT",
22161                b"1"
22162            ]),
22163            "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
22164        );
22165        assert_eq!(
22166            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
22167            "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
22168        );
22169        // The two sample filters are settled a key at a time, before the join.
22170        assert_eq!(
22171            f.run(&[
22172                b"TS.NRANGE",
22173                b"2",
22174                b"x",
22175                b"y",
22176                b"-",
22177                b"+",
22178                b"FILTER_BY_VALUE",
22179                b"2",
22180                b"30"
22181            ]),
22182            "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
22183             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
22184             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
22185             *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
22186        );
22187    }
22188
22189    /// The aggregation on a joined read names one reduction a key and then the
22190    /// one bucket width, and each name may be a comma list, so a row can be
22191    /// wider than the key count.
22192    #[test]
22193    fn an_nrange_aggregation_names_one_reduction_a_key() {
22194        let mut f = joined();
22195        assert_eq!(
22196            f.run(&[
22197                b"TS.NRANGE",
22198                b"2",
22199                b"x",
22200                b"y",
22201                b"-",
22202                b"+",
22203                b"AGGREGATION",
22204                b"sum",
22205                b"sum",
22206                b"20"
22207            ]),
22208            "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
22209             *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
22210             *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
22211             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
22212        );
22213        // A comma list on the first key widens the row to three columns.
22214        assert_eq!(
22215            f.run(&[
22216                b"TS.NRANGE",
22217                b"2",
22218                b"x",
22219                b"y",
22220                b"-",
22221                b"+",
22222                b"AGGREGATION",
22223                b"sum,count",
22224                b"avg",
22225                b"20"
22226            ]),
22227            "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
22228             *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
22229             *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
22230             *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
22231        );
22232        // Everything behind the width moves along with it, so BUCKETTIMESTAMP
22233        // sits one or two past the width whatever the key count is.
22234        assert_eq!(
22235            f.run(&[
22236                b"TS.NRANGE",
22237                b"2",
22238                b"x",
22239                b"y",
22240                b"-",
22241                b"+",
22242                b"AGGREGATION",
22243                b"avg",
22244                b"sum",
22245                b"100",
22246                b"EMPTY",
22247                b"BUCKETTIMESTAMP",
22248                b"end"
22249            ]),
22250            "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
22251        );
22252        // A COUNT landing in one of the name slots is a reduction name and not
22253        // the keyword, and the read then has no count at all.
22254        assert_eq!(
22255            f.run(&[
22256                b"TS.NRANGE",
22257                b"2",
22258                b"x",
22259                b"y",
22260                b"-",
22261                b"+",
22262                b"AGGREGATION",
22263                b"avg",
22264                b"COUNT",
22265                b"100"
22266            ]),
22267            "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
22268        );
22269    }
22270
22271    /// The sentences a joined read answers when it does not add up, which are
22272    /// the module's own and come out in the module's own order.
22273    #[test]
22274    fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
22275        let mut f = joined();
22276        f.run(&[b"SET", b"str", b"hi"]);
22277        let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
22278        let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
22279                       must be equal to numkeys\r\n";
22280        let cases: &[(&[&[u8]], &str)] = &[
22281            (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
22282            (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
22283            (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
22284            // Not enough words behind the count for the keys and both ends of
22285            // the span, which is an arity error however many keys were named.
22286            (
22287                &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
22288                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
22289            ),
22290            (
22291                &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
22292                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
22293            ),
22294            // The reduction names are read before the two ends of the span,
22295            // which no other option is.
22296            (
22297                &[
22298                    b"TS.NRANGE",
22299                    b"2",
22300                    b"x",
22301                    b"y",
22302                    b"abc",
22303                    b"+",
22304                    b"AGGREGATION",
22305                    b"nope",
22306                    b"sum",
22307                    b"100",
22308                ],
22309                "-ERR TSDB: Unknown aggregation type\r\n",
22310            ),
22311            (
22312                &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
22313                "-ERR TSDB: wrong fromTimestamp\r\n",
22314            ),
22315            (
22316                &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
22317                "-ERR TSDB: wrong toTimestamp\r\n",
22318            ),
22319            // A name slot that is missing or holds a number is the count
22320            // sentence, and a width slot that is itself a reduction name is
22321            // that sentence as well.
22322            (
22323                &[
22324                    b"TS.NRANGE",
22325                    b"2",
22326                    b"x",
22327                    b"y",
22328                    b"-",
22329                    b"+",
22330                    b"AGGREGATION",
22331                    b"avg",
22332                ],
22333                numkeys,
22334            ),
22335            (
22336                &[
22337                    b"TS.NRANGE",
22338                    b"2",
22339                    b"x",
22340                    b"y",
22341                    b"-",
22342                    b"+",
22343                    b"AGGREGATION",
22344                    b"100",
22345                    b"sum",
22346                    b"100",
22347                ],
22348                numkeys,
22349            ),
22350            (
22351                &[
22352                    b"TS.NRANGE",
22353                    b"2",
22354                    b"x",
22355                    b"y",
22356                    b"-",
22357                    b"+",
22358                    b"AGGREGATION",
22359                    b"avg",
22360                    b"sum",
22361                    b"sum",
22362                    b"100",
22363                ],
22364                numkeys,
22365            ),
22366            (
22367                &[
22368                    b"TS.NRANGE",
22369                    b"2",
22370                    b"x",
22371                    b"y",
22372                    b"-",
22373                    b"+",
22374                    b"AGGREGATION",
22375                    b"avg",
22376                    b"sum",
22377                    b"abc",
22378                ],
22379                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
22380            ),
22381            (
22382                &[
22383                    b"TS.NRANGE",
22384                    b"2",
22385                    b"x",
22386                    b"y",
22387                    b"-",
22388                    b"+",
22389                    b"AGGREGATION",
22390                    b"avg",
22391                    b"sum",
22392                    b"0",
22393                ],
22394                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
22395            ),
22396            // With one key none of that applies and the plain parser runs, so a
22397            // lone width is a missing width rather than a count mismatch.
22398            (
22399                &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
22400                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
22401            ),
22402            (
22403                &[
22404                    b"TS.NRANGE",
22405                    b"1",
22406                    b"x",
22407                    b"-",
22408                    b"+",
22409                    b"AGGREGATION",
22410                    b"100",
22411                    b"200",
22412                ],
22413                "-ERR TSDB: Unknown aggregation type\r\n",
22414            ),
22415            // The keys come last and in the order they were named.
22416            (
22417                &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
22418                "-ERR TSDB: the key does not exist\r\n",
22419            ),
22420            (
22421                &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
22422                "-ERR WRONGTYPE Operation against a key \
22423                 holding the wrong kind of value\r\n",
22424            ),
22425        ];
22426        for (argv, want) in cases {
22427            let got = f.run(argv);
22428            assert_eq!(&got, want, "{argv:?}");
22429        }
22430    }
22431
22432    /// `TS.READ`, which is a key, one timestamp and everything from there on.
22433    #[test]
22434    fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
22435        let mut f = joined();
22436        assert_eq!(
22437            f.run(&[b"TS.READ", b"x", b"-"]),
22438            "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
22439             *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
22440        );
22441        // A plus is the last sample on its own, and a timestamp between two
22442        // samples starts at the one behind it.
22443        assert_eq!(
22444            f.run(&[b"TS.READ", b"x", b"+"]),
22445            "*1\r\n*2\r\n:50\r\n+5\r\n"
22446        );
22447        assert_eq!(
22448            f.run(&[b"TS.READ", b"x", b"25"]),
22449            "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
22450        );
22451        // Past the end, a series holding nothing and a key that is not there
22452        // are all the empty array rather than an error.
22453        assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
22454        assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
22455        assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
22456        assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
22457        // The timestamp refusal goes out with nothing in front of it, and a key
22458        // holding something else answers the bare WRONGTYPE rather than the
22459        // module's prefixed one, both unlike the rest of the family.
22460        assert_eq!(
22461            f.run(&[b"TS.READ", b"x", b"abc"]),
22462            "-TSDB: invalid timestamp\r\n"
22463        );
22464        assert_eq!(
22465            f.run(&[b"TS.READ", b"x", b"-1"]),
22466            "-TSDB: invalid timestamp\r\n"
22467        );
22468        f.run(&[b"SET", b"str", b"hi"]);
22469        assert_eq!(
22470            f.run(&[b"TS.READ", b"str", b"-"]),
22471            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
22472        );
22473        // Anything other than exactly three words is an arity error, so there
22474        // is nowhere to put an option even though the table says minus three.
22475        assert_eq!(
22476            f.run(&[b"TS.READ", b"x"]),
22477            "-ERR wrong number of arguments for 'ts.read' command\r\n"
22478        );
22479        assert_eq!(
22480            f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
22481            "-ERR wrong number of arguments for 'ts.read' command\r\n"
22482        );
22483    }
22484
22485    /// The keys of a joined read sit behind a count, so `COMMAND GETKEYS` has
22486    /// to read the count to find them.
22487    #[test]
22488    fn getkeys_reads_the_count_of_a_joined_read() {
22489        let mut f = Fixture::new();
22490        assert_eq!(
22491            f.run(&[
22492                b"COMMAND",
22493                b"GETKEYS",
22494                b"TS.NRANGE",
22495                b"2",
22496                b"a",
22497                b"b",
22498                b"-",
22499                b"+"
22500            ]),
22501            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
22502        );
22503        assert_eq!(
22504            f.run(&[
22505                b"COMMAND",
22506                b"GETKEYS",
22507                b"TS.NREVRANGE",
22508                b"1",
22509                b"a",
22510                b"-",
22511                b"+"
22512            ]),
22513            "*1\r\n$1\r\na\r\n"
22514        );
22515        // A count of zero, or one too large for the words that follow it, is
22516        // the server's own refusal and not the module's.
22517        for n in [b"0".as_slice(), b"9", b"abc"] {
22518            assert_eq!(
22519                f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
22520                "-ERR Invalid arguments specified for command\r\n"
22521            );
22522        }
22523    }
22524
22525    /// The five series every test of the label surface works against.
22526    fn labelled() -> Fixture {
22527        let mut f = Fixture::new();
22528        f.run(&[
22529            b"TS.CREATE",
22530            b"a",
22531            b"LABELS",
22532            b"room",
22533            b"kitchen",
22534            b"x",
22535            b"1",
22536        ]);
22537        f.run(&[
22538            b"TS.CREATE",
22539            b"b",
22540            b"LABELS",
22541            b"room",
22542            b"bedroom",
22543            b"x",
22544            b"2",
22545        ]);
22546        f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
22547        f.run(&[b"TS.CREATE", b"d"]);
22548        f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
22549        f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
22550        f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
22551        f
22552    }
22553
22554    /// The filter grammar, which is four steps and a `strtok` rather than a
22555    /// grammar, and which every command that searches on labels shares.
22556    #[test]
22557    fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
22558        let mut f = labelled();
22559        let cases: &[(&[&[u8]], &str)] = &[
22560            // The plain forms, and the order the answer comes back in, which is
22561            // by key name and not by anything the series remembers.
22562            (
22563                &[b"TS.QUERYINDEX", b"room=kitchen"],
22564                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
22565            ),
22566            (
22567                &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
22568                "*1\r\n$1\r\na\r\n",
22569            ),
22570            (
22571                &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
22572                "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
22573            ),
22574            // An empty list still counts as something that says which series to
22575            // take, it just never takes any.
22576            (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
22577            // Absent and present, neither of which stands on its own.
22578            (
22579                &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
22580                "*1\r\n$1\r\nc\r\n",
22581            ),
22582            (
22583                &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
22584                "*1\r\n$1\r\na\r\n",
22585            ),
22586            (
22587                &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
22588                "-ERR TSDB: please provide at least one matcher\r\n",
22589            ),
22590            // A run of separators is one separator and everything past the
22591            // second field is dropped, so all three of these ask one question.
22592            (
22593                &[b"TS.QUERYINDEX", b"room==kitchen"],
22594                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
22595            ),
22596            (
22597                &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
22598                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
22599            ),
22600            (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
22601            // A bracket is only a list when it sits straight behind the
22602            // separator, and then the label in front of it has to be there.
22603            (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
22604            (
22605                &[b"TS.QUERYINDEX", b"=(1)"],
22606                "-ERR TSDB: failed parsing labels\r\n",
22607            ),
22608            (
22609                &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
22610                "-ERR TSDB: failed parsing labels\r\n",
22611            ),
22612            (
22613                &[b"TS.QUERYINDEX", b"room=(kitchen"],
22614                "-ERR TSDB: failed parsing labels\r\n",
22615            ),
22616            (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
22617            (
22618                &[b"TS.QUERYINDEX", b"nonsense"],
22619                "-ERR TSDB: failed parsing labels\r\n",
22620            ),
22621            // Nothing here says which series to take.
22622            (
22623                &[b"TS.QUERYINDEX", b"room!=kitchen"],
22624                "-ERR TSDB: please provide at least one matcher\r\n",
22625            ),
22626            // Names and values are both compared byte for byte.
22627            (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
22628            (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
22629            (
22630                &[b"TS.QUERYINDEX"],
22631                "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
22632            ),
22633        ];
22634        for (argv, want) in cases {
22635            let got = f.run(argv);
22636            assert_eq!(&got, want, "{:?}", argv.last());
22637        }
22638    }
22639
22640    /// `TS.QUERYLABELS`, whose filter is the one that is allowed to be missing.
22641    #[test]
22642    fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
22643        let mut f = labelled();
22644        let cases: &[(&[&[u8]], &str)] = &[
22645            (
22646                &[b"TS.QUERYLABELS", b"LABELS"],
22647                "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
22648            ),
22649            (
22650                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
22651                "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
22652            ),
22653            (
22654                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
22655                "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
22656            ),
22657            // The series wearing `r` twice contributes the smaller of the two
22658            // here, which is not the one it was written down as first.
22659            (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
22660            (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
22661            (
22662                &[b"TS.QUERYLABELS", b"VALUES"],
22663                "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
22664            ),
22665            (
22666                &[b"TS.QUERYLABELS", b"ZZZ"],
22667                "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
22668            ),
22669            (
22670                &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
22671                "-ERR TSDB: unknown argument, expected FILTER\r\n",
22672            ),
22673            (
22674                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
22675                "-ERR TSDB: FILTER given with no filter expressions\r\n",
22676            ),
22677            // With no filter at all every series is taken, which is why the
22678            // first case here answers about `r` as well. A filter that is there
22679            // still has to say which series to take.
22680            (
22681                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
22682                "-ERR TSDB: please provide at least one matcher\r\n",
22683            ),
22684            (
22685                &[
22686                    b"TS.QUERYLABELS",
22687                    b"LABELS",
22688                    b"FILTER",
22689                    b"room=kitchen",
22690                    b"x=",
22691                ],
22692                "*1\r\n$4\r\nroom\r\n",
22693            ),
22694        ];
22695        for (argv, want) in cases {
22696            let got = f.run(argv);
22697            assert_eq!(&got, want, "{:?}", argv.last());
22698        }
22699    }
22700
22701    /// `TS.MGET`, the newest sample of every series a filter takes, and the two
22702    /// ways of asking for the labels back alongside it.
22703    #[test]
22704    fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
22705        let mut f = labelled();
22706        let cases: &[(&[&[u8]], &str)] = &[
22707            // A series with no samples writes an empty array where the sample
22708            // goes rather than dropping out of the reply.
22709            (
22710                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
22711                "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
22712                 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
22713            ),
22714            (
22715                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
22716                "*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\
22717                 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
22718                 *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",
22719            ),
22720            // A selected label the series does not wear is a nil, not a gap.
22721            (
22722                &[
22723                    b"TS.MGET",
22724                    b"SELECTED_LABELS",
22725                    b"x",
22726                    b"FILTER",
22727                    b"room=kitchen",
22728                ],
22729                "*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\
22730                 *2\r\n:100\r\n+1.5\r\n\
22731                 *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",
22732            ),
22733            // The other half of the duplicated name rule. This one takes the
22734            // first written down where `TS.QUERYLABELS` takes the smallest.
22735            (
22736                &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
22737                "*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",
22738            ),
22739            (
22740                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
22741                "*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\
22742                 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
22743            ),
22744            // A word that is not an option is ignored, but a missing `FILTER`
22745            // is an arity error whatever else was written.
22746            (
22747                &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
22748                "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
22749            ),
22750            (
22751                &[b"TS.MGET", b"a", b"b", b"c"],
22752                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
22753            ),
22754            (
22755                &[b"TS.MGET", b"FILTER"],
22756                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
22757            ),
22758            // Both keyword checks happen before the filter is read, and the two
22759            // sentences spell the second keyword without its `ED`.
22760            (
22761                &[
22762                    b"TS.MGET",
22763                    b"WITHLABELS",
22764                    b"SELECTED_LABELS",
22765                    b"x",
22766                    b"FILTER",
22767                    b"bad",
22768                ],
22769                "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
22770            ),
22771            (
22772                &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
22773                "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
22774            ),
22775        ];
22776        for (argv, want) in cases {
22777            let got = f.run(argv);
22778            assert_eq!(&got, want, "{:?}", argv.last());
22779        }
22780    }
22781
22782    /// What RESP3 changes across the label surface, which is a set where there
22783    /// was an array and a map where there was a pair of them.
22784    #[test]
22785    fn resp3_writes_the_label_surface_as_sets_and_maps() {
22786        let mut f = labelled();
22787        f.out = Out::new(Proto::Resp3);
22788        let cases: &[(&[&[u8]], &str)] = &[
22789            (
22790                &[b"TS.QUERYINDEX", b"room=kitchen"],
22791                "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
22792            ),
22793            (
22794                &[b"TS.QUERYLABELS", b"LABELS"],
22795                "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
22796            ),
22797            (
22798                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
22799                "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
22800            ),
22801            // The key stops being the first of three and becomes the map key,
22802            // and the labels stop being pairs and become a map of their own.
22803            (
22804                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
22805                "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
22806                 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
22807            ),
22808            (
22809                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
22810                "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
22811                 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
22812                 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
22813            ),
22814            (
22815                &[
22816                    b"TS.MGET",
22817                    b"SELECTED_LABELS",
22818                    b"x",
22819                    b"FILTER",
22820                    b"room=kitchen",
22821                ],
22822                "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
22823                 *2\r\n:100\r\n,1.5\r\n\
22824                 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
22825            ),
22826            // A map with a name in it twice, which is what a series wearing one
22827            // label name twice turns into.
22828            (
22829                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
22830                "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
22831                 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
22832            ),
22833        ];
22834        for (argv, want) in cases {
22835            let got = f.run(argv);
22836            assert_eq!(&got, want, "{:?}", argv.last());
22837        }
22838    }
22839
22840    /// The same five series with enough samples in them for a group to have
22841    /// something to fold.
22842    fn spanned() -> Fixture {
22843        let mut f = labelled();
22844        f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
22845        f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
22846        f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
22847        f
22848    }
22849
22850    /// A span read out of every series a filter takes, with and without a group
22851    /// over the top of it.
22852    #[test]
22853    fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
22854        let mut f = spanned();
22855        let cases: &[(&[&[u8]], &str)] = &[
22856            (
22857                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
22858                "*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\
22859                 *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",
22860            ),
22861            // Newest first is applied to each series before anything else sees
22862            // the rows.
22863            (
22864                &[
22865                    b"TS.MREVRANGE",
22866                    b"-",
22867                    b"+",
22868                    b"WITHLABELS",
22869                    b"FILTER",
22870                    b"room=kitchen",
22871                ],
22872                "*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\
22873                 *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\
22874                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
22875                 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
22876            ),
22877            // A label a series does not wear comes back against a nil rather
22878            // than being left out.
22879            (
22880                &[
22881                    b"TS.MRANGE",
22882                    b"-",
22883                    b"+",
22884                    b"SELECTED_LABELS",
22885                    b"x",
22886                    b"FILTER",
22887                    b"room=kitchen",
22888                ],
22889                "*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\
22890                 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
22891                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
22892                 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
22893            ),
22894            // The fold: 100 is in both series and adds up, the other two are in
22895            // one each and are still rows.
22896            (
22897                &[
22898                    b"TS.MRANGE",
22899                    b"-",
22900                    b"+",
22901                    b"FILTER",
22902                    b"room=kitchen",
22903                    b"GROUPBY",
22904                    b"room",
22905                    b"REDUCE",
22906                    b"sum",
22907                ],
22908                "*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\
22909                 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
22910            ),
22911            // RESP2 has nowhere to put the reducer and the member keys, so a
22912            // group wearing labels writes them as two more labels.
22913            (
22914                &[
22915                    b"TS.MRANGE",
22916                    b"-",
22917                    b"+",
22918                    b"WITHLABELS",
22919                    b"FILTER",
22920                    b"room=kitchen",
22921                    b"GROUPBY",
22922                    b"room",
22923                    b"REDUCE",
22924                    b"max",
22925                ],
22926                "*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\
22927                 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
22928                 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
22929                 *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",
22930            ),
22931            // A count is applied to each member and then again to the fold.
22932            (
22933                &[
22934                    b"TS.MREVRANGE",
22935                    b"-",
22936                    b"+",
22937                    b"COUNT",
22938                    b"1",
22939                    b"FILTER",
22940                    b"room=kitchen",
22941                    b"GROUPBY",
22942                    b"room",
22943                    b"REDUCE",
22944                    b"count",
22945                ],
22946                "*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",
22947            ),
22948            // Nothing wears the label, so nothing is in any group.
22949            (
22950                &[
22951                    b"TS.MRANGE",
22952                    b"-",
22953                    b"+",
22954                    b"FILTER",
22955                    b"room=kitchen",
22956                    b"GROUPBY",
22957                    b"nope",
22958                    b"REDUCE",
22959                    b"sum",
22960                ],
22961                "*0\r\n",
22962            ),
22963            (
22964                &[
22965                    b"TS.MRANGE",
22966                    b"-",
22967                    b"+",
22968                    b"AGGREGATION",
22969                    b"sum,avg",
22970                    b"100",
22971                    b"FILTER",
22972                    b"room=bedroom",
22973                ],
22974                "*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",
22975            ),
22976            // The errors, in the order they are looked for.
22977            (
22978                &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
22979                "-ERR TSDB: missing FILTER argument\r\n",
22980            ),
22981            (
22982                &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
22983                "-ERR TSDB: missing labels for filter argument\r\n",
22984            ),
22985            (
22986                &[
22987                    b"TS.MRANGE",
22988                    b"-",
22989                    b"+",
22990                    b"GROUPBY",
22991                    b"room",
22992                    b"REDUCE",
22993                    b"sum",
22994                    b"FILTER",
22995                    b"room=kitchen",
22996                ],
22997                "-ERR TSDB: GROUPBY should always come after filter\r\n",
22998            ),
22999            // The group is four words from the end here, so the length is what
23000            // is wrong with it.
23001            (
23002                &[
23003                    b"TS.MRANGE",
23004                    b"-",
23005                    b"+",
23006                    b"FILTER",
23007                    b"room=kitchen",
23008                    b"GROUPBY",
23009                    b"room",
23010                    b"REDUCE",
23011                    b"sum",
23012                    b"x",
23013                ],
23014                "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
23015            ),
23016            // And here it is not, so its words are filters and answer first.
23017            (
23018                &[
23019                    b"TS.MRANGE",
23020                    b"-",
23021                    b"+",
23022                    b"FILTER",
23023                    b"nope",
23024                    b"GROUPBY",
23025                    b"room",
23026                    b"REDUCE",
23027                    b"sum",
23028                    b"x",
23029                ],
23030                "-ERR TSDB: failed parsing labels\r\n",
23031            ),
23032            (
23033                &[
23034                    b"TS.MRANGE",
23035                    b"-",
23036                    b"+",
23037                    b"FILTER",
23038                    b"room=kitchen",
23039                    b"GROUPBY",
23040                    b"room",
23041                    b"REDUCE",
23042                    b"twa",
23043                ],
23044                "-ERR TSDB: Invalid reducer type\r\n",
23045            ),
23046            (
23047                &[
23048                    b"TS.MRANGE",
23049                    b"-",
23050                    b"+",
23051                    b"AGGREGATION",
23052                    b"sum,avg",
23053                    b"100",
23054                    b"FILTER",
23055                    b"room=kitchen",
23056                    b"GROUPBY",
23057                    b"room",
23058                    b"REDUCE",
23059                    b"sum",
23060                ],
23061                "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
23062            ),
23063            // The label list ends at a keyword, so this is a `COUNT` with a
23064            // `FILTER` where its number should be.
23065            (
23066                &[
23067                    b"TS.MRANGE",
23068                    b"-",
23069                    b"+",
23070                    b"SELECTED_LABELS",
23071                    b"COUNT",
23072                    b"FILTER",
23073                    b"room=kitchen",
23074                ],
23075                "-ERR TSDB: Couldn't parse COUNT\r\n",
23076            ),
23077        ];
23078        for (argv, want) in cases {
23079            let got = f.run(argv);
23080            assert_eq!(&got, want, "{argv:?}");
23081        }
23082    }
23083
23084    /// The multi key reads on RESP3, where the key becomes a map key and the
23085    /// reducer and the member keys become fields of their own.
23086    #[test]
23087    fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
23088        let mut f = spanned();
23089        f.out = Out::new(Proto::Resp3);
23090        let cases: &[(&[&[u8]], &str)] = &[
23091            (
23092                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
23093                "%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\
23094                 *1\r\n*2\r\n:200\r\n,2\r\n",
23095            ),
23096            // The reductions a read asked for, which RESP2 has no room for at
23097            // all and which is empty on a read that asked for none.
23098            (
23099                &[
23100                    b"TS.MRANGE",
23101                    b"-",
23102                    b"+",
23103                    b"AGGREGATION",
23104                    b"sum,avg",
23105                    b"100",
23106                    b"FILTER",
23107                    b"room=bedroom",
23108                ],
23109                "%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\
23110                 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
23111            ),
23112            (
23113                &[
23114                    b"TS.MRANGE",
23115                    b"-",
23116                    b"+",
23117                    b"FILTER",
23118                    b"room=kitchen",
23119                    b"GROUPBY",
23120                    b"room",
23121                    b"REDUCE",
23122                    b"sum",
23123                ],
23124                "%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\
23125                 $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\
23126                 *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",
23127            ),
23128            // The labels hold only the pair the group was made on, because the
23129            // reducer and the sources have somewhere else to go.
23130            (
23131                &[
23132                    b"TS.MRANGE",
23133                    b"-",
23134                    b"+",
23135                    b"WITHLABELS",
23136                    b"FILTER",
23137                    b"room=kitchen",
23138                    b"GROUPBY",
23139                    b"room",
23140                    b"REDUCE",
23141                    b"max",
23142                ],
23143                "%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\
23144                 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
23145                 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
23146                 *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",
23147            ),
23148            (
23149                &[
23150                    b"TS.MRANGE",
23151                    b"-",
23152                    b"+",
23153                    b"FILTER",
23154                    b"room=kitchen",
23155                    b"GROUPBY",
23156                    b"nope",
23157                    b"REDUCE",
23158                    b"sum",
23159                ],
23160                "%0\r\n",
23161            ),
23162        ];
23163        for (argv, want) in cases {
23164            let got = f.run(argv);
23165            assert_eq!(&got, want, "{argv:?}");
23166        }
23167    }
23168
23169    /// `TS.CREATERULE`, whose refusals come in an order of their own.
23170    #[test]
23171    fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
23172        let mut f = Fixture::new();
23173        f.run(&[b"TS.CREATE", b"src"]);
23174        f.run(&[b"TS.CREATE", b"dst"]);
23175        f.run(&[b"SET", b"plain", b"v"]);
23176        let cases: &[(&[&[u8]], &str)] = &[
23177            // The width is read before the reduction, the reduction before the
23178            // width being above zero, and all three before either key is looked
23179            // at, so a command that is wrong twice complains about the first.
23180            (
23181                &[
23182                    b"TS.CREATERULE",
23183                    b"src",
23184                    b"dst",
23185                    b"AGGREGATION",
23186                    b"nope",
23187                    b"x",
23188                ],
23189                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
23190            ),
23191            (
23192                &[
23193                    b"TS.CREATERULE",
23194                    b"src",
23195                    b"dst",
23196                    b"AGGREGATION",
23197                    b"nope",
23198                    b"10",
23199                ],
23200                "-ERR TSDB: Unknown aggregation type\r\n",
23201            ),
23202            (
23203                &[
23204                    b"TS.CREATERULE",
23205                    b"src",
23206                    b"dst",
23207                    b"AGGREGATION",
23208                    b"avg",
23209                    b"0",
23210                ],
23211                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
23212            ),
23213            (
23214                &[
23215                    b"TS.CREATERULE",
23216                    b"src",
23217                    b"dst",
23218                    b"AGGREGATION",
23219                    b"avg",
23220                    b"10",
23221                    b"x",
23222                ],
23223                "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
23224            ),
23225            (
23226                &[
23227                    b"TS.CREATERULE",
23228                    b"src",
23229                    b"src",
23230                    b"AGGREGATION",
23231                    b"avg",
23232                    b"10",
23233                ],
23234                "-ERR TSDB: the source key and destination key should be different\r\n",
23235            ),
23236            // A key holding something else answers the same as a key that is not
23237            // there at all, because the source is looked up first and neither of
23238            // them is a series.
23239            (
23240                &[
23241                    b"TS.CREATERULE",
23242                    b"nope",
23243                    b"plain",
23244                    b"AGGREGATION",
23245                    b"avg",
23246                    b"10",
23247                ],
23248                "-ERR TSDB: the key does not exist\r\n",
23249            ),
23250            (
23251                &[
23252                    b"TS.CREATERULE",
23253                    b"src",
23254                    b"nope",
23255                    b"AGGREGATION",
23256                    b"avg",
23257                    b"10",
23258                ],
23259                "-ERR TSDB: the key does not exist\r\n",
23260            ),
23261            // A keyword other than AGGREGATION is an arity error rather than a
23262            // syntax one, because the arity is all that is checked.
23263            (
23264                &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
23265                "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
23266            ),
23267            (
23268                &[
23269                    b"TS.CREATERULE",
23270                    b"src",
23271                    b"dst",
23272                    b"AGGREGATION",
23273                    b"avg",
23274                    b"10",
23275                ],
23276                "+OK\r\n",
23277            ),
23278            // The link is now in place, so the same rule again is refused from
23279            // the destination's end.
23280            (
23281                &[
23282                    b"TS.CREATERULE",
23283                    b"src",
23284                    b"dst",
23285                    b"AGGREGATION",
23286                    b"avg",
23287                    b"10",
23288                ],
23289                "-ERR TSDB: the destination key already has a src rule\r\n",
23290            ),
23291            // A source that is already someone's destination, and a destination
23292            // that is already someone's source, are two different sentences.
23293            (
23294                &[
23295                    b"TS.CREATERULE",
23296                    b"dst",
23297                    b"src",
23298                    b"AGGREGATION",
23299                    b"avg",
23300                    b"10",
23301                ],
23302                "-ERR TSDB: the source key already has a source rule\r\n",
23303            ),
23304            (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
23305            (
23306                &[b"TS.DELETERULE", b"src", b"dst"],
23307                "-ERR TSDB: compaction rule does not exist\r\n",
23308            ),
23309            // The source is looked up and the destination is not, so a missing
23310            // destination is a missing rule and a missing source is a missing
23311            // key, which is the other way round from `TS.CREATERULE`.
23312            (
23313                &[b"TS.DELETERULE", b"src", b"nope"],
23314                "-ERR TSDB: compaction rule does not exist\r\n",
23315            ),
23316            (
23317                &[b"TS.DELETERULE", b"nope", b"dst"],
23318                "-ERR TSDB: the key does not exist\r\n",
23319            ),
23320        ];
23321        for (argv, want) in cases {
23322            let got = f.run(argv);
23323            assert_eq!(&got, want, "{argv:?}");
23324        }
23325    }
23326
23327    /// What a rule writes, which is every bucket but the one it is filling.
23328    #[test]
23329    fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
23330        let mut f = Fixture::new();
23331        f.run(&[b"TS.CREATE", b"src"]);
23332        f.run(&[b"TS.CREATE", b"dst"]);
23333        // The readings written before the rule was made are not folded, so the
23334        // destination is still empty after the first two.
23335        f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
23336        f.run(&[
23337            b"TS.CREATERULE",
23338            b"src",
23339            b"dst",
23340            b"AGGREGATION",
23341            b"sum",
23342            b"100",
23343        ]);
23344        f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
23345        assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
23346        // The bucket the rule is filling holds only what it was given, so it is
23347        // 2 rather than 3, and it is written when a reading lands past it.
23348        assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
23349        f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
23350        assert_eq!(
23351            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
23352            "*1\r\n*2\r\n:0\r\n+2\r\n"
23353        );
23354        // A reading into a bucket that has already been written works that
23355        // bucket out again over everything the source now holds.
23356        f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
23357        assert_eq!(
23358            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
23359            "*1\r\n*2\r\n:0\r\n+11\r\n"
23360        );
23361        // Deleting from the source works the buckets it touched out again and
23362        // reopens the newest one, so `LATEST` starts from the whole bucket.
23363        assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
23364        assert_eq!(
23365            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
23366            "*1\r\n*2\r\n:0\r\n+8\r\n"
23367        );
23368        assert_eq!(
23369            f.run(&[b"TS.GET", b"dst", b"LATEST"]),
23370            "*2\r\n:100\r\n+4\r\n"
23371        );
23372        // The link shows on both ends, and dropping either key takes it down.
23373        assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
23374        f.run(&[b"DEL", b"dst"]);
23375        assert_eq!(
23376            f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
23377            "-ERR TSDB: compaction rule does not exist\r\n"
23378        );
23379    }
23380
23381    /// The three shapes an `XADD` id can take, and the one rule behind all of
23382    /// them.
23383    #[test]
23384    fn xadd_ids_only_ever_go_up() {
23385        let mut f = Fixture::new();
23386        // A bare millisecond is that millisecond and sequence zero.
23387        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
23388        // And `5-*` is the next free sequence inside it.
23389        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
23390        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
23391        assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
23392        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
23393
23394        assert!(
23395            f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
23396                .contains("equal or smaller")
23397        );
23398        assert!(
23399            f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
23400                .contains("must be greater than 0-0")
23401        );
23402        assert!(
23403            f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
23404                .contains("Invalid stream ID")
23405        );
23406        // The pairs have to be pairs, and Redis calls an odd one an arity error
23407        // rather than a syntax error even though the table has already passed.
23408        assert!(
23409            f.run(&[b"XADD", b"s", b"*", b"a"])
23410                .contains("wrong number of arguments")
23411        );
23412
23413        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
23414        // producer can tell nobody is consuming this yet from the write landed.
23415        assert_eq!(
23416            f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
23417            "$-1\r\n"
23418        );
23419        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
23420        assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
23421        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
23422    }
23423
23424    /// The trim options, which are three keywords that disagree about how many
23425    /// arguments they take.
23426    #[test]
23427    fn trimming_reads_its_options_the_way_redis_does() {
23428        let mut f = Fixture::new();
23429        for i in 1..=10u32 {
23430            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
23431        }
23432        assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
23433        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
23434        assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
23435        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
23436
23437        // One argument after the keyword and the `~` is read as the threshold,
23438        // which is what a real server does and is the reason this is a number
23439        // complaint and not a syntax one.
23440        assert!(
23441            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
23442                .contains("not an integer")
23443        );
23444        assert!(
23445            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
23446                .contains("MAXLEN argument must be >= 0")
23447        );
23448        // The strategy check runs before the approximation check, so a LIMIT
23449        // with neither is told about the missing strategy.
23450        assert!(
23451            f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
23452                .contains("without specifying a trimming strategy")
23453        );
23454        assert!(
23455            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
23456                .contains("without the special ~ option")
23457        );
23458        assert!(
23459            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
23460                .contains("at the same time are not compatible")
23461        );
23462        // NOMKSTREAM is XADD's and XTRIM does not take it.
23463        assert!(
23464            f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
23465                .contains("syntax error")
23466        );
23467        assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
23468    }
23469
23470    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
23471    #[test]
23472    fn xrange_looks_the_key_up_before_it_reads_the_count() {
23473        let mut f = Fixture::new();
23474        f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
23475        f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
23476
23477        assert_eq!(
23478            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
23479            "*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\
23480             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
23481        );
23482        assert_eq!(
23483            f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
23484            "*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"
23485        );
23486        // The exclusive bound is stepped after the missing sequence is filled
23487        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
23488        // `6-1` is still in the range.
23489        assert_eq!(
23490            f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
23491            "*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\
23492             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
23493        );
23494        assert_eq!(
23495            f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
23496            "*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"
23497        );
23498        assert!(
23499            f.run(&[b"XRANGE", b"s", b"(-", b"+"])
23500                .contains("Invalid stream ID")
23501        );
23502
23503        // The two kinds of nothing. A key that is not there is an empty array
23504        // and a key that is there with a count of zero is a null array, because
23505        // the lookup happens first.
23506        assert_eq!(
23507            f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
23508            "*0\r\n"
23509        );
23510        assert_eq!(
23511            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
23512            "*-1\r\n"
23513        );
23514        f.run(&[b"SET", b"str", b"v"]);
23515        assert!(
23516            f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
23517                .starts_with("-WRONGTYPE")
23518        );
23519        // The count is read in a loop, so the last one wins.
23520        assert_eq!(
23521            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
23522            "*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"
23523        );
23524    }
23525
23526    /// `XDEL` and `XACK` check every id before they touch any of them.
23527    #[test]
23528    fn a_bad_id_late_in_the_list_stops_the_whole_command() {
23529        let mut f = Fixture::new();
23530        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
23531        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
23532        assert!(
23533            f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
23534                .contains("Invalid stream ID")
23535        );
23536        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
23537        assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
23538        assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
23539        assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
23540        assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
23541    }
23542
23543    /// `XGROUP`, and the two different complaints it makes about arguments.
23544    #[test]
23545    fn xgroup_has_an_arity_per_subcommand() {
23546        let mut f = Fixture::new();
23547        assert!(
23548            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
23549                .contains("requires the key")
23550        );
23551        assert_eq!(
23552            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
23553            "+OK\r\n"
23554        );
23555        // A second CREATE is BUSYGROUP and not an ordinary error, because a
23556        // client racing another one to make a group branches on the prefix.
23557        assert!(
23558            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
23559                .starts_with("-BUSYGROUP")
23560        );
23561        assert_eq!(
23562            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
23563            ":1\r\n"
23564        );
23565        assert_eq!(
23566            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
23567            ":0\r\n"
23568        );
23569        assert_eq!(
23570            f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
23571            ":0\r\n"
23572        );
23573
23574        // Below the subcommand's own arity is an arity error naming the pair.
23575        let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
23576        assert!(
23577            short.contains("wrong number of arguments for 'xgroup|destroy' command"),
23578            "{short}"
23579        );
23580        // At or above it in a shape the handler will not take is the other one.
23581        let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
23582        assert!(
23583            odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
23584            "{odd}"
23585        );
23586        assert!(
23587            f.run(&[b"XGROUP", b"NOSUCH", b"s"])
23588                .contains("Try XGROUP HELP")
23589        );
23590
23591        assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
23592        assert!(
23593            f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
23594                .starts_with("-NOGROUP")
23595        );
23596        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
23597        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
23598        assert!(
23599            f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
23600                .contains("requires the key")
23601        );
23602    }
23603
23604    /// A group read, an acknowledgement, and what is left in between.
23605    #[test]
23606    fn xreadgroup_hands_out_and_xack_takes_back() {
23607        let mut f = Fixture::new();
23608        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
23609        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
23610        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
23611
23612        let first = f.run(&[
23613            b"XREADGROUP",
23614            b"GROUP",
23615            b"g",
23616            b"c1",
23617            b"COUNT",
23618            b"1",
23619            b"STREAMS",
23620            b"s",
23621            b">",
23622        ]);
23623        assert_eq!(
23624            first,
23625            "*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"
23626        );
23627        // A history read names its stream even with nothing to show, which is
23628        // the difference between it and a `>` read that found nothing.
23629        assert_eq!(
23630            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
23631            "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
23632        );
23633        assert_eq!(
23634            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
23635            "*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"
23636        );
23637
23638        assert_eq!(
23639            f.run(&[b"XPENDING", b"s", b"g"]),
23640            "*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"
23641        );
23642        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
23643        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
23644        // Empty is four nulls and not a zero with three empty things.
23645        assert_eq!(
23646            f.run(&[b"XPENDING", b"s", b"g"]),
23647            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
23648        );
23649
23650        // A history read of an entry that has since been deleted is the id with
23651        // a null beside it, so the consumer can still acknowledge it.
23652        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
23653        f.run(&[b"XDEL", b"s", b"2-1"]);
23654        assert_eq!(
23655            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
23656            "*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"
23657        );
23658
23659        // The group lookup runs before the id parse, so a `+` at a stream with
23660        // no such group is told about the group and not about the id.
23661        assert!(
23662            f.run(&[
23663                b"XREADGROUP",
23664                b"GROUP",
23665                b"nope",
23666                b"c",
23667                b"STREAMS",
23668                b"s",
23669                b"+"
23670            ])
23671            .starts_with("-NOGROUP")
23672        );
23673        assert!(
23674            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
23675                .contains("meaningless in the context of XREADGROUP")
23676        );
23677        assert!(
23678            f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
23679                .contains("only supported by XREADGROUP")
23680        );
23681        assert!(
23682            f.run(&[
23683                b"XREADGROUP",
23684                b"GROUP",
23685                b"g",
23686                b"c",
23687                b"STREAMS",
23688                b"s",
23689                b"a",
23690                b"b"
23691            ])
23692            .contains("Unbalanced 'xreadgroup' list of streams")
23693        );
23694    }
23695
23696    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
23697    /// answer.
23698    #[test]
23699    fn xread_with_no_block_writes_the_null_itself() {
23700        let mut f = Fixture::new();
23701        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
23702        assert_eq!(
23703            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
23704            "*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"
23705        );
23706        // Nothing new is a null array and not an empty one, and a stream with
23707        // nothing new is left out rather than sent with an empty list.
23708        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
23709        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
23710        f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
23711        assert_eq!(
23712            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
23713            "*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"
23714        );
23715        // `$` is the last id, so nothing that is already there comes back.
23716        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
23717        // And `+` is the last entry, whatever COUNT says.
23718        assert_eq!(
23719            f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
23720            "*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"
23721        );
23722        // A count of zero means unlimited here, which is the opposite of what it
23723        // means to XRANGE.
23724        assert_eq!(
23725            f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
23726            "*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"
23727        );
23728        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
23729        assert!(
23730            f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
23731                .contains("not an integer")
23732        );
23733        assert!(
23734            f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
23735                .contains("timeout is negative")
23736        );
23737        assert!(
23738            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
23739                .contains("Unbalanced 'xread' list of streams")
23740        );
23741    }
23742
23743    /// A blocked reader, and the two ways it stops being blocked.
23744    #[test]
23745    fn a_blocked_xread_wakes_on_the_next_entry() {
23746        let mut f = Fixture::new();
23747        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
23748        let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
23749        assert_eq!(flow, Flow::Block);
23750        assert!(reply.is_empty());
23751
23752        // Everybody parked on the stream gets the entry, because a read takes
23753        // nothing away. That is the difference between this and BLPOP. Two
23754        // clients rather than one twice, since a client that is waiting is not
23755        // reading and cannot block again.
23756        f.session = Session::new(8);
23757        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
23758        assert_eq!(flow, Flow::Block);
23759        assert_eq!(f.server.parked(), 2);
23760
23761        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
23762        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";
23763        for client in [7, 8] {
23764            let mut out = Out::new(Proto::Resp2);
23765            assert!(f.server.serve_waiter(client, 0, &mut out));
23766            assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
23767        }
23768
23769        // And a deadline that runs out is a null array, the same as a plain
23770        // XREAD that found nothing.
23771        f.server.forget_waiters(7);
23772        f.server.forget_waiters(8);
23773        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
23774        assert_eq!(flow, Flow::Block);
23775        let mut out = Out::new(Proto::Resp2);
23776        assert!(!f.server.serve_waiter(8, 0, &mut out));
23777        assert!(out.as_slice().is_empty());
23778        assert!(f.server.serve_waiter(8, u64::MAX, &mut out));
23779        assert_eq!(
23780            core::str::from_utf8(out.as_slice()).expect("ascii"),
23781            "*-1\r\n"
23782        );
23783    }
23784
23785    /// A blocked group reader whose group is destroyed under it.
23786    #[test]
23787    fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
23788        let mut f = Fixture::new();
23789        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
23790        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
23791        let (flow, _) = f.flow(&[
23792            b"XREADGROUP",
23793            b"GROUP",
23794            b"g",
23795            b"c",
23796            b"BLOCK",
23797            b"0",
23798            b"STREAMS",
23799            b"s",
23800            b">",
23801        ]);
23802        assert_eq!(flow, Flow::Block);
23803
23804        f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
23805        let mut out = Out::new(Proto::Resp2);
23806        assert!(f.server.serve_waiter(7, 0, &mut out));
23807        // The ordinary sentence and not a special one about having been parked,
23808        // which is what a running 8.10 sends.
23809        assert_eq!(
23810            core::str::from_utf8(out.as_slice()).expect("ascii"),
23811            "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
23812        );
23813    }
23814
23815    /// `XCLAIM`, whose argument shape is the odd one in the group.
23816    #[test]
23817    fn xclaim_reads_ids_until_one_will_not_parse() {
23818        let mut f = Fixture::new();
23819        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
23820        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
23821        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
23822        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
23823
23824        // Everything after the first argument that is not an id is an option, so
23825        // a `-` is an unrecognised option and not a bad id.
23826        assert!(
23827            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
23828                .contains("Unrecognized XCLAIM option '-'")
23829        );
23830        assert_eq!(
23831            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
23832            "*1\r\n$3\r\n1-1\r\n"
23833        );
23834        // An id that is pending but whose entry has gone is an empty answer, and
23835        // it leaves the pending list on the way past.
23836        f.run(&[b"XDEL", b"s", b"2-1"]);
23837        assert_eq!(
23838            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
23839            "*0\r\n"
23840        );
23841        assert!(
23842            f.run(&[b"XPENDING", b"s", b"g"])
23843                .starts_with("*4\r\n:1\r\n")
23844        );
23845        assert!(
23846            f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
23847                .starts_with("-NOGROUP")
23848        );
23849        assert!(
23850            f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
23851                .contains("Invalid min-idle-time argument for XCLAIM")
23852        );
23853    }
23854
23855    /// `XAUTOCLAIM`, and the third value nobody expects.
23856    #[test]
23857    fn xautoclaim_reports_what_it_dropped() {
23858        let mut f = Fixture::new();
23859        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
23860        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
23861        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
23862        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
23863        f.run(&[b"XDEL", b"s", b"1-1"]);
23864
23865        // The cursor, what was claimed, and what was dropped for no longer being
23866        // in the stream. The third one is what makes a sweep converge.
23867        assert_eq!(
23868            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
23869            "*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"
23870        );
23871        assert!(
23872            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
23873                .contains("COUNT must be > 0")
23874        );
23875        assert!(
23876            f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
23877                .starts_with("-NOGROUP")
23878        );
23879    }
23880
23881    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
23882    #[test]
23883    fn xdelex_answers_one_integer_an_id() {
23884        let mut f = Fixture::new();
23885        for i in 1..=4 {
23886            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
23887        }
23888        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
23889        f.run(&[
23890            b"XREADGROUP",
23891            b"GROUP",
23892            b"g",
23893            b"c",
23894            b"COUNT",
23895            b"2",
23896            b"STREAMS",
23897            b"s",
23898            b">",
23899        ]);
23900
23901        // One means gone and minus one means it was not there to start with.
23902        assert_eq!(
23903            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
23904            "*2\r\n:1\r\n:-1\r\n"
23905        );
23906        // `KEEPREF` leaves the pending entry behind, so the group still counts
23907        // the one it was handed even though the entry has gone.
23908        assert!(
23909            f.run(&[b"XPENDING", b"s", b"g"])
23910                .starts_with("*4\r\n:2\r\n")
23911        );
23912        // `DELREF` takes it out of every pending list on the way past.
23913        assert_eq!(
23914            f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
23915            "*1\r\n:1\r\n"
23916        );
23917        // `1-1` is still in the list, because the delete before it said KEEPREF.
23918        assert_eq!(
23919            f.run(&[b"XPENDING", b"s", b"g"]),
23920            "*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"
23921        );
23922
23923        // Two means somebody still wants it, and the question is wider than the
23924        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
23925        // refused even though no consumer has ever been handed it.
23926        assert_eq!(
23927            f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
23928            "*2\r\n:2\r\n:2\r\n"
23929        );
23930
23931        // A key that is not there answers minus ones without reading the IDs.
23932        assert_eq!(
23933            f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
23934            "*2\r\n:-1\r\n:-1\r\n"
23935        );
23936        // A key that is there validates every ID before deleting any of them.
23937        assert!(
23938            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
23939                .starts_with("-ERR Invalid stream ID")
23940        );
23941        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
23942
23943        assert!(
23944            f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
23945                .contains("Number of IDs must be a positive integer")
23946        );
23947        assert!(
23948            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
23949                .contains("The `numids` parameter must match the number of arguments")
23950        );
23951        // The condition is one word, so a second one is a syntax error, and so
23952        // is one ID more than the count promised.
23953        assert!(
23954            f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
23955                .starts_with("-ERR syntax error")
23956        );
23957        assert!(
23958            f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
23959                .starts_with("-ERR syntax error")
23960        );
23961        // The key is looked up first, so the wrong type beats the syntax.
23962        f.run(&[b"SET", b"str", b"v"]);
23963        assert!(
23964            f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
23965                .starts_with("-WRONGTYPE")
23966        );
23967    }
23968
23969    /// `XACKDEL`, whose reply is about the pending list and not about the log.
23970    #[test]
23971    fn xackdel_reports_what_the_group_was_holding() {
23972        let mut f = Fixture::new();
23973        for i in 1..=3 {
23974            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
23975        }
23976        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
23977        f.run(&[
23978            b"XREADGROUP",
23979            b"GROUP",
23980            b"g",
23981            b"c",
23982            b"COUNT",
23983            b"1",
23984            b"STREAMS",
23985            b"s",
23986            b">",
23987        ]);
23988
23989        // Minus one is not about the stream: `2-1` is sitting there unread and
23990        // still answers minus one, because the group was not holding it. It also
23991        // stays, since only an ID that was acknowledged is deleted.
23992        assert_eq!(
23993            f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
23994            "*2\r\n:1\r\n:-1\r\n"
23995        );
23996        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
23997
23998        // A missing group is minus one an ID and not a NOGROUP.
23999        assert_eq!(
24000            f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
24001            "*1\r\n:-1\r\n"
24002        );
24003        assert_eq!(
24004            f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
24005            "*1\r\n:-1\r\n"
24006        );
24007
24008        // The acknowledgement happens whatever the condition says, so an ACKED
24009        // that answers two has still emptied the pending list.
24010        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
24011        f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
24012        assert_eq!(
24013            f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
24014            "*1\r\n:2\r\n"
24015        );
24016        assert_eq!(
24017            f.run(&[b"XPENDING", b"s", b"g"]),
24018            "*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"
24019        );
24020        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
24021    }
24022
24023    /// `XNACK`, which hands an entry back to nobody.
24024    #[test]
24025    fn xnack_releases_an_entry_for_the_next_claim() {
24026        let mut f = Fixture::new();
24027        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24028        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
24029        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24030        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
24031        // Twice, so the delivery count is two and the words have something to
24032        // do with it.
24033        f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
24034
24035        assert_eq!(
24036            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
24037            ":1\r\n"
24038        );
24039        // No owner, no idle time, and the count left where it was. A released
24040        // entry reads as idle for longer than any min-idle-time, which is what
24041        // puts it at the front of the next claim.
24042        assert_eq!(
24043            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
24044            "*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"
24045        );
24046        // The consumer no longer holds it, so a filtered XPENDING skips it.
24047        assert_eq!(
24048            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
24049            "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
24050        );
24051        // The bookmark did not move, so a `>` read will not hand it out again.
24052        assert_eq!(
24053            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
24054            "*-1\r\n"
24055        );
24056        // A claim at any min-idle-time takes it.
24057        assert_eq!(
24058            f.run(&[
24059                b"XAUTOCLAIM",
24060                b"s",
24061                b"g",
24062                b"c2",
24063                b"99999999",
24064                b"-",
24065                b"JUSTID"
24066            ]),
24067            "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
24068        );
24069
24070        // `SILENT` takes one off the count rather than putting it back to zero,
24071        // which only shows on an entry that has been handed out more than once.
24072        // It was delivered and then claimed, so it is on two and goes to one.
24073        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
24074        assert!(
24075            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24076                .contains(":-1\r\n:1\r\n")
24077        );
24078        // And it stops at zero rather than wrapping.
24079        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
24080        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
24081        assert!(
24082            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24083                .contains(":-1\r\n:0\r\n")
24084        );
24085        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
24086        f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
24087        assert!(
24088            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24089                .contains(":9223372036854775807\r\n")
24090        );
24091        f.run(&[
24092            b"XNACK",
24093            b"s",
24094            b"g",
24095            b"FATAL",
24096            b"IDS",
24097            b"1",
24098            b"1-1",
24099            b"RETRYCOUNT",
24100            b"3",
24101        ]);
24102        assert!(
24103            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24104                .contains(":-1\r\n:3\r\n")
24105        );
24106
24107        // Releasing something the group is not holding is zero, and `FORCE`
24108        // makes the pending entry rather than answering zero. A forced entry
24109        // starts at zero, since there was no earlier count to keep.
24110        f.run(&[b"XACK", b"s", b"g", b"2-1"]);
24111        assert_eq!(
24112            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
24113            ":0\r\n"
24114        );
24115        assert_eq!(
24116            f.run(&[
24117                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
24118            ]),
24119            ":1\r\n"
24120        );
24121        assert!(
24122            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
24123                .contains(":-1\r\n:0\r\n")
24124        );
24125        // `FORCE` on an ID the stream does not have is still zero.
24126        assert_eq!(
24127            f.run(&[
24128                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
24129            ]),
24130            ":0\r\n"
24131        );
24132
24133        // The group is looked up before the mode word, and it raises rather
24134        // than answering per ID the way the two delete commands do.
24135        assert_eq!(
24136            f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
24137            "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
24138        );
24139        assert!(
24140            f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
24141                .starts_with("-ERR")
24142        );
24143        // Its own sentences, which are not the ones XDELEX uses.
24144        assert!(
24145            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
24146                .contains("numids must be a positive integer")
24147        );
24148        assert!(
24149            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
24150                .contains("number of IDs doesn't match numids")
24151        );
24152        // Everything past the counted IDs is an option, so one too many is an
24153        // option nobody recognises and not a count that does not add up.
24154        assert!(
24155            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
24156                .contains("Unrecognized XNACK option '2-1'")
24157        );
24158    }
24159
24160    /// `XINFO`, which is where the shape of the storage shows through.
24161    #[test]
24162    fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
24163        let mut f = Fixture::new();
24164        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24165        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
24166        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24167        f.run(&[
24168            b"XREADGROUP",
24169            b"GROUP",
24170            b"g",
24171            b"c1",
24172            b"COUNT",
24173            b"1",
24174            b"STREAMS",
24175            b"s",
24176            b">",
24177        ]);
24178
24179        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
24180        // Ten pairs, since the six idempotency fields have nothing behind them
24181        // here and a zero would claim they had. That is D-27.
24182        assert!(info.starts_with("*20\r\n"), "{info}");
24183        assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
24184        assert!(
24185            info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
24186            "{info}"
24187        );
24188        assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
24189        assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
24190
24191        let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
24192        assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
24193        assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
24194        assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
24195        assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
24196
24197        // A consumer that has never been given anything reports minus one for
24198        // inactive rather than the moment it turned up, which is what tells a
24199        // worker that is stuck from one that has nothing to do.
24200        f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
24201        let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
24202        assert!(consumers.starts_with("*2\r\n"), "{consumers}");
24203        assert!(
24204            consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
24205            "{consumers}"
24206        );
24207        // And in name order, which the storage does not hold them in.
24208        let c1 = consumers.find("c1").unwrap();
24209        let c2 = consumers.find("c2").unwrap();
24210        assert!(c1 < c2, "{consumers}");
24211
24212        let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
24213        assert!(full.starts_with("*18\r\n"), "{full}");
24214        assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
24215        assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
24216
24217        assert!(
24218            f.run(&[b"XINFO", b"STREAM", b"missing"])
24219                .contains("no such key")
24220        );
24221        assert!(
24222            f.run(&[b"XINFO", b"GROUPS", b"missing"])
24223                .contains("no such key")
24224        );
24225        assert!(
24226            f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
24227                .starts_with("-NOGROUP")
24228        );
24229        assert!(
24230            f.run(&[b"XINFO", b"NOSUCH", b"s"])
24231                .contains("Try XINFO HELP")
24232        );
24233        assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
24234        assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
24235    }
24236
24237    /// `XPENDING`'s long form, which reads its arguments by counting them.
24238    #[test]
24239    fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
24240        let mut f = Fixture::new();
24241        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24242        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
24243        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
24244
24245        let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
24246        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");
24247        assert_eq!(
24248            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
24249            "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
24250        );
24251        // A consumer nobody has heard of holds nothing rather than erroring.
24252        assert_eq!(
24253            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
24254            "*0\r\n"
24255        );
24256        assert_eq!(
24257            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
24258            list
24259        );
24260        // IDLE is only read at position three.
24261        assert!(
24262            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
24263                .contains("syntax error")
24264        );
24265        assert!(
24266            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
24267                .contains("syntax error")
24268        );
24269        assert_eq!(
24270            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
24271            "*0\r\n"
24272        );
24273        assert!(
24274            f.run(&[b"XPENDING", b"missing", b"g"])
24275                .starts_with("-NOGROUP")
24276        );
24277    }
24278
24279    /// `XSETID`, which is three counters and two refusals.
24280    #[test]
24281    fn xsetid_will_not_go_below_what_is_there() {
24282        let mut f = Fixture::new();
24283        f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
24284        assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
24285        assert_eq!(
24286            f.run(&[
24287                b"XSETID",
24288                b"s",
24289                b"10-1",
24290                b"ENTRIESADDED",
24291                b"7",
24292                b"MAXDELETEDID",
24293                b"9-1"
24294            ]),
24295            "+OK\r\n"
24296        );
24297        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
24298        assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
24299        assert!(
24300            info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
24301            "{info}"
24302        );
24303
24304        assert!(
24305            f.run(&[b"XSETID", b"s", b"1-1"])
24306                .contains("smaller than the target stream top item")
24307        );
24308        assert!(
24309            f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
24310                .contains("entries_added must be positive")
24311        );
24312        assert!(
24313            f.run(&[b"XSETID", b"missing", b"1-1"])
24314                .contains("no such key")
24315        );
24316    }
24317
24318    /// RESP3, where the two reads answer a map and the entries stay an array.
24319    #[test]
24320    fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
24321        let mut f = Fixture::new();
24322        f.run(&[b"HELLO", b"3"]);
24323        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
24324        // A map header and then the key and the entries side by side, with no
24325        // two element array wrapping the pair.
24326        assert_eq!(
24327            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
24328            "%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"
24329        );
24330        // The fields are still one flat array and not a map, which is Redis's
24331        // shape and is what every consumer written before RESP3 expects.
24332        assert_eq!(
24333            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
24334            "*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"
24335        );
24336        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
24337    }
24338
24339    /// A store to migrate values into, so a test can watch the inversion.
24340    ///
24341    /// A vector rather than a file for the same reason the tier's own tests use
24342    /// one: the file work has not attached a real store yet, and what this is
24343    /// checking is the policy above the store rather than the store.
24344    struct Mem {
24345        blobs: Vec<Vec<u8>>,
24346    }
24347
24348    impl yo_kv::cold::Blocks for Mem {
24349        fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
24350            self.blobs.push(bytes.to_vec());
24351            Ok(yo_common::Addr::new(
24352                yo_common::Space::Log,
24353                (self.blobs.len() - 1) as u64,
24354            ))
24355        }
24356
24357        fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
24358            self.blobs
24359                .get(at.offset() as usize)
24360                .map(Vec::as_slice)
24361                .ok_or_else(|| {
24362                    yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
24363                })
24364        }
24365
24366        fn bytes(&self) -> u64 {
24367            self.blobs.iter().map(|b| b.len() as u64).sum()
24368        }
24369    }
24370
24371    /// A server holding several segments of strings, with somewhere to put them.
24372    ///
24373    /// Answers the fixture and what it was holding when it stopped filling.
24374    /// The three tests that call this are the ones Miri is not run over.
24375    ///
24376    /// What they are about is the regime a database is in once the arena has
24377    /// several segments, and a segment is two megabytes, so there is no smaller
24378    /// version of the question: twenty four thousand keys is already the least
24379    /// that gets there. Interpreted, each of them sat for over forty minutes
24380    /// and was still going. The arena's own segment handling is interpreted in
24381    /// full in its own crate, and the policy these three check is ordinary
24382    /// bookkeeping with no unsafe block anywhere in it.
24383    fn filled(attach: bool) -> (Fixture, usize) {
24384        let mut f = Fixture::new();
24385        if attach {
24386            f.server
24387                .striped(0)
24388                .hold_stripe(0)
24389                .attach(Box::new(Mem { blobs: Vec::new() }));
24390        }
24391        let val = vec![b'v'; 256];
24392        for i in 0..24000u32 {
24393            let k = format!("key:{i:08}");
24394            f.run(&[b"SET", k.as_bytes(), &val]);
24395        }
24396        let full = f.server.memory_bytes();
24397        assert!(full > 3 * 1024 * 1024, "the arena is several segments");
24398        (f, full)
24399    }
24400
24401    /// Write until the server is under `limit` or the writes run out.
24402    ///
24403    /// The same shape the eviction test uses. A memory limit is enforced in
24404    /// front of a command, so nothing happens until something is written, and
24405    /// the budget means one command does not do the whole job.
24406    fn press(f: &mut Fixture, limit: usize) {
24407        let val = vec![b'v'; 256];
24408        for i in 0..3000u32 {
24409            let k = format!("new:{i:08}");
24410            assert_eq!(
24411                f.run(&[b"SET", k.as_bytes(), &val]),
24412                "+OK\r\n",
24413                "write {i} was refused"
24414            );
24415            f.server.refresh_memory();
24416            if f.server.memory_bytes() <= limit {
24417                return;
24418            }
24419        }
24420        panic!(
24421            "it never got under: {} against {limit}",
24422            f.server.memory_bytes()
24423        );
24424    }
24425
24426    #[test]
24427    fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
24428        let mut f = Fixture::new();
24429        assert_eq!(
24430            f.run(&[b"CONFIG", b"GET", b"maxstore"]),
24431            "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
24432            "no limit is the default"
24433        );
24434        // The same memory value parser `maxmemory` uses, and the same trap in
24435        // it, plus the one spelling that means no limit at all.
24436        for (typed, bytes) in [
24437            (&b"0"[..], "0"),
24438            (b"1024", "1024"),
24439            (b"1k", "1000"),
24440            (b"1gb", "1073741824"),
24441            (b"-1", "-1"),
24442        ] {
24443            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
24444            assert_eq!(
24445                f.run(&[b"CONFIG", b"GET", b"maxstore"]),
24446                format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
24447                "set {}",
24448                String::from_utf8_lossy(typed)
24449            );
24450        }
24451        for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
24452            assert_eq!(
24453                f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
24454                "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
24455                "refused {}",
24456                String::from_utf8_lossy(bad)
24457            );
24458        }
24459        // Nothing is attached, so the answer to a memory limit is still Redis's.
24460        let info = f.run(&[b"INFO", b"memory"]);
24461        assert!(info.contains("maxstore:-1"), "{info}");
24462        assert!(info.contains("yo_memory_regime:evict"), "{info}");
24463        assert!(info.contains("yo_store_bytes:0"), "{info}");
24464    }
24465
24466    #[cfg_attr(miri, ignore = "several megabytes of arena, see `filled`")]
24467    #[test]
24468    fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
24469        // The inversion. The same pressure that makes a Redis server throw keys
24470        // away makes this one move values to the file, and afterwards every key
24471        // is still there and still answers with what was stored in it.
24472        let (mut f, full) = filled(true);
24473        let keys = f.run(&[b"DBSIZE"]);
24474        assert!(
24475            f.run(&[b"INFO", b"memory"])
24476                .contains("yo_memory_regime:migrate"),
24477            "a database with somewhere to put values migrates"
24478        );
24479
24480        let limit = full - 2 * 1024 * 1024;
24481        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
24482        f.run(&[
24483            b"CONFIG",
24484            b"SET",
24485            b"maxmemory",
24486            limit.to_string().as_bytes(),
24487        ]);
24488        press(&mut f, limit);
24489
24490        assert!(
24491            f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
24492            "nothing was thrown away"
24493        );
24494        let after: usize = f.run(&[b"DBSIZE"])[1..]
24495            .trim_end()
24496            .parse()
24497            .expect("a count");
24498        let before: usize = keys[1..].trim_end().parse().expect("a count");
24499        assert!(after > before, "the keys that came in are all still here");
24500        assert!(
24501            f.server.store_bytes() > 0,
24502            "and what came out of memory went to the file"
24503        );
24504        // And the values read back, which is the part that makes it a migration
24505        // rather than a loss.
24506        let val = format!("$256\r\n{}\r\n", "v".repeat(256));
24507        assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
24508        assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
24509    }
24510
24511    #[cfg_attr(miri, ignore = "several megabytes of arena, see `filled`")]
24512    #[test]
24513    fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
24514        // The documented setting for a drop in cache. A file that may hold
24515        // nothing cannot be migrated to, so eviction is all that is left, and
24516        // the server behaves exactly as it did before any of this existed.
24517        let (mut f, full) = filled(true);
24518        f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
24519        assert!(
24520            f.run(&[b"INFO", b"memory"])
24521                .contains("yo_memory_regime:evict"),
24522            "nothing may go to the file"
24523        );
24524
24525        let limit = full - 2 * 1024 * 1024;
24526        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
24527        f.run(&[
24528            b"CONFIG",
24529            b"SET",
24530            b"maxmemory",
24531            limit.to_string().as_bytes(),
24532        ]);
24533        press(&mut f, limit);
24534
24535        assert!(
24536            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
24537            "keys were thrown away, which is what was asked for"
24538        );
24539        assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
24540    }
24541
24542    #[cfg_attr(miri, ignore = "several megabytes of arena, see `filled`")]
24543    #[test]
24544    fn a_full_file_goes_back_to_evicting() {
24545        // A storage limit reached is a storage limit, and eviction is the right
24546        // answer to one. The budget here is a few kilobytes, so the first round
24547        // of migration fills it and everything after that is evicted.
24548        let (mut f, full) = filled(true);
24549        f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
24550        let limit = full - 2 * 1024 * 1024;
24551        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
24552        f.run(&[
24553            b"CONFIG",
24554            b"SET",
24555            b"maxmemory",
24556            limit.to_string().as_bytes(),
24557        ]);
24558        press(&mut f, limit);
24559
24560        assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
24561        assert!(
24562            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
24563            "and then it started evicting"
24564        );
24565        assert!(
24566            f.run(&[b"INFO", b"memory"])
24567                .contains("yo_memory_regime:evict"),
24568            "and it says so"
24569        );
24570    }
24571    // ------------------------------------------------------------- stripes
24572
24573    /// Every string command, run twice: once on a database that is one keyspace
24574    /// and once on a database that is eight, with the same commands in the same
24575    /// order and the replies compared byte for byte.
24576    ///
24577    /// This is the whole claim the striping rests on. A key belongs to one
24578    /// stripe and to no other, so the answer to a command cannot depend on how
24579    /// many stripes there are, and the way to check that is to ask the same
24580    /// question of two servers that differ in nothing else.
24581    ///
24582    /// The keys are chosen to land on different stripes rather than to look
24583    /// tidy. `MSET a 1 b 2 c 3` over eight stripes is only a test of anything if
24584    /// those three keys are not all on the same one, and at eight stripes three
24585    /// keys land together about one time in fifty.
24586    #[test]
24587    fn the_string_group_answers_the_same_however_many_stripes_there_are() {
24588        let script: &[&[&[u8]]] = &[
24589            // The single key commands, which are the ones that get handed one
24590            // stripe at the dispatch site.
24591            &[b"SET", b"k1", b"v1"],
24592            &[b"SET", b"k2", b"v2"],
24593            &[b"GET", b"k1"],
24594            &[b"GET", b"nothing"],
24595            &[b"GETSET", b"k1", b"v1b"],
24596            &[b"SETNX", b"k1", b"no"],
24597            &[b"SETNX", b"k3", b"yes"],
24598            &[b"APPEND", b"k3", b"!"],
24599            &[b"STRLEN", b"k3"],
24600            &[b"SETRANGE", b"k3", b"1", b"XY"],
24601            &[b"GETRANGE", b"k3", b"0", b"-1"],
24602            &[b"INCR", b"n1"],
24603            &[b"INCRBY", b"n1", b"41"],
24604            &[b"DECRBY", b"n1", b"2"],
24605            &[b"INCRBYFLOAT", b"f1", b"1.5"],
24606            &[b"SETEX", b"e1", b"100", b"v"],
24607            &[b"PSETEX", b"e2", b"100000", b"v"],
24608            &[b"GETEX", b"e1", b"PERSIST"],
24609            &[b"GETDEL", b"k2"],
24610            &[b"GET", b"k2"],
24611            &[b"DIGEST", b"k1"],
24612            &[b"DELEX", b"k3"],
24613            // The five that name more than one key, which are the ones that
24614            // cannot be handed one stripe at all.
24615            &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
24616            &[b"MGET", b"a", b"b", b"c", b"missing"],
24617            &[b"MSETNX", b"d", b"4", b"e", b"5"],
24618            &[b"MSETNX", b"e", b"6", b"f", b"7"],
24619            &[b"MGET", b"d", b"e", b"f"],
24620            &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
24621            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
24622            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
24623            &[b"MGET", b"g", b"h"],
24624            &[b"SET", b"s1", b"ohmytext"],
24625            &[b"SET", b"s2", b"mynewtext"],
24626            &[b"LCS", b"s1", b"s2"],
24627            &[b"LCS", b"s1", b"s2", b"LEN"],
24628            &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
24629            &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
24630            &[b"LCS", b"s1", b"gone"],
24631            // And the errors, which have to be the same errors.
24632            &[b"MSET", b"odd"],
24633            &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
24634            &[b"MGET"],
24635        ];
24636
24637        let mut one = Fixture::new();
24638        let mut many = Fixture::striped(8);
24639        for parts in script {
24640            let a = one.run(parts);
24641            let b = many.run(parts);
24642            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
24643        }
24644    }
24645
24646    /// The keys of an `MSET` really do end up on different stripes.
24647    ///
24648    /// Without this the test above could pass on a server whose stripe number
24649    /// happened to be a constant, which is a striped database in name only.
24650    #[test]
24651    fn a_striped_database_spreads_the_keys_it_is_given() {
24652        let mut f = Fixture::striped(8);
24653        for i in 0..256 {
24654            let key = format!("key:{i}");
24655            f.run(&[b"SET", key.as_bytes(), b"v"]);
24656        }
24657        assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
24658    }
24659
24660    /// A wrong type stops an `MGET` no more than it does on one stripe: the key
24661    /// that is not a string comes back nil and the rest of the reply is intact.
24662    #[test]
24663    fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
24664        let mut one = Fixture::new();
24665        let mut many = Fixture::striped(8);
24666        for f in [&mut one, &mut many] {
24667            f.run(&[b"SET", b"str", b"v"]);
24668            // Planted rather than pushed. `RPUSH` belongs to the list group,
24669            // which has not been taught about stripes yet and would refuse the
24670            // wide server. What is under test is what `MGET` does when it walks
24671            // onto a key that is not a string, and that does not care how the
24672            // key got there.
24673            f.server
24674                .striped(0)
24675                .hold(b"list")
24676                .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
24677                .expect("a new list");
24678        }
24679        assert_eq!(
24680            one.run(&[b"MGET", b"str", b"list", b"gone"]),
24681            many.run(&[b"MGET", b"str", b"list", b"gone"])
24682        );
24683    }
24684
24685    /// The same claim for the keyspace group, and the same way of checking it.
24686    ///
24687    /// `SORT` is not in the script because it is the one command in that file
24688    /// that has not been taught about stripes, and `SCAN`, `KEYS` and
24689    /// `RANDOMKEY` are not in it either, because those three do not promise an
24690    /// order and comparing two replies byte for byte would be asserting one.
24691    /// They get tests of their own below.
24692    #[test]
24693    fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
24694        let script: &[&[&[u8]]] = &[
24695            &[b"SET", b"k1", b"v1"],
24696            &[b"SET", b"k2", b"v2"],
24697            &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
24698            &[b"TYPE", b"k1"],
24699            &[b"TYPE", b"gone"],
24700            &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
24701            &[b"EXPIRE", b"k1", b"100"],
24702            &[b"TTL", b"k1"],
24703            &[b"EXPIRE", b"k1", b"200", b"NX"],
24704            &[b"PERSIST", b"k1"],
24705            &[b"TTL", b"k1"],
24706            &[b"PEXPIREAT", b"k2", b"1900000000000"],
24707            &[b"EXPIRETIME", b"k2"],
24708            &[b"PEXPIRETIME", b"k2"],
24709            &[b"PERSIST", b"k2"],
24710            &[b"OBJECT", b"ENCODING", b"k1"],
24711            &[b"OBJECT", b"REFCOUNT", b"k1"],
24712            &[b"OBJECT", b"IDLETIME", b"k1"],
24713            &[b"OBJECT", b"FREQ", b"k1"],
24714            &[b"OBJECT", b"ENCODING", b"gone"],
24715            &[b"OBJECT", b"HELP"],
24716            &[b"RENAME", b"k1", b"k9"],
24717            &[b"GET", b"k9"],
24718            &[b"RENAME", b"gone", b"x"],
24719            &[b"RENAMENX", b"k9", b"k2"],
24720            &[b"RENAMENX", b"k9", b"k8"],
24721            &[b"GET", b"k8"],
24722            &[b"COPY", b"k8", b"c1"],
24723            &[b"COPY", b"k8", b"c1"],
24724            &[b"COPY", b"k8", b"c1", b"REPLACE"],
24725            &[b"COPY", b"k8", b"k8"],
24726            &[b"COPY", b"gone", b"c2"],
24727            &[b"COPY", b"k8", b"k8", b"DB", b"1"],
24728            &[b"COPY", b"k8", b"c9", b"DB", b"9"],
24729            &[b"MOVE", b"c1", b"1"],
24730            &[b"MOVE", b"c1", b"1"],
24731            &[b"MOVE", b"k8", b"0"],
24732            &[b"DEL", b"k2", b"gone"],
24733            &[b"UNLINK", b"k8", b"k8"],
24734            &[b"DBSIZE"],
24735        ];
24736
24737        let mut one = Fixture::new();
24738        let mut many = Fixture::striped(8);
24739        for parts in script {
24740            let a = one.run(parts);
24741            let b = many.run(parts);
24742            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
24743        }
24744
24745        // `RESTORE` needs bytes a client would have got from a `DUMP`, so the
24746        // payload is taken from the store rather than parsed back out of a
24747        // reply that is not text. Both servers dump the same key and the bytes
24748        // are the same bytes, which is the first half of what is being checked
24749        // here.
24750        for f in [&mut one, &mut many] {
24751            f.run(&[b"SET", b"d1", b"payload"]);
24752            let payload = f
24753                .server
24754                .striped(0)
24755                .hold(b"d1")
24756                .dump(b"d1")
24757                .expect("a key that is there");
24758            assert!(
24759                f.run(&[b"DUMP", b"d1"])
24760                    .starts_with(&format!("${}", payload.len())),
24761                "a payload of the length the store gave"
24762            );
24763            assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
24764            assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
24765            assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
24766            assert_eq!(
24767                f.run(&[b"RESTORE", b"d2", b"0", &payload]),
24768                "-BUSYKEY Target key name already exists.\r\n"
24769            );
24770            assert_eq!(
24771                f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
24772                "-ERR DUMP payload version or checksum are wrong\r\n"
24773            );
24774        }
24775    }
24776
24777    /// A `SCAN` of a database of eight stripes comes back with all of it.
24778    ///
24779    /// The cursor is the thing under test. It has to carry the stripe as well
24780    /// as the place in it, so a client that stops at one stripe and comes back
24781    /// carries on in that stripe and not at the top of the database, and the
24782    /// walk has to end once rather than eight times.
24783    #[test]
24784    fn a_scan_of_a_striped_database_walks_all_of_it() {
24785        // Eight stripes and a COUNT of ten, so eighty keys is already more than
24786        // one page on every stripe and the cursor has to carry which stripe it
24787        // was on, which is the thing being checked.
24788        let n = if cfg!(miri) { 80 } else { 500 };
24789        let mut f = Fixture::striped(8);
24790        for i in 0..n {
24791            let key = format!("key:{i}");
24792            f.run(&[b"SET", key.as_bytes(), b"v"]);
24793        }
24794
24795        let mut seen = Vec::new();
24796        let mut cursor = "0".to_owned();
24797        let mut calls = 0;
24798        loop {
24799            let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
24800            let (next, keys) = scan_reply(&reply);
24801            seen.extend(keys);
24802            cursor = next;
24803            calls += 1;
24804            assert!(calls < 5_000, "a scan that will not finish");
24805            if cursor == "0" {
24806                break;
24807            }
24808        }
24809        seen.sort();
24810        assert_eq!(seen.len(), n, "a quiet scan answered a key twice");
24811        assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
24812
24813        // And the options still work when the walk is over several stripes,
24814        // since a `MATCH` is applied to keys a stripe handed up and a `TYPE` is
24815        // applied by each stripe on the way.
24816        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
24817        let (_, keys) = scan_reply(&reply);
24818        assert_eq!(keys.len(), 10, "key:40 through key:49");
24819        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
24820        let (_, keys) = scan_reply(&reply);
24821        assert!(keys.is_empty(), "nothing here is a list");
24822    }
24823
24824    /// `RANDOMKEY` on a striped database answers a key from any of the stripes.
24825    ///
24826    /// The draw picks the stripe first, so the thing that can go wrong is that
24827    /// it always picks the same one, and two hundred draws over eight stripes
24828    /// would make that obvious.
24829    #[test]
24830    fn a_random_key_can_come_from_any_stripe() {
24831        let mut f = Fixture::striped(8);
24832        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
24833        for i in 0..200 {
24834            let key = format!("key:{i}");
24835            f.run(&[b"SET", key.as_bytes(), b"v"]);
24836        }
24837        let mut homes = std::collections::HashSet::new();
24838        for _ in 0..200 {
24839            let got = f.run(&[b"RANDOMKEY"]);
24840            let key = got.split("\r\n").nth(1).expect("a key").to_owned();
24841            assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
24842            homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
24843        }
24844        assert_eq!(homes.len(), 8, "some stripe was never drawn from");
24845    }
24846
24847    /// Two keys that are not on the same stripe, which is what `RENAME` and
24848    /// `COPY` have to cope with and what a test has to arrange rather than
24849    /// hope for.
24850    fn apart(f: &mut Fixture, src: &str) -> String {
24851        let home = f.server.striped(0).stripe_of(src.as_bytes());
24852        for i in 0..1_000 {
24853            let dst = format!("dst:{i}");
24854            if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
24855                return dst;
24856            }
24857        }
24858        panic!("eight stripes and a thousand keys all landed in one place");
24859    }
24860
24861    /// A rename whose two keys are on two stripes moves the value, the deadline
24862    /// and, for a collection, the body itself.
24863    #[test]
24864    fn a_rename_across_stripes_takes_everything_with_it() {
24865        let mut f = Fixture::striped(8);
24866        let dst = apart(&mut f, "src");
24867        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
24868
24869        f.run(&[b"SET", src, b"v"]);
24870        f.run(&[b"EXPIRE", src, b"100"]);
24871        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
24872        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
24873        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
24874        assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
24875
24876        // A list, because a string lives in its record and a collection lives
24877        // in a slab, and the second of those is the one that can be left
24878        // behind. Planted through the store, since the list group has not been
24879        // taught about stripes yet.
24880        f.server
24881            .striped(0)
24882            .hold(src)
24883            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
24884            .expect("a new list");
24885        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
24886        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
24887        assert_eq!(
24888            f.server.striped(0).hold(dst).llen(dst).expect("a list"),
24889            2,
24890            "the members are on the stripe the key moved to"
24891        );
24892
24893        // And `RENAMENX` still refuses a destination that is taken, which is
24894        // the one answer the cross stripe path has to work out for itself.
24895        f.run(&[b"SET", src, b"v"]);
24896        assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
24897        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
24898        assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
24899    }
24900
24901    /// And a copy across two stripes leaves both keys behind it.
24902    #[test]
24903    fn a_copy_across_stripes_leaves_the_source_where_it_was() {
24904        let mut f = Fixture::striped(8);
24905        let dst = apart(&mut f, "src");
24906        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
24907
24908        f.run(&[b"SET", src, b"v"]);
24909        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
24910        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
24911        assert_eq!(
24912            f.run(&[b"COPY", src, dst]),
24913            ":0\r\n",
24914            "the destination is taken"
24915        );
24916        f.run(&[b"SET", src, b"w"]);
24917        assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
24918        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
24919
24920        // A collection is cloned rather than moved, so both keys have a body of
24921        // their own afterwards and writing to one does not show up in the
24922        // other.
24923        f.run(&[b"DEL", src, dst]);
24924        f.server
24925            .striped(0)
24926            .hold(src)
24927            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
24928            .expect("a new list");
24929        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
24930        f.server
24931            .striped(0)
24932            .hold(src)
24933            .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
24934            .expect("a list that is there");
24935        assert_eq!(f.server.striped(0).hold(src).llen(src).expect("a list"), 3);
24936        assert_eq!(f.server.striped(0).hold(dst).llen(dst).expect("a list"), 2);
24937    }
24938
24939    /// Every bitmap command, on one stripe and on eight, replies compared byte
24940    /// for byte.
24941    ///
24942    /// `BITOP` is the one that names more than one key and it is where the work
24943    /// went. The rest are single key commands that now find their own stripe,
24944    /// and they are here because the cheapest way to be sure the routing is
24945    /// right is to ask.
24946    #[test]
24947    fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
24948        let script: &[&[&[u8]]] = &[
24949            &[b"SET", b"k1", b"foobar"],
24950            &[b"SETBIT", b"b1", b"7", b"1"],
24951            &[b"SETBIT", b"b1", b"7", b"0"],
24952            &[b"GETBIT", b"k1", b"6"],
24953            &[b"GETBIT", b"k1", b"100"],
24954            &[b"BITCOUNT", b"k1"],
24955            &[b"BITCOUNT", b"k1", b"0", b"0"],
24956            &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
24957            &[b"BITPOS", b"k1", b"1"],
24958            &[b"BITPOS", b"k1", b"0", b"2"],
24959            &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
24960            &[
24961                b"BITFIELD",
24962                b"bf",
24963                b"SET",
24964                b"u8",
24965                b"0",
24966                b"255",
24967                b"GET",
24968                b"u8",
24969                b"0",
24970            ],
24971            &[
24972                b"BITFIELD",
24973                b"bf",
24974                b"OVERFLOW",
24975                b"SAT",
24976                b"INCRBY",
24977                b"u8",
24978                b"0",
24979                b"10",
24980            ],
24981            &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
24982            // The multi key one, over sources that are not on one stripe unless
24983            // eight stripes have folded into one.
24984            &[b"SET", b"s1", b"abc"],
24985            &[b"SET", b"s2", b"abd"],
24986            &[b"SET", b"s3", b"a"],
24987            &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
24988            &[b"GET", b"d1"],
24989            &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
24990            &[b"GET", b"d2"],
24991            &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
24992            &[b"STRLEN", b"d3"],
24993            &[b"BITOP", b"NOT", b"d4", b"s1"],
24994            &[b"STRLEN", b"d4"],
24995            &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
24996            &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
24997            &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
24998            &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
24999            // A source that is not there reads as empty, and a result with
25000            // nothing in it deletes the destination rather than writing one.
25001            &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
25002            &[b"EXISTS", b"d1"],
25003            &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
25004            &[b"GET", b"d9"],
25005            // And the errors, which have to be the same errors. The key that
25006            // is not a string is planted below rather than pushed here, since
25007            // the list group has not been taught about stripes yet.
25008            &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
25009            &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
25010            &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
25011            &[b"BITOP", b"DIFF", b"d1", b"s1"],
25012            &[b"BITOP", b"NOPE", b"d1", b"s1"],
25013            &[b"BITCOUNT", b"list"],
25014            &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
25015        ];
25016
25017        let mut one = Fixture::new();
25018        let mut many = Fixture::striped(8);
25019        for f in [&mut one, &mut many] {
25020            plant_list(f, b"list");
25021        }
25022        for parts in script {
25023            let a = one.run(parts);
25024            let b = many.run(parts);
25025            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25026        }
25027    }
25028
25029    /// A list under `key`, put there through the store.
25030    ///
25031    /// What a test does when it wants a key of the wrong type on a striped
25032    /// server, because the command that would make one is in a group that has
25033    /// not been taught about stripes yet.
25034    fn plant_list(f: &mut Fixture, key: &[u8]) {
25035        f.server
25036            .striped(0)
25037            .hold(key)
25038            .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
25039            .expect("a new list");
25040    }
25041
25042    /// A `BITOP` whose keys are on two stripes reads both of them.
25043    ///
25044    /// The test above spreads its keys by hashing and would still pass if one
25045    /// stripe were doing all the work, since the answers would be the same. This
25046    /// one puts the destination and the two sources where they are known not to
25047    /// share a stripe.
25048    #[test]
25049    fn a_bitop_across_stripes_reads_every_source() {
25050        let mut f = Fixture::striped(8);
25051        let other = apart(&mut f, "src");
25052        let (src, far) = (b"src".as_slice(), other.as_bytes());
25053        assert_ne!(
25054            f.server.striped(0).stripe_of(src),
25055            f.server.striped(0).stripe_of(far),
25056            "the two keys are the point of the test"
25057        );
25058
25059        f.run(&[b"SET", src, b"abc"]);
25060        f.run(&[b"SET", far, b"abd"]);
25061        assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
25062        assert_eq!(
25063            f.run(&[b"GET", far]),
25064            "$3\r\nab`\r\n",
25065            "a destination that is also a source"
25066        );
25067        f.run(&[b"SET", far, b"abd"]);
25068        assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
25069        assert_eq!(
25070            f.run(&[b"GET", src]),
25071            "$3\r\n\0\0\x07\r\n",
25072            "and the other way round"
25073        );
25074
25075        // A result of nothing deletes a destination on whatever stripe it is
25076        // on, and a source of the wrong type is refused before anything is
25077        // written.
25078        f.run(&[b"SET", src, b"abc"]);
25079        f.run(&[b"DEL", far]);
25080        assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
25081        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
25082        f.run(&[b"SET", src, b"abc"]);
25083        f.run(&[b"DEL", far]);
25084        plant_list(&mut f, far);
25085        assert_eq!(
25086            f.run(&[b"BITOP", b"OR", b"out", src, far]),
25087            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25088        );
25089        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
25090    }
25091
25092    /// Every HyperLogLog command, on one stripe and on eight.
25093    ///
25094    /// Not under Miri, for the reason on
25095    /// `the_debug_forms_answer_four_different_shapes`, and twice over here
25096    /// because the script is run against both shapes of server.
25097    #[cfg_attr(miri, ignore = "sixteen thousand registers a command")]
25098    #[test]
25099    fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
25100        let script: &[&[&[u8]]] = &[
25101            &[b"PFADD", b"h1", b"a", b"b", b"c"],
25102            &[b"PFADD", b"h1", b"a"],
25103            &[b"PFADD", b"h2"],
25104            &[b"PFADD", b"h2", b"c", b"d", b"e"],
25105            &[b"PFCOUNT", b"h1"],
25106            &[b"PFCOUNT", b"h2"],
25107            &[b"PFCOUNT", b"missing"],
25108            // The two that name more than one key.
25109            &[b"PFCOUNT", b"h1", b"h2"],
25110            &[b"PFCOUNT", b"h1", b"missing"],
25111            &[b"PFMERGE", b"m", b"h1", b"h2"],
25112            &[b"PFCOUNT", b"m"],
25113            &[b"STRLEN", b"m"],
25114            &[b"PFMERGE", b"m"],
25115            &[b"PFCOUNT", b"m"],
25116            &[b"PFMERGE", b"m2", b"missing"],
25117            &[b"PFCOUNT", b"m2"],
25118            // The debugging ones, which are single key and change what they
25119            // look at.
25120            &[b"PFDEBUG", b"ENCODING", b"h1"],
25121            &[b"PFDEBUG", b"DECODE", b"h1"],
25122            &[b"PFDEBUG", b"TODENSE", b"h1"],
25123            &[b"PFDEBUG", b"ENCODING", b"h1"],
25124            &[b"PFDEBUG", b"TODENSE", b"h1"],
25125            &[b"PFCOUNT", b"h1", b"h2"],
25126            &[b"PFSELFTEST"],
25127            // And the errors.
25128            &[b"SET", b"plain", b"not a sketch at all"],
25129            &[b"PFADD", b"plain", b"a"],
25130            &[b"PFCOUNT", b"plain"],
25131            &[b"PFCOUNT", b"h1", b"plain"],
25132            &[b"PFMERGE", b"plain", b"h1"],
25133            &[b"PFMERGE", b"m", b"plain"],
25134            &[b"PFDEBUG", b"ENCODING", b"gone"],
25135            &[b"PFDEBUG", b"NOPE", b"h1"],
25136        ];
25137
25138        let mut one = Fixture::new();
25139        let mut many = Fixture::striped(8);
25140        for parts in script {
25141            let a = one.run(parts);
25142            let b = many.run(parts);
25143            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25144        }
25145    }
25146
25147    /// Every set command, on one stripe and on eight.
25148    ///
25149    /// The commands that answer members answer them in whatever order the set
25150    /// or the table they were built in holds them, so those replies are
25151    /// compared as sets. Everything else is compared byte for byte. Two servers
25152    /// agreeing on the order would be a fact about the tables and not about the
25153    /// answer, and asserting it would make this test fail for a reason nobody
25154    /// cares about.
25155    #[test]
25156    fn the_set_group_answers_the_same_however_many_stripes_there_are() {
25157        const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
25158        let script: &[&[&[u8]]] = &[
25159            &[b"SADD", b"s1", b"a", b"b", b"c"],
25160            &[b"SADD", b"s1", b"a"],
25161            &[b"SADD", b"s2", b"b", b"c", b"d"],
25162            &[b"SADD", b"ints", b"1", b"2", b"3"],
25163            &[b"SCARD", b"s1"],
25164            &[b"SISMEMBER", b"s1", b"a"],
25165            &[b"SISMEMBER", b"s1", b"z"],
25166            &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
25167            &[b"SMEMBERS", b"s1"],
25168            &[b"SREM", b"s1", b"c"],
25169            &[b"SADD", b"s1", b"c"],
25170            &[b"SSCAN", b"s1", b"0"],
25171            &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
25172            // The two draws, on a set of one member, which is the only shape
25173            // whose answer two servers have to agree on.
25174            &[b"SADD", b"one", b"m"],
25175            &[b"SRANDMEMBER", b"one"],
25176            &[b"SRANDMEMBER", b"one", b"-3"],
25177            &[b"SRANDMEMBER", b"gone"],
25178            &[b"SPOP", b"one"],
25179            &[b"SPOP", b"one"],
25180            &[b"SPOP", b"gone", b"2"],
25181            // The one that names two keys.
25182            &[b"SMOVE", b"s1", b"s2", b"a"],
25183            &[b"SMOVE", b"s1", b"s2", b"zzz"],
25184            &[b"SMOVE", b"gone", b"s2", b"a"],
25185            &[b"SMEMBERS", b"s1"],
25186            &[b"SMEMBERS", b"s2"],
25187            // The algebra.
25188            &[b"SINTER", b"s1", b"s2"],
25189            &[b"SUNION", b"s1", b"s2"],
25190            &[b"SDIFF", b"s2", b"s1"],
25191            &[b"SINTER", b"s1", b"gone"],
25192            &[b"SUNION", b"s1", b"gone"],
25193            &[b"SDIFF", b"gone", b"s1"],
25194            &[b"SINTER", b"ints", b"s1"],
25195            &[b"SINTERCARD", b"2", b"s1", b"s2"],
25196            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
25197            &[b"SUNIONCARD", b"2", b"s1", b"s2"],
25198            &[b"SDIFFCARD", b"2", b"s2", b"s1"],
25199            &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
25200            &[b"SMEMBERS", b"d1"],
25201            &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
25202            &[b"SCARD", b"d2"],
25203            &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
25204            &[b"SCARD", b"d3"],
25205            // An empty result deletes the destination rather than storing a
25206            // set with nothing in it.
25207            &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
25208            &[b"EXISTS", b"d4"],
25209            // And a destination that is also a source.
25210            &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
25211            &[b"SCARD", b"s2"],
25212            // The errors, which have to be the same errors.
25213            &[b"SET", b"str", b"v"],
25214            &[b"SADD", b"str", b"a"],
25215            &[b"SINTER", b"s1", b"str"],
25216            &[b"SINTERSTORE", b"d5", b"s1", b"str"],
25217            &[b"EXISTS", b"d5"],
25218            &[b"SMOVE", b"str", b"s2", b"a"],
25219            &[b"SMOVE", b"s1", b"str", b"b"],
25220            &[b"SMOVE", b"gone", b"str", b"b"],
25221            &[b"SINTERCARD", b"0", b"s1"],
25222            &[b"SINTERCARD", b"3", b"s1", b"s2"],
25223            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
25224            &[b"SPOP", b"s1", b"-1"],
25225        ];
25226
25227        let mut one = Fixture::new();
25228        let mut many = Fixture::striped(8);
25229        for parts in script {
25230            let a = one.run(parts);
25231            let b = many.run(parts);
25232            let name = String::from_utf8_lossy(parts[0]).to_uppercase();
25233            if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
25234                assert_eq!(sorted(&a), sorted(&b), "{name}");
25235            } else {
25236                assert_eq!(a, b, "{name}");
25237            }
25238        }
25239    }
25240
25241    /// The algebra over sets that are known to be on different stripes.
25242    #[test]
25243    fn a_set_operation_across_stripes_reads_every_set() {
25244        let mut f = Fixture::striped(8);
25245        let second = apart(&mut f, "s1");
25246        let third = apart(&mut f, &second);
25247        let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
25248
25249        f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
25250        f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
25251        assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
25252        assert_eq!(
25253            sorted(&f.run(&[b"SUNION", s1, s2])),
25254            ["a", "b", "c", "d"],
25255            "a union of two stripes is both of them"
25256        );
25257        assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
25258        assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
25259        assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
25260        assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
25261
25262        // A destination on a third stripe, and then one that is also a source.
25263        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
25264        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
25265        assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
25266        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
25267        assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
25268        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
25269
25270        // An empty result deletes a destination wherever it is, and a key of
25271        // the wrong type stops the command before the destination is touched.
25272        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
25273        assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
25274        f.run(&[b"SET", s3, b"v"]);
25275        assert_eq!(
25276            f.run(&[b"SINTER", s1, s3]),
25277            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25278        );
25279        assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
25280    }
25281
25282    /// An `SMOVE` whose two keys are on two stripes.
25283    #[test]
25284    fn a_move_across_stripes_takes_the_member_with_it() {
25285        let mut f = Fixture::striped(8);
25286        let other = apart(&mut f, "src");
25287        let (src, dst) = (b"src".as_slice(), other.as_bytes());
25288
25289        f.run(&[b"SADD", src, b"a", b"b"]);
25290        f.run(&[b"SADD", dst, b"c"]);
25291        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
25292        assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
25293        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
25294        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
25295
25296        // A destination that is not there is created on its own stripe, and a
25297        // source that loses its last member is deleted from its own.
25298        f.run(&[b"DEL", dst]);
25299        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
25300        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
25301        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
25302
25303        // And a source that is not there answers zero without ever asking what
25304        // the destination holds, which is Redis's order and not the obvious
25305        // one.
25306        f.run(&[b"SET", dst, b"v"]);
25307        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
25308        f.run(&[b"SADD", src, b"b"]);
25309        assert_eq!(
25310            f.run(&[b"SMOVE", src, dst, b"b"]),
25311            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25312        );
25313    }
25314
25315    /// A count and a merge over sketches that are known to be on two stripes.
25316    #[test]
25317    fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
25318        let mut f = Fixture::striped(8);
25319        let other = apart(&mut f, "src");
25320        let (src, far) = (b"src".as_slice(), other.as_bytes());
25321
25322        for i in 0..150 {
25323            let ele = format!("e:{i}");
25324            f.run(&[b"PFADD", src, ele.as_bytes()]);
25325        }
25326        for i in 150..200 {
25327            let ele = format!("e:{i}");
25328            f.run(&[b"PFADD", far, ele.as_bytes()]);
25329        }
25330        // The three numbers a real server gives for these elements, which are
25331        // the numbers the single stripe tests in the keyspace crate check too.
25332        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
25333        assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
25334        assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
25335
25336        // A merge whose destination is on a third stripe, and then one that
25337        // writes into a source.
25338        let dest = apart(&mut f, &other);
25339        assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
25340        assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
25341        assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
25342        assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
25343        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
25344    }
25345
25346    /// Every sorted set command, on one stripe and on eight.
25347    ///
25348    /// Every reply here is compared byte for byte, unlike the set group, because
25349    /// a sorted set answers in rank order and members sharing a score come out
25350    /// in the order of their bytes. There is nothing left for the table the
25351    /// answer was built in to decide.
25352    #[test]
25353    fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
25354        let script: &[&[&[u8]]] = &[
25355            &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
25356            &[b"ZADD", b"z1", b"NX", b"9", b"a"],
25357            &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
25358            &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
25359            &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
25360            &[b"ZINCRBY", b"z1", b"1.5", b"b"],
25361            &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
25362            &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
25363            &[b"ZADD", b"one", b"1", b"m"],
25364            &[b"ZCARD", b"z1"],
25365            &[b"ZCARD", b"gone"],
25366            &[b"ZSCORE", b"z1", b"a"],
25367            &[b"ZSCORE", b"z1", b"zz"],
25368            &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
25369            &[b"ZRANK", b"z1", b"c"],
25370            &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
25371            &[b"ZREVRANK", b"z1", b"c"],
25372            &[b"ZRANK", b"z1", b"gone"],
25373            &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
25374            &[b"ZCOUNT", b"z1", b"(1", b"3"],
25375            &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
25376            // The range commands, which are one parse and one walk.
25377            &[b"ZRANGE", b"z1", b"0", b"-1"],
25378            &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
25379            &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
25380            &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
25381            &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
25382            &[b"ZREVRANGE", b"z1", b"0", b"-1"],
25383            &[
25384                b"ZRANGEBYSCORE",
25385                b"z1",
25386                b"-inf",
25387                b"+inf",
25388                b"LIMIT",
25389                b"1",
25390                b"1",
25391            ],
25392            &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
25393            &[b"ZSCAN", b"z1", b"0"],
25394            &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
25395            // The draw, on a sorted set of one member, which is the only shape
25396            // whose answer two servers have to agree on.
25397            &[b"ZRANDMEMBER", b"one"],
25398            &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
25399            &[b"ZRANDMEMBER", b"gone"],
25400            // The one that copies a window into another key.
25401            &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
25402            &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
25403            &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
25404            &[b"EXISTS", b"d0"],
25405            // The algebra, in both its shapes.
25406            &[b"ZUNION", b"2", b"z1", b"z2"],
25407            &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
25408            &[
25409                b"ZUNION",
25410                b"2",
25411                b"z1",
25412                b"z2",
25413                b"WEIGHTS",
25414                b"2",
25415                b"3",
25416                b"AGGREGATE",
25417                b"MAX",
25418                b"WITHSCORES",
25419            ],
25420            &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
25421            &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
25422            &[b"ZDIFF", b"2", b"gone", b"z1"],
25423            &[b"ZINTERCARD", b"2", b"z1", b"z2"],
25424            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
25425            &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
25426            &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
25427            &[
25428                b"ZINTERSTORE",
25429                b"d2",
25430                b"2",
25431                b"z1",
25432                b"z2",
25433                b"AGGREGATE",
25434                b"MIN",
25435            ],
25436            &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
25437            &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
25438            &[b"ZCARD", b"d3"],
25439            // An empty result deletes the destination rather than storing a
25440            // sorted set with nothing in it.
25441            &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
25442            &[b"EXISTS", b"d4"],
25443            // A plain set is a sorted set where every score is one, so it is a
25444            // legal input to all of these.
25445            &[b"SADD", b"plain", b"a", b"x"],
25446            &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
25447            &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
25448            // And a destination that is also a source.
25449            &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
25450            &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
25451            // The three removals and the two pops.
25452            &[b"ZREM", b"d5", b"x", b"nothere"],
25453            &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
25454            &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
25455            &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
25456            &[b"ZPOPMIN", b"z1"],
25457            &[b"ZPOPMAX", b"z1", b"2"],
25458            &[b"ZPOPMIN", b"gone"],
25459            &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
25460            &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
25461            // The errors, which have to be the same errors.
25462            &[b"SET", b"str", b"v"],
25463            &[b"ZADD", b"str", b"1", b"a"],
25464            &[b"ZSCORE", b"str", b"a"],
25465            &[b"ZADD", b"z1", b"nan", b"a"],
25466            &[b"ZUNION", b"2", b"z1", b"str"],
25467            &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
25468            &[b"EXISTS", b"d6"],
25469            &[b"ZINTERCARD", b"0", b"z1"],
25470            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
25471            &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
25472            &[b"ZMPOP", b"1", b"str", b"MIN"],
25473            &[b"ZPOPMIN", b"z1", b"-1"],
25474        ];
25475
25476        let mut one = Fixture::new();
25477        let mut many = Fixture::striped(8);
25478        for parts in script {
25479            let a = one.run(parts);
25480            let b = many.run(parts);
25481            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25482        }
25483    }
25484
25485    /// The algebra over sorted sets that are known to be on different stripes.
25486    #[test]
25487    fn a_sorted_set_operation_across_stripes_reads_every_input() {
25488        let mut f = Fixture::striped(8);
25489        let second = apart(&mut f, "z1");
25490        let third = apart(&mut f, &second);
25491        let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
25492
25493        f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
25494        f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
25495        // a is 1, c is 4, b is 2 and 3 added together, which is the order they
25496        // come out in and the answer that says both stripes were read.
25497        assert_eq!(
25498            f.run(&[b"ZUNION", b"2", z1, z2]),
25499            "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
25500        );
25501        assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
25502        assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
25503        assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
25504        assert_eq!(
25505            f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
25506            ":1\r\n"
25507        );
25508
25509        // A destination on a third stripe, and the weights and the aggregate
25510        // reaching every input.
25511        assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
25512        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
25513        assert_eq!(
25514            f.run(&[
25515                b"ZUNIONSTORE",
25516                z3,
25517                b"2",
25518                z1,
25519                z2,
25520                b"WEIGHTS",
25521                b"2",
25522                b"3",
25523                b"AGGREGATE",
25524                b"MAX"
25525            ]),
25526            ":3\r\n"
25527        );
25528        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
25529        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
25530        assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
25531        assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
25532        assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
25533
25534        // A pop over keys on several stripes takes from the first one that has
25535        // anything, which is what makes the order of the keys matter.
25536        let popped = format!(
25537            "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
25538            second.len()
25539        );
25540        assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
25541        f.run(&[b"ZADD", z2, b"3", b"b"]);
25542
25543        // An empty result deletes a destination wherever it is, and an input of
25544        // the wrong type stops the command before the destination is touched.
25545        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
25546        assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
25547        f.run(&[b"SET", z3, b"v"]);
25548        assert_eq!(
25549            f.run(&[b"ZUNION", b"2", z1, z3]),
25550            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25551        );
25552        assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
25553
25554        // And a destination that is also a source works across stripes for the
25555        // reason it works on one: the whole result is built before anything is
25556        // written.
25557        assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
25558        assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
25559        assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
25560    }
25561
25562    /// A `ZRANGESTORE` whose two keys are on two stripes.
25563    #[test]
25564    fn a_range_store_across_stripes_copies_the_window() {
25565        let mut f = Fixture::striped(8);
25566        let other = apart(&mut f, "src");
25567        let third = apart(&mut f, &other);
25568        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
25569
25570        f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
25571        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
25572        assert_eq!(
25573            f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
25574            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
25575        );
25576        assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
25577
25578        // A window walked backwards takes the other end of the sorted set and
25579        // still stores what it took in score order.
25580        assert_eq!(
25581            f.run(&[
25582                b"ZRANGESTORE",
25583                dst,
25584                src,
25585                b"+inf",
25586                b"-inf",
25587                b"BYSCORE",
25588                b"REV",
25589                b"LIMIT",
25590                b"0",
25591                b"2"
25592            ]),
25593            ":2\r\n"
25594        );
25595        assert_eq!(
25596            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
25597            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
25598        );
25599
25600        // An empty window deletes the destination on its own stripe, and a
25601        // source of the wrong type is refused before the destination is touched.
25602        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
25603        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
25604        f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
25605        f.run(&[b"SET", plain, b"v"]);
25606        assert_eq!(
25607            f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
25608            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25609        );
25610        assert_eq!(
25611            f.run(&[b"ZCARD", dst]),
25612            ":3\r\n",
25613            "and left the destination"
25614        );
25615    }
25616
25617    /// Every list command, on one stripe and on eight.
25618    ///
25619    /// The blocking six are in here too, both when they can be answered on the
25620    /// spot and when they cannot, since a command that parks its client writes
25621    /// nothing at all and two servers have to agree about that as much as they
25622    /// agree about a reply.
25623    #[test]
25624    fn the_list_group_answers_the_same_however_many_stripes_there_are() {
25625        let script: &[&[&[u8]]] = &[
25626            &[b"RPUSH", b"l1", b"a", b"b", b"c"],
25627            &[b"LPUSH", b"l1", b"z"],
25628            &[b"RPUSHX", b"l1", b"d"],
25629            &[b"LPUSHX", b"gone", b"x"],
25630            &[b"RPUSHX", b"gone", b"x"],
25631            &[b"LLEN", b"l1"],
25632            &[b"LLEN", b"gone"],
25633            &[b"LRANGE", b"l1", b"0", b"-1"],
25634            &[b"LRANGE", b"l1", b"1", b"2"],
25635            &[b"LRANGE", b"l1", b"5", b"9"],
25636            &[b"LINDEX", b"l1", b"0"],
25637            &[b"LINDEX", b"l1", b"-1"],
25638            &[b"LINDEX", b"l1", b"99"],
25639            &[b"LSET", b"l1", b"0", b"y"],
25640            &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
25641            &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
25642            &[b"LPOS", b"l1", b"b"],
25643            &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
25644            &[b"LPOS", b"l1", b"nothere"],
25645            &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
25646            &[b"LREM", b"l1", b"1", b"aa"],
25647            &[b"LTRIM", b"l1", b"0", b"3"],
25648            &[b"LRANGE", b"l1", b"0", b"-1"],
25649            &[b"LPOP", b"l1"],
25650            &[b"RPOP", b"l1"],
25651            &[b"LPOP", b"l1", b"2"],
25652            &[b"LPOP", b"gone"],
25653            &[b"LPOP", b"gone", b"2"],
25654            &[b"EXISTS", b"l1"],
25655            // The ones that name two keys, and the one that takes a block of
25656            // elements rather than the one on the end.
25657            &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
25658            &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
25659            &[b"RPOPLPUSH", b"src", b"dst"],
25660            &[b"LRANGE", b"dst", b"0", b"-1"],
25661            &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
25662            &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
25663            &[
25664                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
25665            ],
25666            &[
25667                b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
25668            ],
25669            &[b"LRANGE", b"dst", b"0", b"-1"],
25670            &[
25671                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
25672            ],
25673            &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
25674            &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
25675            &[b"LMPOP", b"1", b"gone", b"LEFT"],
25676            // The blocking ones, first with something there to answer them and
25677            // then with nothing, which parks the client and writes nothing.
25678            &[b"RPUSH", b"q", b"a", b"b", b"c"],
25679            &[b"BLPOP", b"gone", b"q", b"0"],
25680            &[b"BRPOP", b"q", b"0"],
25681            &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
25682            &[b"RPUSH", b"q", b"x", b"y", b"z"],
25683            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
25684            &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
25685            &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
25686            &[b"BLPOP", b"q", b"0"],
25687            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
25688            // The errors, which have to be the same errors.
25689            &[b"SET", b"plain", b"v"],
25690            &[b"LPUSH", b"plain", b"a"],
25691            &[b"LLEN", b"plain"],
25692            &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
25693            &[b"LRANGE", b"dst", b"0", b"-1"],
25694            &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
25695            &[b"LSET", b"gone", b"0", b"v"],
25696            &[b"LSET", b"dst", b"99", b"v"],
25697            &[b"LPOP", b"dst", b"-1"],
25698            &[b"LMPOP", b"0", b"dst", b"LEFT"],
25699            &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
25700        ];
25701
25702        let mut one = Fixture::new();
25703        let mut many = Fixture::striped(8);
25704        for parts in script {
25705            let a = one.run(parts);
25706            let b = many.run(parts);
25707            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25708        }
25709    }
25710
25711    /// An `LMOVE` and an `LMOVEM` whose two keys are on two stripes.
25712    #[test]
25713    fn a_list_move_across_stripes_takes_the_elements_with_it() {
25714        let mut f = Fixture::striped(8);
25715        let other = apart(&mut f, "src");
25716        let third = apart(&mut f, &other);
25717        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
25718
25719        f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
25720        assert_eq!(
25721            f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
25722            "$1\r\na\r\n"
25723        );
25724        assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
25725        assert_eq!(
25726            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
25727            "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
25728            "one went on each end of the destination"
25729        );
25730        assert_eq!(
25731            f.run(&[b"LRANGE", src, b"0", b"-1"]),
25732            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
25733        );
25734
25735        // A block of them, which under BULK arrives in the order it left.
25736        assert_eq!(
25737            f.run(&[
25738                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
25739            ]),
25740            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
25741        );
25742        assert_eq!(
25743            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
25744            "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
25745        );
25746        assert_eq!(
25747            f.run(&[b"EXISTS", src]),
25748            ":0\r\n",
25749            "and the source is gone with its last element"
25750        );
25751
25752        // An `EXACTLY` the source cannot fill moves nothing, and a source that
25753        // is not there at all is the two kinds of nothing the two commands have.
25754        f.run(&[b"RPUSH", src, b"e", b"f"]);
25755        assert_eq!(
25756            f.run(&[
25757                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
25758            ]),
25759            "*-1\r\n"
25760        );
25761        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
25762        assert_eq!(
25763            f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
25764            "$-1\r\n"
25765        );
25766        assert_eq!(
25767            f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
25768            "*-1\r\n"
25769        );
25770
25771        // A destination of the wrong type is refused before anything is taken,
25772        // which is the order that matters most here, since an element already
25773        // out of the source would have nowhere to go back to.
25774        f.run(&[b"SET", plain, b"v"]);
25775        assert_eq!(
25776            f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
25777            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25778        );
25779        assert_eq!(
25780            f.run(&[b"LLEN", src]),
25781            ":2\r\n",
25782            "and left the source alone"
25783        );
25784        assert_eq!(
25785            f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
25786            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
25787        );
25788        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
25789    }
25790
25791    /// A parked client served by a push that landed on another stripe.
25792    ///
25793    /// A waiter remembers the database and not the stripe, which is the point:
25794    /// serving it runs the same attempt the command ran, and the attempt finds
25795    /// the stripe each of its keys is on for itself.
25796    #[test]
25797    fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
25798        let mut f = Fixture::striped(8);
25799        let other = apart(&mut f, "q");
25800        let (q, far) = (b"q".as_slice(), other.as_bytes());
25801
25802        assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
25803        assert_eq!(f.server.parked(), 1);
25804        f.run(&[b"RPUSH", far, b"v"]);
25805        let mut out = Out::new(Proto::Resp2);
25806        assert!(f.server.serve_waiter(7, 0, &mut out));
25807        let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
25808        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
25809        assert_eq!(
25810            f.run(&[b"EXISTS", far]),
25811            ":0\r\n",
25812            "and it took the element with it"
25813        );
25814
25815        // And a move across two stripes is served the same way, by the push
25816        // that fills its source.
25817        f.server.forget_waiters(7);
25818        assert_eq!(
25819            f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
25820            Flow::Block
25821        );
25822        f.run(&[b"RPUSH", q, b"w"]);
25823        let mut out = Out::new(Proto::Resp2);
25824        assert!(f.server.serve_waiter(7, 0, &mut out));
25825        assert_eq!(
25826            core::str::from_utf8(out.as_slice()).expect("ascii"),
25827            "$1\r\nw\r\n"
25828        );
25829        assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
25830    }
25831
25832    /// Every stream command, on one stripe and on eight.
25833    ///
25834    /// Every ID is written out rather than left to the clock, so the two servers
25835    /// are being compared on what they store and not on how long the test took
25836    /// to get from one of them to the other.
25837    #[test]
25838    fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
25839        let script: &[&[&[u8]]] = &[
25840            &[b"XADD", b"s", b"1-1", b"a", b"1"],
25841            &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
25842            &[b"XADD", b"s", b"3-1", b"d", b"4"],
25843            &[b"XADD", b"s", b"1-1", b"e", b"5"],
25844            &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
25845            &[b"XLEN", b"s"],
25846            &[b"XLEN", b"gone"],
25847            &[b"XRANGE", b"s", b"-", b"+"],
25848            &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
25849            &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
25850            &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
25851            &[b"XREVRANGE", b"s", b"+", b"-"],
25852            &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
25853            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
25854            &[b"XREAD", b"STREAMS", b"s", b"$"],
25855            // The groups, which is where most of the state is.
25856            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
25857            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
25858            &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
25859            &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
25860            &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
25861            &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
25862            &[
25863                b"XREADGROUP",
25864                b"GROUP",
25865                b"g",
25866                b"c1",
25867                b"COUNT",
25868                b"1",
25869                b"STREAMS",
25870                b"s",
25871                b"0",
25872            ],
25873            &[
25874                b"XREADGROUP",
25875                b"GROUP",
25876                b"nope",
25877                b"c1",
25878                b"STREAMS",
25879                b"s",
25880                b">",
25881            ],
25882            &[b"XPENDING", b"s", b"g"],
25883            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
25884            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
25885            &[b"XPENDING", b"s", b"nope"],
25886            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
25887            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
25888            &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
25889            &[b"XACK", b"s", b"g", b"1-1"],
25890            &[b"XACK", b"s", b"g", b"1-1"],
25891            &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
25892            &[b"XPENDING", b"s", b"g"],
25893            &[b"XINFO", b"STREAM", b"s"],
25894            &[b"XINFO", b"GROUPS", b"s"],
25895            &[b"XINFO", b"CONSUMERS", b"s", b"g"],
25896            &[b"XINFO", b"STREAM", b"gone"],
25897            // Deleting, trimming and moving the ID on.
25898            &[b"XDEL", b"s", b"3-1"],
25899            &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
25900            &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
25901            &[b"XADD", b"s", b"9-1", b"z", b"9"],
25902            &[b"XTRIM", b"s", b"MAXLEN", b"1"],
25903            &[b"XTRIM", b"s", b"MINID", b"9"],
25904            &[b"XSETID", b"s", b"99-1"],
25905            &[b"XSETID", b"s", b"1-1"],
25906            &[b"XLEN", b"s"],
25907            &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
25908            &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
25909            &[b"XGROUP", b"DESTROY", b"s", b"g"],
25910            &[b"XGROUP", b"DESTROY", b"s", b"g"],
25911            // And the errors.
25912            &[b"SET", b"plain", b"v"],
25913            &[b"XADD", b"plain", b"1-1", b"a", b"1"],
25914            &[b"XLEN", b"plain"],
25915            &[b"XREAD", b"STREAMS", b"plain", b"0"],
25916            &[b"XRANGE", b"s", b"bogus", b"+"],
25917            &[b"XADD", b"s", b"1-1", b"a"],
25918            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
25919            &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
25920        ];
25921
25922        let mut one = Fixture::new();
25923        let mut many = Fixture::striped(8);
25924        for parts in script {
25925            let a = one.run(parts);
25926            let b = many.run(parts);
25927            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
25928        }
25929    }
25930
25931    /// An `XREAD` and an `XREADGROUP` naming two keys on two stripes.
25932    ///
25933    /// Nothing is shared between the two streams, so the only thing this can go
25934    /// wrong at is looking both of them up, which is exactly what a read that
25935    /// held one database and walked it would get wrong.
25936    #[test]
25937    fn a_stream_read_across_stripes_reads_every_key() {
25938        let mut f = Fixture::striped(8);
25939        let other = apart(&mut f, "s1");
25940        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
25941
25942        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
25943        f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
25944        let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
25945        assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
25946        assert!(got.contains("1-1"), "the first one is in there: {got}");
25947        assert!(got.contains("2-1"), "and so is the second: {got}");
25948
25949        // A group read looks its group up on every key before it reads any of
25950        // them, so a group that is missing on the far key stops the near one.
25951        f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
25952        let got = f.run(&[
25953            b"XREADGROUP",
25954            b"GROUP",
25955            b"g",
25956            b"c",
25957            b"STREAMS",
25958            s1,
25959            s2,
25960            b">",
25961            b">",
25962        ]);
25963        assert!(got.starts_with("-NOGROUP"), "{got}");
25964        assert_eq!(
25965            f.run(&[b"XPENDING", s1, b"g"]),
25966            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
25967            "and read nothing from the key that did have the group"
25968        );
25969
25970        f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
25971        let got = f.run(&[
25972            b"XREADGROUP",
25973            b"GROUP",
25974            b"g",
25975            b"c",
25976            b"STREAMS",
25977            s1,
25978            s2,
25979            b">",
25980            b">",
25981        ]);
25982        assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
25983    }
25984
25985    /// A client parked on an `XREAD` woken by an entry on another stripe.
25986    #[test]
25987    fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
25988        let mut f = Fixture::striped(8);
25989        let other = apart(&mut f, "s1");
25990        let (s1, far) = (b"s1".as_slice(), other.as_bytes());
25991        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
25992        f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
25993
25994        assert_eq!(
25995            f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
25996                .0,
25997            Flow::Block
25998        );
25999        f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
26000        let mut out = Out::new(Proto::Resp2);
26001        assert!(f.server.serve_waiter(7, 0, &mut out));
26002        let want = format!(
26003            "*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",
26004            other.len()
26005        );
26006        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
26007    }
26008
26009    /// Every JSON command, on one stripe and on eight.
26010    #[test]
26011    fn the_json_group_answers_the_same_however_many_stripes_there_are() {
26012        let script: &[&[&[u8]]] = &[
26013            &[
26014                b"JSON.SET",
26015                b"d",
26016                b"$",
26017                br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
26018            ],
26019            &[b"JSON.SET", b"d", b"$.a", b"2"],
26020            &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
26021            &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
26022            &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
26023            &[b"JSON.GET", b"d"],
26024            &[b"JSON.GET", b"d", b"$.b"],
26025            &[b"JSON.GET", b"gone", b"$"],
26026            &[b"JSON.TYPE", b"d", b"$.b"],
26027            &[b"JSON.TYPE", b"d", b"$.s"],
26028            &[b"JSON.TOGGLE", b"d", b"$.t"],
26029            &[b"JSON.ARRLEN", b"d", b"$.b"],
26030            &[b"JSON.OBJLEN", b"d", b"$"],
26031            &[b"JSON.OBJKEYS", b"d", b"$"],
26032            &[b"JSON.STRLEN", b"d", b"$.s"],
26033            &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
26034            &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
26035            &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
26036            &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
26037            &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
26038            &[b"JSON.ARRPOP", b"d", b"$.b"],
26039            &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
26040            &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
26041            &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
26042            &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
26043            &[b"JSON.RESP", b"d", b"$.b"],
26044            &[b"JSON.DEBUG", b"MEMORY", b"d"],
26045            &[b"JSON.CLEAR", b"d", b"$.b"],
26046            &[b"JSON.DEL", b"d", b"$.m"],
26047            &[b"JSON.FORGET", b"d", b"$.nothere"],
26048            // The two that name more than one key.
26049            &[
26050                b"JSON.MSET",
26051                b"m1",
26052                b"$",
26053                b"1",
26054                b"m2",
26055                b"$",
26056                b"2",
26057                b"m3",
26058                b"$",
26059                b"3",
26060            ],
26061            &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
26062            &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
26063            &[b"JSON.GET", b"m1", b"$"],
26064            &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
26065            &[b"JSON.GET", b"m2", b"$"],
26066            // And the errors.
26067            &[b"SET", b"plain", b"v"],
26068            &[b"JSON.GET", b"plain", b"$"],
26069            &[b"JSON.SET", b"plain", b"$", b"1"],
26070            &[b"JSON.MGET", b"m1", b"plain", b"$"],
26071            &[b"JSON.SET", b"d", b"$.b", b"["],
26072            &[b"JSON.DEL", b"plain"],
26073        ];
26074
26075        let mut one = Fixture::new();
26076        let mut many = Fixture::striped(8);
26077        for parts in script {
26078            let a = one.run(parts);
26079            let b = many.run(parts);
26080            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26081        }
26082    }
26083
26084    /// A `JSON.MSET` and a `JSON.MGET` whose keys are on several stripes.
26085    ///
26086    /// `JSON.MSET` works every triple out against the keyspace as it was before
26087    /// the command and writes nothing until all of them are known to work, so
26088    /// the thing to check is that a triple that cannot be written stops the
26089    /// ones on other stripes as well as the ones on its own.
26090    #[test]
26091    fn a_json_multi_write_across_stripes_reaches_every_key() {
26092        let mut f = Fixture::striped(8);
26093        let second = apart(&mut f, "m1");
26094        let third = apart(&mut f, &second);
26095        let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
26096
26097        assert_eq!(
26098            f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
26099            "+OK\r\n"
26100        );
26101        assert_eq!(
26102            f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
26103            "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
26104        );
26105
26106        // A value that is not JSON is refused before anything is written, and
26107        // the key on the far stripe keeps what it had.
26108        assert_eq!(
26109            f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
26110            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
26111        );
26112        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
26113
26114        // A path that names nowhere is not an error. That triple is skipped,
26115        // the ones on the other stripes are still written, and the reply is a
26116        // nil rather than OK.
26117        assert_eq!(
26118            f.run(&[
26119                b"JSON.MSET",
26120                m1,
26121                b"$",
26122                b"9",
26123                m2,
26124                b"$.deep",
26125                b"9",
26126                m3,
26127                b"$",
26128                b"7"
26129            ]),
26130            "$-1\r\n"
26131        );
26132        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
26133        assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
26134        assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
26135    }
26136
26137    /// Every geospatial command, on one stripe and on eight.
26138    #[test]
26139    fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
26140        let script: &[&[&[u8]]] = &[
26141            &[
26142                b"GEOADD",
26143                b"g",
26144                b"13.361389",
26145                b"38.115556",
26146                b"palermo",
26147                b"15.087269",
26148                b"37.502669",
26149                b"catania",
26150            ],
26151            &[
26152                b"GEOADD",
26153                b"g",
26154                b"NX",
26155                b"13.361389",
26156                b"38.115556",
26157                b"palermo",
26158            ],
26159            &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
26160            &[b"GEOPOS", b"g", b"palermo", b"nothere"],
26161            &[b"GEOHASH", b"g", b"palermo", b"catania"],
26162            &[b"GEODIST", b"g", b"palermo", b"catania"],
26163            &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
26164            &[b"GEODIST", b"g", b"palermo", b"nothere"],
26165            &[
26166                b"GEOSEARCH",
26167                b"g",
26168                b"FROMLONLAT",
26169                b"15",
26170                b"37",
26171                b"BYRADIUS",
26172                b"200",
26173                b"KM",
26174                b"ASC",
26175                b"WITHCOORD",
26176                b"WITHDIST",
26177                b"WITHHASH",
26178            ],
26179            &[
26180                b"GEOSEARCH",
26181                b"g",
26182                b"FROMMEMBER",
26183                b"palermo",
26184                b"BYBOX",
26185                b"400",
26186                b"400",
26187                b"KM",
26188                b"DESC",
26189            ],
26190            &[
26191                b"GEORADIUS",
26192                b"g",
26193                b"15",
26194                b"37",
26195                b"200",
26196                b"KM",
26197                b"COUNT",
26198                b"1",
26199            ],
26200            &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
26201            &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
26202            &[
26203                b"GEOSEARCHSTORE",
26204                b"dst",
26205                b"g",
26206                b"FROMLONLAT",
26207                b"15",
26208                b"37",
26209                b"BYRADIUS",
26210                b"200",
26211                b"KM",
26212            ],
26213            &[b"ZRANGE", b"dst", b"0", b"-1"],
26214            &[
26215                b"GEOSEARCHSTORE",
26216                b"dst",
26217                b"g",
26218                b"FROMLONLAT",
26219                b"15",
26220                b"37",
26221                b"BYRADIUS",
26222                b"1",
26223                b"M",
26224                b"STOREDIST",
26225            ],
26226            &[b"EXISTS", b"dst"],
26227            &[
26228                b"GEORADIUS",
26229                b"g",
26230                b"15",
26231                b"37",
26232                b"200",
26233                b"KM",
26234                b"STORE",
26235                b"dst",
26236            ],
26237            &[b"ZCARD", b"dst"],
26238            // And the errors.
26239            &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
26240            &[b"SET", b"plain", b"v"],
26241            &[b"GEOPOS", b"plain", b"a"],
26242            &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
26243            &[
26244                b"GEOSEARCHSTORE",
26245                b"dst",
26246                b"g",
26247                b"FROMLONLAT",
26248                b"15",
26249                b"37",
26250                b"BYRADIUS",
26251                b"200",
26252                b"KM",
26253                b"WITHCOORD",
26254            ],
26255        ];
26256
26257        let mut one = Fixture::new();
26258        let mut many = Fixture::striped(8);
26259        for parts in script {
26260            let a = one.run(parts);
26261            let b = many.run(parts);
26262            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26263        }
26264    }
26265
26266    /// A `GEOSEARCHSTORE` whose two keys are on two stripes.
26267    #[test]
26268    fn a_geo_search_store_across_stripes_writes_what_it_found() {
26269        let mut f = Fixture::striped(8);
26270        let other = apart(&mut f, "g");
26271        let third = apart(&mut f, &other);
26272        let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
26273
26274        f.run(&[
26275            b"GEOADD",
26276            g,
26277            b"13.361389",
26278            b"38.115556",
26279            b"palermo",
26280            b"15.087269",
26281            b"37.502669",
26282            b"catania",
26283        ]);
26284        assert_eq!(
26285            f.run(&[
26286                b"GEOSEARCHSTORE",
26287                dst,
26288                g,
26289                b"FROMLONLAT",
26290                b"15",
26291                b"37",
26292                b"BYRADIUS",
26293                b"200",
26294                b"KM",
26295                b"ASC",
26296            ]),
26297            ":2\r\n"
26298        );
26299        assert_eq!(
26300            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
26301            "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
26302            "the geohash is the score, so the order is not the search order"
26303        );
26304        assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
26305
26306        // `STOREDIST` stores the distance in the unit the search was asked in,
26307        // which is the destination stripe's sorted set and not the source's.
26308        assert_eq!(
26309            f.run(&[
26310                b"GEOSEARCHSTORE",
26311                dst,
26312                g,
26313                b"FROMMEMBER",
26314                b"palermo",
26315                b"BYRADIUS",
26316                b"200",
26317                b"KM",
26318                b"STOREDIST",
26319            ]),
26320            ":2\r\n"
26321        );
26322        assert_eq!(
26323            f.run(&[b"ZSCORE", dst, b"palermo"]),
26324            "$1\r\n0\r\n",
26325            "the centre is nought away from itself"
26326        );
26327
26328        // A search that found nothing deletes the destination on its own
26329        // stripe, and a source of the wrong type is refused with the
26330        // destination left alone.
26331        assert_eq!(
26332            f.run(&[
26333                b"GEOSEARCHSTORE",
26334                dst,
26335                g,
26336                b"FROMLONLAT",
26337                b"0",
26338                b"0",
26339                b"BYRADIUS",
26340                b"1",
26341                b"M",
26342            ]),
26343            ":0\r\n"
26344        );
26345        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
26346        f.run(&[
26347            b"GEOSEARCHSTORE",
26348            dst,
26349            g,
26350            b"FROMLONLAT",
26351            b"15",
26352            b"37",
26353            b"BYRADIUS",
26354            b"200",
26355            b"KM",
26356        ]);
26357        f.run(&[b"SET", plain, b"v"]);
26358        assert_eq!(
26359            f.run(&[
26360                b"GEOSEARCHSTORE",
26361                dst,
26362                plain,
26363                b"FROMLONLAT",
26364                b"15",
26365                b"37",
26366                b"BYRADIUS",
26367                b"200",
26368                b"KM",
26369            ]),
26370            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
26371        );
26372        assert_eq!(
26373            f.run(&[b"ZCARD", dst]),
26374            ":2\r\n",
26375            "and left the destination"
26376        );
26377    }
26378
26379    /// Every time series command, on one stripe and on eight.
26380    ///
26381    /// Every timestamp is written out rather than left to the clock, so the two
26382    /// servers are compared on the samples they hold and not on how long the
26383    /// test took to get from one of them to the other.
26384    #[test]
26385    fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
26386        let script: &[&[&[u8]]] = &[
26387            &[
26388                b"TS.CREATE",
26389                b"ts:a",
26390                b"LABELS",
26391                b"sensor",
26392                b"a",
26393                b"room",
26394                b"1",
26395            ],
26396            &[b"TS.CREATE", b"ts:a"],
26397            &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
26398            &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
26399            &[
26400                b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
26401            ],
26402            &[
26403                b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
26404            ],
26405            &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
26406            &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
26407            &[b"TS.GET", b"ts:a"],
26408            &[b"TS.GET", b"gone"],
26409            &[b"TS.RANGE", b"ts:a", b"-", b"+"],
26410            &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
26411            &[
26412                b"TS.RANGE",
26413                b"ts:a",
26414                b"-",
26415                b"+",
26416                b"AGGREGATION",
26417                b"avg",
26418                b"2000",
26419            ],
26420            &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
26421            &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
26422            &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
26423            &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
26424            &[b"TS.READ", b"ts:a", b"0"],
26425            &[b"TS.READ", b"ts:a", b"+"],
26426            // The filters, which are the ones that have to walk every stripe.
26427            &[b"TS.QUERYINDEX", b"sensor=a"],
26428            &[b"TS.QUERYINDEX", b"room=1"],
26429            &[b"TS.QUERYINDEX", b"room=9"],
26430            &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
26431            &[
26432                b"TS.QUERYLABELS",
26433                b"VALUES",
26434                b"sensor",
26435                b"FILTER",
26436                b"room=1",
26437            ],
26438            &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
26439            &[
26440                b"TS.MGET",
26441                b"SELECTED_LABELS",
26442                b"sensor",
26443                b"FILTER",
26444                b"sensor=a",
26445            ],
26446            &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
26447            &[
26448                b"TS.MREVRANGE",
26449                b"-",
26450                b"+",
26451                b"WITHLABELS",
26452                b"FILTER",
26453                b"sensor=a",
26454            ],
26455            &[
26456                b"TS.MRANGE",
26457                b"-",
26458                b"+",
26459                b"FILTER",
26460                b"room=1",
26461                b"GROUPBY",
26462                b"room",
26463                b"REDUCE",
26464                b"max",
26465            ],
26466            &[b"TS.INFO", b"ts:a"],
26467            // And a rule, which is the one thing here that names two keys.
26468            &[
26469                b"TS.CREATERULE",
26470                b"ts:a",
26471                b"ts:down",
26472                b"AGGREGATION",
26473                b"avg",
26474                b"1000",
26475            ],
26476            &[b"TS.CREATE", b"ts:down"],
26477            &[
26478                b"TS.CREATERULE",
26479                b"ts:a",
26480                b"ts:down",
26481                b"AGGREGATION",
26482                b"avg",
26483                b"1000",
26484            ],
26485            &[b"TS.ADD", b"ts:a", b"5000", b"4"],
26486            &[b"TS.ADD", b"ts:a", b"6000", b"5"],
26487            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
26488            &[b"TS.GET", b"ts:down", b"LATEST"],
26489            &[b"TS.INFO", b"ts:down"],
26490            &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
26491            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
26492            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
26493            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
26494            &[b"TS.DEL", b"ts:a", b"0", b"1000"],
26495            // And the errors.
26496            &[b"SET", b"plain", b"v"],
26497            &[b"TS.ADD", b"plain", b"1", b"1"],
26498            &[b"TS.GET", b"plain"],
26499            &[b"TS.READ", b"plain", b"0"],
26500            &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
26501            &[b"TS.RANGE", b"gone", b"-", b"+"],
26502            &[b"TS.INFO", b"gone"],
26503        ];
26504
26505        let mut one = Fixture::new();
26506        let mut many = Fixture::striped(8);
26507        for parts in script {
26508            let a = one.run(parts);
26509            let b = many.run(parts);
26510            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26511        }
26512    }
26513
26514    /// A compaction rule whose two ends are on two stripes.
26515    ///
26516    /// This is the one thing in the family that walks from a key to another key,
26517    /// and it walks it in both directions: a sample on the source closes a
26518    /// bucket on the destination, a `LATEST` read on the destination folds the
26519    /// bucket the source is still filling, and a delete on the source rewrites
26520    /// what the destination already held. The same script is run against a
26521    /// server one stripe wide, where the two keys share a store, and against one
26522    /// eight stripes wide, where they do not.
26523    #[test]
26524    fn a_compaction_rule_across_stripes_reaches_both_ends() {
26525        let mut many = Fixture::striped(8);
26526        let other = apart(&mut many, "src");
26527        let (src, dst) = (b"src".as_slice(), other.as_bytes());
26528        let mut one = Fixture::new();
26529        let mut both = |parts: &[&[u8]]| {
26530            let a = one.run(parts);
26531            let b = many.run(parts);
26532            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26533            a
26534        };
26535
26536        both(&[b"TS.CREATE", src]);
26537        both(&[b"TS.CREATE", dst]);
26538        assert_eq!(
26539            both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
26540            "+OK\r\n"
26541        );
26542        both(&[b"TS.ADD", src, b"1000", b"1"]);
26543        both(&[b"TS.ADD", src, b"1500", b"3"]);
26544        // The bucket the source is filling is not written down yet, and asking
26545        // for it works it out off the source.
26546        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
26547        let open = both(&[b"TS.GET", dst, b"LATEST"]);
26548        assert!(open.contains(":1000"), "the open bucket is folded: {open}");
26549
26550        // A sample past the bucket closes it, which is the write that has to
26551        // land on the other stripe.
26552        both(&[b"TS.ADD", src, b"2000", b"5"]);
26553        let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
26554        assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
26555        assert!(got.contains(":1000"), "{got}");
26556
26557        // And a delete on the source takes it away again.
26558        both(&[b"TS.DEL", src, b"1000", b"1999"]);
26559        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
26560
26561        // Both ends still know about each other, and the link comes apart from
26562        // the source.
26563        assert!(
26564            both(&[b"TS.INFO", dst]).contains("src"),
26565            "the source is named"
26566        );
26567        assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
26568        assert_eq!(
26569            both(&[b"TS.DELETERULE", src, dst]),
26570            "-ERR TSDB: compaction rule does not exist\r\n"
26571        );
26572    }
26573
26574    /// A label filter takes the series it names wherever they landed.
26575    #[test]
26576    fn a_label_query_across_stripes_finds_every_series() {
26577        let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
26578        let mut many = Fixture::striped(8);
26579        let mut homes: Vec<usize> = names
26580            .iter()
26581            .map(|name| many.server.striped(0).stripe_of(name))
26582            .collect();
26583        homes.sort_unstable();
26584        homes.dedup();
26585        assert!(homes.len() > 1, "the six keys are not all on one stripe");
26586
26587        let mut one = Fixture::new();
26588        let mut both = |parts: &[&[u8]]| {
26589            let a = one.run(parts);
26590            let b = many.run(parts);
26591            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26592            a
26593        };
26594        for name in &names {
26595            both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
26596            both(&[b"TS.ADD", name, b"1000", b"1"]);
26597        }
26598
26599        let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
26600        assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
26601        assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
26602        assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
26603        assert_eq!(
26604            both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
26605            "*1\r\n$4\r\nroom\r\n"
26606        );
26607    }
26608
26609    /// Every hash command, and the field import beside it, on one stripe and on
26610    /// eight.
26611    ///
26612    /// `HRANDFIELD` with a count draws from the stripe's own generator and two
26613    /// stripes do not draw the same numbers, so the only draw here is off a hash
26614    /// holding one field, where every generator gives the same answer.
26615    #[test]
26616    fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
26617        let script: &[&[&[u8]]] = &[
26618            &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
26619            &[b"HMSET", b"h", b"c", b"3"],
26620            &[b"HSETNX", b"h", b"a", b"9"],
26621            &[b"HSETNX", b"h", b"d", b"4"],
26622            &[b"HGET", b"h", b"a"],
26623            &[b"HGET", b"h", b"nope"],
26624            &[b"HMGET", b"h", b"a", b"nope"],
26625            &[b"HLEN", b"h"],
26626            &[b"HEXISTS", b"h", b"a"],
26627            &[b"HSTRLEN", b"h", b"a"],
26628            &[b"HGETALL", b"h"],
26629            &[b"HKEYS", b"h"],
26630            &[b"HVALS", b"h"],
26631            &[b"HINCRBY", b"h", b"a", b"5"],
26632            &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
26633            &[b"HSCAN", b"h", b"0"],
26634            &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
26635            &[b"HSCAN", b"h", b"0", b"NOVALUES"],
26636            &[b"HDEL", b"h", b"d"],
26637            &[b"HSET", b"one", b"f", b"v"],
26638            &[b"HRANDFIELD", b"one"],
26639            &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
26640            // The field deadlines.
26641            &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
26642            &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
26643            &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
26644            &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
26645            &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
26646            &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
26647            &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
26648            &[b"HGET", b"h", b"b"],
26649            // The three that came later and word everything their own way.
26650            &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
26651            &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
26652            &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
26653            &[b"HGET", b"h", b"e"],
26654            // And the import, whose key is the third word.
26655            &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
26656            &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
26657            &[b"HGETALL", b"imp"],
26658            &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
26659            &[b"HIMPORT", b"DISCARD", b"fs"],
26660            // And the errors.
26661            &[b"SET", b"plain", b"v"],
26662            &[b"HSET", b"plain", b"a", b"1"],
26663            &[b"HGETALL", b"plain"],
26664            &[b"HGET", b"gone", b"a"],
26665            &[b"HINCRBY", b"h", b"a", b"nan"],
26666        ];
26667
26668        let mut one = Fixture::new();
26669        let mut many = Fixture::striped(8);
26670        // The field deadlines are absolute milliseconds worked out from the
26671        // clock, so both servers are put on the same one rather than left to
26672        // read the wall a moment apart.
26673        one.server.set_clock_ms(1_700_000_000_000);
26674        many.server.set_clock_ms(1_700_000_000_000);
26675        for parts in script {
26676            let a = one.run(parts);
26677            let b = many.run(parts);
26678            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26679        }
26680    }
26681
26682    /// Every array command, on one stripe and on eight.
26683    #[test]
26684    fn the_array_group_answers_the_same_however_many_stripes_there_are() {
26685        let script: &[&[&[u8]]] = &[
26686            &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
26687            &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
26688            &[b"ARGET", b"a", b"1"],
26689            &[b"ARGET", b"a", b"99"],
26690            &[b"ARMGET", b"a", b"0", b"5", b"99"],
26691            &[b"ARGETRANGE", b"a", b"0", b"7"],
26692            &[b"ARLEN", b"a"],
26693            &[b"ARCOUNT", b"a"],
26694            &[b"ARINSERT", b"a", b"m", b"n"],
26695            &[b"ARSCAN", b"a", b"0", b"20"],
26696            &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
26697            &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
26698            &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
26699            &[b"ARLASTITEMS", b"a", b"2"],
26700            &[b"ARLASTITEMS", b"a", b"2", b"REV"],
26701            &[b"ARNEXT", b"a"],
26702            &[b"ARSEEK", b"a", b"3"],
26703            &[b"AROP", b"a", b"0", b"20", b"USED"],
26704            &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
26705            &[b"ARINFO", b"a"],
26706            &[b"ARINFO", b"a", b"FULL"],
26707            &[b"ARDEL", b"a", b"0"],
26708            &[b"ARDELRANGE", b"a", b"1", b"2"],
26709            &[b"ARCOUNT", b"a"],
26710            &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
26711            &[b"ARGETRANGE", b"r", b"0", b"9"],
26712            // And the errors.
26713            &[b"SET", b"plain", b"v"],
26714            &[b"ARGET", b"plain", b"0"],
26715            &[b"ARSET", b"plain", b"0", b"v"],
26716            &[b"ARGET", b"gone", b"0"],
26717            &[b"ARSET", b"a", b"bad", b"v"],
26718        ];
26719
26720        let mut one = Fixture::new();
26721        let mut many = Fixture::striped(8);
26722        for parts in script {
26723            let a = one.run(parts);
26724            let b = many.run(parts);
26725            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26726        }
26727    }
26728
26729    /// Every graph and vector set command, on one stripe and on eight.
26730    ///
26731    /// `VRANDMEMBER` is not in here for the reason `HRANDFIELD` with a count is
26732    /// not: it draws from the stripe's generator, and the stripes do not share
26733    /// one.
26734    #[test]
26735    fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
26736        let script: &[&[&[u8]]] = &[
26737            &[b"G.NADD", b"g", b"n1", b"name", b"one"],
26738            &[b"G.NADD", b"g", b"n2", b"name", b"two"],
26739            &[b"G.NADD", b"g", b"n3"],
26740            &[b"G.NGET", b"g", b"n1"],
26741            &[b"G.NGET", b"g", b"gone"],
26742            &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
26743            &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
26744            &[b"G.OUT", b"g", b"n1", b"knows"],
26745            &[b"G.IN", b"g", b"n2", b"knows"],
26746            &[b"G.DEG", b"g", b"n1", b"knows"],
26747            &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
26748            &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
26749            &[b"G.PATH", b"g", b"n1", b"n3"],
26750            &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
26751            &[b"G.NDEL", b"g", b"n3"],
26752            &[b"G.NGET", b"g", b"n3"],
26753            // The vector set, which is one index under one key.
26754            &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
26755            &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
26756            &[b"VCARD", b"v"],
26757            &[b"VDIM", b"v"],
26758            &[b"VEMB", b"v", b"e1"],
26759            &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
26760            &[b"VSIM", b"v", b"ELE", b"e1"],
26761            &[b"VISMEMBER", b"v", b"e1"],
26762            &[b"VISMEMBER", b"v", b"gone"],
26763            &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
26764            &[b"VGETATTR", b"v", b"e1"],
26765            &[b"VRANGE", b"v", b"-", b"+"],
26766            &[b"VLINKS", b"v", b"e1"],
26767            &[b"VINFO", b"v"],
26768            &[b"VREM", b"v", b"e2"],
26769            &[b"VCARD", b"v"],
26770            // And the errors.
26771            &[b"SET", b"plain", b"v"],
26772            &[b"G.NGET", b"plain", b"n1"],
26773            &[b"VCARD", b"plain"],
26774            &[b"G.NADD", b"gone2", b"n"],
26775            &[b"VEMB", b"gone3", b"e"],
26776        ];
26777
26778        let mut one = Fixture::new();
26779        let mut many = Fixture::striped(8);
26780        for parts in script {
26781            let a = one.run(parts);
26782            let b = many.run(parts);
26783            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26784        }
26785    }
26786
26787    /// Every bloom filter, cuckoo filter, count min sketch, top k and t digest
26788    /// command, on one stripe and on eight.
26789    #[test]
26790    fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
26791        let script: &[&[&[u8]]] = &[
26792            // The bloom filter.
26793            &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
26794            &[b"BF.ADD", b"bf", b"a"],
26795            &[b"BF.ADD", b"bf", b"a"],
26796            &[b"BF.MADD", b"bf", b"b", b"c"],
26797            &[b"BF.EXISTS", b"bf", b"a"],
26798            &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
26799            &[b"BF.CARD", b"bf"],
26800            &[b"BF.INFO", b"bf"],
26801            &[b"BF.INFO", b"bf", b"CAPACITY"],
26802            &[b"BF.DEBUG", b"bf"],
26803            &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
26804            &[b"BF.EXISTS", b"made", b"x"],
26805            &[b"BF.SCANDUMP", b"bf", b"0"],
26806            // The cuckoo filter.
26807            &[b"CF.RESERVE", b"cf", b"100"],
26808            &[b"CF.ADD", b"cf", b"a"],
26809            &[b"CF.ADDNX", b"cf", b"a"],
26810            &[b"CF.COUNT", b"cf", b"a"],
26811            &[b"CF.EXISTS", b"cf", b"a"],
26812            &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
26813            &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
26814            &[b"CF.DEL", b"cf", b"a"],
26815            &[b"CF.COMPACT", b"cf"],
26816            &[b"CF.INFO", b"cf"],
26817            &[b"CF.DEBUG", b"cf"],
26818            &[b"CF.SCANDUMP", b"cf", b"0"],
26819            // The count min sketch.
26820            &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
26821            &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
26822            &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
26823            &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
26824            &[b"CMS.INFO", b"cms"],
26825            // The top k sketch.
26826            &[b"TOPK.RESERVE", b"tk", b"3"],
26827            &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
26828            &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
26829            &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
26830            &[b"TOPK.COUNT", b"tk", b"a", b"c"],
26831            &[b"TOPK.LIST", b"tk"],
26832            &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
26833            &[b"TOPK.INFO", b"tk"],
26834            // The t digest.
26835            &[b"TDIGEST.CREATE", b"td"],
26836            &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
26837            &[b"TDIGEST.MIN", b"td"],
26838            &[b"TDIGEST.MAX", b"td"],
26839            &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
26840            &[b"TDIGEST.CDF", b"td", b"3"],
26841            &[b"TDIGEST.RANK", b"td", b"3"],
26842            &[b"TDIGEST.REVRANK", b"td", b"3"],
26843            &[b"TDIGEST.BYRANK", b"td", b"0"],
26844            &[b"TDIGEST.BYREVRANK", b"td", b"0"],
26845            &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
26846            &[b"TDIGEST.INFO", b"td"],
26847            &[b"TDIGEST.RESET", b"td"],
26848            &[b"TDIGEST.MIN", b"td"],
26849            // And the errors.
26850            &[b"SET", b"plain", b"v"],
26851            &[b"BF.ADD", b"plain", b"a"],
26852            &[b"CF.ADD", b"plain", b"a"],
26853            &[b"CMS.QUERY", b"plain", b"a"],
26854            &[b"TOPK.ADD", b"plain", b"a"],
26855            &[b"TDIGEST.ADD", b"plain", b"1"],
26856            &[b"CMS.INFO", b"gone"],
26857            &[b"TOPK.INFO", b"gone"],
26858            &[b"TDIGEST.INFO", b"gone"],
26859        ];
26860
26861        let mut one = Fixture::new();
26862        let mut many = Fixture::striped(8);
26863        for parts in script {
26864            let a = one.run(parts);
26865            let b = many.run(parts);
26866            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26867        }
26868    }
26869
26870    /// The two sketch merges, with their sources on stripes of their own.
26871    ///
26872    /// These are the only two commands in the ten groups that name more than one
26873    /// key, and both read a run of sources and write a destination, so both go
26874    /// wrong in the same way if a merge holds one store and looks every source up
26875    /// in it.
26876    #[test]
26877    fn a_sketch_merge_across_stripes_reads_every_source() {
26878        let mut many = Fixture::striped(8);
26879        let other = apart(&mut many, "s1");
26880        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
26881        let mut one = Fixture::new();
26882        let mut both = |parts: &[&[u8]]| {
26883            let a = one.run(parts);
26884            let b = many.run(parts);
26885            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26886            a
26887        };
26888
26889        // The count min sketch. The destination has to be the sources' shape,
26890        // and it is named first, so all three keys are read before anything is
26891        // written.
26892        for key in [b"cd".as_slice(), s1, s2] {
26893            both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
26894        }
26895        both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
26896        both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
26897        assert_eq!(
26898            both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
26899            "+OK\r\n",
26900            "the merge took both sources"
26901        );
26902        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
26903        // And with weights, which are read against the sources in order.
26904        both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
26905        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
26906        // A source that is not a sketch is answered before anything is written.
26907        both(&[b"SET", b"plain", b"v"]);
26908        assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
26909        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
26910
26911        // The t digest, which builds its destination and then puts it in place.
26912        // The two source keys are used again here, so what they held goes first.
26913        both(&[b"FLUSHALL"]);
26914        both(&[b"TDIGEST.CREATE", b"td"]);
26915        both(&[b"TDIGEST.CREATE", s1]);
26916        both(&[b"TDIGEST.CREATE", s2]);
26917        both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
26918        both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
26919        assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
26920        assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
26921        assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
26922    }
26923
26924    /// Every shape of `SORT`, on one stripe and on eight.
26925    ///
26926    /// The key it sorts, the keys a `BY` names, the keys a `GET` names and the
26927    /// destination are four different names and nothing lines them up, so on
26928    /// eight stripes this script is reading and writing all over the database
26929    /// while on one it is doing what it always did.
26930    #[test]
26931    fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
26932        let script: &[&[&[u8]]] = &[
26933            &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
26934            &[b"SORT", b"l"],
26935            &[b"SORT", b"l", b"DESC"],
26936            &[b"SORT", b"l", b"ALPHA"],
26937            &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
26938            &[b"SORT_RO", b"l"],
26939            // A weight per element, so the order comes off keys the command
26940            // never named.
26941            &[
26942                b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
26943            ],
26944            &[b"SORT", b"l", b"BY", b"w_*"],
26945            &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
26946            &[b"DEL", b"w_2"],
26947            &[b"SORT", b"l", b"BY", b"w_*"],
26948            // And the answer off another set of keys again, with `#` mixed in
26949            // so the rows are not all lookups.
26950            &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
26951            &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
26952            // A pattern that reaches into a hash, which is another key again.
26953            &[b"HSET", b"h_1", b"f", b"9"],
26954            &[b"HSET", b"h_2", b"f", b"8"],
26955            &[b"HSET", b"h_3", b"f", b"7"],
26956            &[b"HSET", b"h_10", b"f", b"6"],
26957            &[b"SORT", b"l", b"BY", b"h_*->f"],
26958            &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
26959            // The destination, which is a fourth place to land.
26960            &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
26961            &[b"LRANGE", b"out", b"0", b"-1"],
26962            &[b"SORT", b"l", b"STORE", b"l"],
26963            &[b"LRANGE", b"l", b"0", b"-1"],
26964            // An empty result takes the destination away rather than leaving a
26965            // list of nothing behind.
26966            &[b"SORT", b"missing", b"STORE", b"out"],
26967            &[b"EXISTS", b"out"],
26968            // A set and a sorted set sort the same way a list does, and a set
26969            // written to a destination is sorted even when nothing asked.
26970            &[b"SADD", b"s", b"c", b"a", b"b"],
26971            &[b"SORT", b"s", b"ALPHA"],
26972            &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
26973            &[b"LRANGE", b"out", b"0", b"-1"],
26974            &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
26975            &[b"SORT", b"z", b"BY", b"nosort"],
26976            &[b"SORT", b"z", b"ALPHA", b"DESC"],
26977            // And the two ways it refuses: a key of the wrong type, and an
26978            // element that is not a number under a numeric sort.
26979            &[b"SET", b"str", b"v"],
26980            &[b"SORT", b"str"],
26981            &[b"RPUSH", b"words", b"one", b"two"],
26982            &[b"SORT", b"words"],
26983            &[b"SORT_RO", b"l", b"STORE", b"out"],
26984        ];
26985
26986        let mut one = Fixture::new();
26987        let mut many = Fixture::striped(8);
26988        for parts in script {
26989            let a = one.run(parts);
26990            let b = many.run(parts);
26991            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
26992        }
26993    }
26994
26995    /// One `SORT` whose four kinds of key are on stripes of their own.
26996    ///
26997    /// The script above spreads keys around by writing enough of them, and this
26998    /// one checks the spread rather than trusting it: the list, the weight key
26999    /// for one of its elements and the destination are asserted to be in three
27000    /// places before the command runs.
27001    #[test]
27002    fn a_sort_across_stripes_reads_every_pattern_key() {
27003        let mut f = Fixture::striped(8);
27004        let out = apart(&mut f, "l");
27005        let (list, dest) = (b"l".as_slice(), out.as_bytes());
27006
27007        f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
27008        f.run(&[
27009            b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
27010        ]);
27011        f.run(&[
27012            b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
27013        ]);
27014
27015        // The weights are four keys and they are not all in one place, which is
27016        // the thing that would go unnoticed if the command held a stripe.
27017        let db = f.server.striped(0);
27018        let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
27019            .iter()
27020            .map(|k| db.stripe_of(k.as_slice()))
27021            .collect();
27022        assert!(
27023            weights.iter().any(|s| *s != weights[0]),
27024            "the four weight keys all landed on one stripe, so this proves nothing"
27025        );
27026
27027        assert_eq!(
27028            f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
27029            "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
27030            "the order came off the weights and the answer off the data keys"
27031        );
27032        assert_eq!(
27033            f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
27034            ":4\r\n"
27035        );
27036        assert_eq!(
27037            f.run(&[b"LRANGE", dest, b"0", b"-1"]),
27038            "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
27039            "the destination is on a stripe of its own and got the whole answer"
27040        );
27041    }
27042
27043    /// A `CONFIG SET` reaches every stripe, so where a key landed does not
27044    /// decide what shape it is stored in.
27045    ///
27046    /// This is the setting that would go wrong quietly. A stripe that kept the
27047    /// old ladder would hold the same hash in a different encoding from the
27048    /// stripe next to it, and the only thing that would ever say so is
27049    /// `OBJECT ENCODING`, which is why the check is on that.
27050    #[test]
27051    fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
27052        let mut f = Fixture::striped(8);
27053        let other = apart(&mut f, "h");
27054        let (first, second) = (b"h".as_slice(), other.as_bytes());
27055
27056        assert_eq!(
27057            f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
27058            "+OK\r\n"
27059        );
27060        assert_eq!(
27061            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
27062            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
27063            "the read comes off one stripe and has to answer for all of them"
27064        );
27065        for key in [first, second] {
27066            f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
27067            assert_eq!(
27068                f.run(&[b"OBJECT", b"ENCODING", key]),
27069                "$8\r\nlistpack\r\n",
27070                "two fields is still under the ladder"
27071            );
27072            f.run(&[b"HSET", key, b"c", b"3"]);
27073            assert_eq!(
27074                f.run(&[b"OBJECT", b"ENCODING", key]),
27075                "$9\r\nhashtable\r\n",
27076                "three fields is over it, on whichever stripe the key is on"
27077            );
27078        }
27079
27080        // And the policy, which every stripe has to agree about for the same
27081        // reason: an eviction draws from one stripe at a time.
27082        assert_eq!(
27083            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
27084            "+OK\r\n"
27085        );
27086        let db = f.server.striped(0);
27087        assert!(
27088            (0..db.width()).all(|i| db.hold_stripe(i).policy().name() == "allkeys-lru"),
27089            "a stripe kept the old policy"
27090        );
27091    }
27092
27093    /// What an index holds, as the two numbers `FT.INFO` reports about it.
27094    ///
27095    /// Read off the registry rather than parsed back out of an `FT.INFO` reply,
27096    /// because the reply is thirty odd fields and these two are the ones the
27097    /// keyspace hook moves.
27098    fn held(f: &Fixture, name: &[u8]) -> (usize, u32) {
27099        let search = f.server.search.lock();
27100        let index = search.named(name).expect("the index is there");
27101        (index.held.docs.len(), index.held.docs.last())
27102    }
27103
27104    /// A hash written under an index's prefix reaches it, and one written
27105    /// outside the prefix does not.
27106    #[test]
27107    fn a_hash_that_is_written_reaches_the_index_that_follows_it() {
27108        let mut f = Fixture::new();
27109        f.run(&[
27110            b"FT.CREATE",
27111            b"ix",
27112            b"PREFIX",
27113            b"1",
27114            b"p:",
27115            b"SCHEMA",
27116            b"t",
27117            b"TEXT",
27118        ]);
27119        f.run(&[b"HSET", b"p:1", b"t", b"running dogs"]);
27120        assert_eq!(held(&f, b"ix"), (1, 1));
27121        f.run(&[b"HSET", b"other:1", b"t", b"running dogs"]);
27122        assert_eq!(held(&f, b"ix"), (1, 1));
27123
27124        // Every field of the key and not the one the command named, since a
27125        // document is read from nothing every time.
27126        f.run(&[b"HSET", b"p:1", b"u", b"beta"]);
27127        f.run(&[b"HDEL", b"p:1", b"u"]);
27128        assert_eq!(held(&f, b"ix"), (1, 3));
27129        let search = f.server.search.lock();
27130        let index = search.named(b"ix").expect("there");
27131        assert_eq!(index.held.docs.id(b"p:1"), Some(3));
27132    }
27133
27134    /// A fresh index reads the keys that were already there, and walks past a
27135    /// key of the wrong type without counting a failure.
27136    #[test]
27137    fn a_fresh_index_reads_the_keys_that_were_already_there() {
27138        let mut f = Fixture::new();
27139        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27140        f.run(&[b"SET", b"p:str", b"not a hash"]);
27141        f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
27142        f.run(&[
27143            b"FT.CREATE",
27144            b"ix",
27145            b"PREFIX",
27146            b"1",
27147            b"p:",
27148            b"SCHEMA",
27149            b"t",
27150            b"TEXT",
27151        ]);
27152
27153        assert_eq!(held(&f, b"ix"), (1, 1));
27154        let search = f.server.search.lock();
27155        let index = search.named(b"ix").expect("there");
27156        assert_eq!(index.trouble.whole().failures(), 0);
27157    }
27158
27159    /// `SKIPINITIALSCAN` leaves what was there alone, and a later write to one
27160    /// of those keys still lands.
27161    #[test]
27162    fn an_index_that_skipped_the_scan_fills_up_on_the_next_write() {
27163        let mut f = Fixture::new();
27164        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27165        f.run(&[
27166            b"FT.CREATE",
27167            b"ix",
27168            b"PREFIX",
27169            b"1",
27170            b"p:",
27171            b"SKIPINITIALSCAN",
27172            b"SCHEMA",
27173            b"t",
27174            b"TEXT",
27175        ]);
27176        assert_eq!(held(&f, b"ix"), (0, 0));
27177        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27178        assert_eq!(held(&f, b"ix"), (1, 1));
27179    }
27180
27181    /// A command that changed nothing leaves the document where it was, which
27182    /// is not the same as a command that was not a write.
27183    ///
27184    /// All five of these were measured against 8.10.1. Writing the same value
27185    /// again moves the number and a deadline set for later does not, which is
27186    /// the pair that makes the rule "the fields are not what they were" rather
27187    /// than "this was a write".
27188    #[test]
27189    fn only_a_real_change_gives_the_document_a_new_number() {
27190        let mut f = Fixture::new();
27191        f.run(&[
27192            b"FT.CREATE",
27193            b"ix",
27194            b"PREFIX",
27195            b"1",
27196            b"p:",
27197            b"SCHEMA",
27198            b"t",
27199            b"TEXT",
27200        ]);
27201        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27202        assert_eq!(held(&f, b"ix"), (1, 1));
27203
27204        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27205        assert_eq!(held(&f, b"ix"), (1, 2), "the same value still rewrites");
27206
27207        for quiet in [
27208            vec![b"HSETNX".as_slice(), b"p:1", b"t", b"other"],
27209            vec![b"HDEL".as_slice(), b"p:1", b"nosuch"],
27210            vec![b"HGET".as_slice(), b"p:1", b"t"],
27211            vec![b"HGETALL".as_slice(), b"p:1"],
27212            vec![b"HEXPIRE".as_slice(), b"p:1", b"100", b"FIELDS", b"1", b"t"],
27213            vec![b"HPERSIST".as_slice(), b"p:1", b"FIELDS", b"1", b"t"],
27214            vec![
27215                b"HGETEX".as_slice(),
27216                b"p:1",
27217                b"EX",
27218                b"100",
27219                b"FIELDS",
27220                b"1",
27221                b"t",
27222            ],
27223            vec![b"HGETDEL".as_slice(), b"p:1", b"FIELDS", b"1", b"nosuch"],
27224        ] {
27225            f.run(&quiet);
27226            assert_eq!(held(&f, b"ix"), (1, 2), "{:?} moved the document", quiet[0]);
27227        }
27228
27229        // And the ones that do change something.
27230        f.run(&[b"HSET", b"p:2", b"n", b"1"]);
27231        f.run(&[b"HINCRBY", b"p:2", b"n", b"1"]);
27232        assert_eq!(held(&f, b"ix"), (2, 4));
27233        // A deadline that has already passed takes the field away, and taking
27234        // the last field away takes the key and the document with it. The
27235        // number still moves on the way past, because the field going and the
27236        // key going are two separate pieces of news and the first of them
27237        // writes the document one last time.
27238        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"n"]);
27239        assert_eq!(held(&f, b"ix"), (1, 5));
27240    }
27241
27242    /// The two ways of emptying a hash, which do not leave the same thing
27243    /// behind. `HDEL` of the last field spends no number and is counted as a
27244    /// refusal, and a deadline that has already passed spends one on a document
27245    /// nobody sees and is counted as nothing. Measured against 8.10.1 and not
27246    /// something anyone would guess.
27247    #[test]
27248    fn a_key_emptied_by_a_deadline_spends_a_number_and_one_emptied_by_hdel_does_not() {
27249        /// The index's own failure count.
27250        fn refused(f: &Fixture, name: &[u8]) -> u64 {
27251            let search = f.server.search.lock();
27252            let index = search.named(name).expect("the index is there");
27253            index.trouble.whole().failures()
27254        }
27255
27256        let mut f = Fixture::new();
27257        f.run(&[
27258            b"FT.CREATE",
27259            b"ix",
27260            b"PREFIX",
27261            b"1",
27262            b"p:",
27263            b"SCHEMA",
27264            b"t",
27265            b"TEXT",
27266        ]);
27267        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27268        assert_eq!(held(&f, b"ix"), (1, 1));
27269        f.run(&[b"HDEL", b"p:1", b"t"]);
27270        assert_eq!(
27271            held(&f, b"ix"),
27272            (0, 1),
27273            "HDEL of the last field spends none"
27274        );
27275        assert_eq!(refused(&f, b"ix"), 1, "and is counted as a refusal");
27276
27277        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
27278        assert_eq!(held(&f, b"ix"), (1, 2));
27279        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"t"]);
27280        assert_eq!(held(&f, b"ix"), (0, 3), "a deadline spends one");
27281        assert_eq!(refused(&f, b"ix"), 1, "and is counted as nothing");
27282
27283        f.run(&[b"HSET", b"p:3", b"t", b"alpha"]);
27284        assert_eq!(held(&f, b"ix"), (1, 4));
27285        f.run(&[b"HGETDEL", b"p:3", b"FIELDS", b"1", b"t"]);
27286        assert_eq!(held(&f, b"ix"), (0, 5), "and so does HGETDEL");
27287
27288        // Two fields and one command is one rewrite and not two, whichever way
27289        // the fields go.
27290        f.run(&[b"HSET", b"p:4", b"t", b"alpha", b"u", b"beta"]);
27291        assert_eq!(held(&f, b"ix"), (1, 6));
27292        f.run(&[b"HEXPIRE", b"p:4", b"0", b"FIELDS", b"2", b"t", b"u"]);
27293        assert_eq!(held(&f, b"ix"), (0, 7));
27294        assert_eq!(refused(&f, b"ix"), 1);
27295    }
27296
27297    /// `HSETEX` with a deadline that has already passed is two pieces of news
27298    /// from one command, so the number moves twice and the value never reaches
27299    /// the index.
27300    #[test]
27301    fn a_field_written_already_past_its_deadline_moves_the_number_twice() {
27302        let mut f = Fixture::new();
27303        f.run(&[
27304            b"FT.CREATE",
27305            b"ix",
27306            b"PREFIX",
27307            b"1",
27308            b"p:",
27309            b"SCHEMA",
27310            b"t",
27311            b"TEXT",
27312            b"u",
27313            b"TEXT",
27314        ]);
27315        f.run(&[b"HSET", b"p:1", b"u", b"keepme"]);
27316        assert_eq!(held(&f, b"ix"), (1, 1));
27317        f.run(&[
27318            b"HSETEX", b"p:1", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
27319        ]);
27320        assert_eq!(
27321            held(&f, b"ix"),
27322            (1, 3),
27323            "the key lived and the field did not"
27324        );
27325
27326        // And the same when the key does not survive it.
27327        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
27328        assert_eq!(held(&f, b"ix"), (2, 4));
27329        f.run(&[
27330            b"HSETEX", b"p:2", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
27331        ]);
27332        assert_eq!(held(&f, b"ix"), (1, 6));
27333    }
27334
27335    /// The number one key is indexed under, or `None` when it holds no
27336    /// document.
27337    fn number(f: &Fixture, name: &[u8], key: &[u8]) -> Option<u32> {
27338        let search = f.server.search.lock();
27339        let index = search.named(name).expect("the index is there");
27340        index.held.docs.id(key)
27341    }
27342
27343    /// An index over `p:` with one document under `p:1`, which is where four of
27344    /// the tests below start.
27345    fn indexed() -> Fixture {
27346        let mut f = Fixture::new();
27347        f.run(&[
27348            b"FT.CREATE",
27349            b"ix",
27350            b"PREFIX",
27351            b"1",
27352            b"p:",
27353            b"SCHEMA",
27354            b"t",
27355            b"TEXT",
27356        ]);
27357        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
27358        f
27359    }
27360
27361    /// Every way a keyspace command takes a key away leaves no document behind,
27362    /// and none of them spends a number or is counted as a refusal.
27363    #[test]
27364    fn a_key_a_keyspace_command_takes_away_loses_its_document() {
27365        for take in [
27366            vec![b"DEL".as_slice(), b"p:1"],
27367            vec![b"UNLINK".as_slice(), b"p:1"],
27368            vec![b"PEXPIREAT".as_slice(), b"p:1", b"1"],
27369            vec![b"EXPIRE".as_slice(), b"p:1", b"-1"],
27370        ] {
27371            let mut f = indexed();
27372            assert_eq!(held(&f, b"ix"), (1, 1));
27373            f.run(&take);
27374            assert_eq!(held(&f, b"ix"), (0, 1), "{:?} left something", take[0]);
27375            let search = f.server.search.lock();
27376            let index = search.named(b"ix").expect("the index is there");
27377            assert_eq!(index.trouble.whole().failures(), 0, "{:?}", take[0]);
27378        }
27379
27380        // A deadline that has not passed yet is not one of them.
27381        let mut f = indexed();
27382        f.run(&[b"EXPIRE", b"p:1", b"1000"]);
27383        assert_eq!(held(&f, b"ix"), (1, 1));
27384        f.run(&[b"PERSIST", b"p:1"]);
27385        assert_eq!(held(&f, b"ix"), (1, 1));
27386    }
27387
27388    /// A rename inside the prefix keeps the number the document had, which is
27389    /// the one write on a followed key that does not spend one. Out of the
27390    /// prefix is an erase and into it is a fresh reading, both measured.
27391    #[test]
27392    fn a_rename_inside_the_prefix_keeps_the_number_the_document_had() {
27393        let mut f = indexed();
27394        f.run(&[b"RENAME", b"p:1", b"p:2"]);
27395        assert_eq!(held(&f, b"ix"), (1, 1), "nothing was read again");
27396        assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
27397        assert_eq!(number(&f, b"ix", b"p:1"), None);
27398
27399        f.run(&[b"RENAME", b"p:2", b"q:1"]);
27400        assert_eq!(held(&f, b"ix"), (0, 1), "out of the prefix is an erase");
27401
27402        f.run(&[b"RENAME", b"q:1", b"p:3"]);
27403        assert_eq!(held(&f, b"ix"), (1, 2), "and into it is a reading");
27404        assert_eq!(number(&f, b"ix", b"p:3"), Some(2));
27405
27406        // `RENAMENX` goes the same way, and the one that answers zero changes
27407        // nothing.
27408        f.run(&[b"HSET", b"p:4", b"t", b"beta"]);
27409        assert_eq!(f.run(&[b"RENAMENX", b"p:3", b"p:4"]), ":0\r\n");
27410        assert_eq!(held(&f, b"ix"), (2, 3));
27411        f.run(&[b"RENAMENX", b"p:3", b"p:5"]);
27412        assert_eq!(number(&f, b"ix", b"p:5"), Some(2));
27413    }
27414
27415    /// A rename over a key that already had a document leaves one document and
27416    /// not two. A real server leaves both, and D-64 is that difference.
27417    #[test]
27418    fn a_rename_over_a_document_leaves_one_of_them() {
27419        let mut f = indexed();
27420        f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
27421        assert_eq!(held(&f, b"ix"), (2, 2));
27422        f.run(&[b"RENAME", b"p:1", b"p:2"]);
27423        assert_eq!(held(&f, b"ix"), (1, 2));
27424        assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
27425    }
27426
27427    /// A key that arrives under the prefix by being copied or restored is read
27428    /// as a new document, and one that is written over by something that is not
27429    /// a hash is erased without a word.
27430    #[test]
27431    fn a_key_that_arrives_under_the_prefix_is_read_and_one_overwritten_is_erased() {
27432        let mut f = indexed();
27433        f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
27434        f.run(&[b"COPY", b"q:1", b"p:2"]);
27435        assert_eq!(held(&f, b"ix"), (2, 2));
27436        assert_eq!(number(&f, b"ix", b"p:2"), Some(2));
27437
27438        // Out of the prefix, where the source keeps the document it had.
27439        f.run(&[b"COPY", b"p:1", b"q:2"]);
27440        assert_eq!(held(&f, b"ix"), (2, 2));
27441
27442        // Over a key that has one, which is a new reading and not a rename.
27443        f.run(&[b"COPY", b"q:1", b"p:1", b"REPLACE"]);
27444        assert_eq!(held(&f, b"ix"), (2, 3));
27445        assert_eq!(number(&f, b"ix", b"p:1"), Some(3));
27446
27447        // And a string landing on top of a document takes it away, spending no
27448        // number and counting no failure.
27449        f.run(&[b"SET", b"s:1", b"plain"]);
27450        f.run(&[b"COPY", b"s:1", b"p:1", b"REPLACE"]);
27451        assert_eq!(held(&f, b"ix"), (1, 3));
27452        let dump = f.run(&[b"DUMP", b"q:1"]);
27453        assert!(dump.starts_with('$'), "{dump}");
27454    }
27455
27456    /// The keyspace group reads a key back on database zero whatever database
27457    /// the command ran on, which is measured and is not what the hash commands
27458    /// do. A `COPY` into another database indexes nothing and takes away
27459    /// whatever the destination had, and a `RESTORE` anywhere else is invisible.
27460    #[test]
27461    fn the_keyspace_group_reads_database_zero_whatever_database_it_ran_on() {
27462        let mut f = indexed();
27463        f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
27464        assert_eq!(held(&f, b"ix"), (2, 2));
27465        // Into database one, so the indexes look for `p:2` on database zero,
27466        // find the one that is still there and read it again.
27467        f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
27468        assert_eq!(held(&f, b"ix"), (2, 3));
27469        // And with nothing under that name on database zero, the copy leaves
27470        // the index one document lighter than it found it.
27471        f.run(&[b"DEL", b"p:2"]);
27472        assert_eq!(held(&f, b"ix"), (1, 3));
27473        f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
27474        assert_eq!(held(&f, b"ix"), (1, 3), "the copy landed out of sight");
27475
27476        // A restore on another database is the same story.
27477        let dump = f.run(&[b"DUMP", b"p:1"]);
27478        assert!(dump.starts_with('$'), "{dump}");
27479        f.run(&[b"SELECT", b"1"]);
27480        f.run(&[b"HSET", b"q:1", b"t", b"gamma"]);
27481        f.run(&[b"RENAME", b"q:1", b"p:3"]);
27482        assert_eq!(held(&f, b"ix"), (1, 3), "and so is a rename");
27483    }
27484
27485    /// `MOVE` is not a change at all, because an index follows a key by name
27486    /// and a write on any database still reaches it.
27487    #[test]
27488    fn a_move_leaves_the_document_where_it_is() {
27489        let mut f = indexed();
27490        f.run(&[b"MOVE", b"p:1", b"1"]);
27491        assert_eq!(held(&f, b"ix"), (1, 1), "the key moved and nothing else");
27492        assert_eq!(number(&f, b"ix", b"p:1"), Some(1));
27493
27494        f.run(&[b"SELECT", b"1"]);
27495        f.run(&[b"HSET", b"p:1", b"t", b"beta"]);
27496        assert_eq!(held(&f, b"ix"), (1, 2), "and a write there still lands");
27497        f.run(&[b"DEL", b"p:1"]);
27498        assert_eq!(held(&f, b"ix"), (0, 2));
27499    }
27500
27501    /// A flush takes every index with it, whichever database it flushed.
27502    #[test]
27503    fn a_flush_drops_the_indexes() {
27504        for flush in [b"FLUSHALL".as_slice(), b"FLUSHDB"] {
27505            let mut f = indexed();
27506            f.run(&[flush]);
27507            assert!(f.server.search.lock().is_empty(), "{flush:?} kept an index");
27508            assert_eq!(f.run(&[b"FT._LIST"]), "*0\r\n");
27509        }
27510
27511        // Even on a database no index ever read, which is what a real server
27512        // does and is not what anyone would guess.
27513        let mut f = indexed();
27514        f.run(&[b"SELECT", b"9"]);
27515        f.run(&[b"FLUSHDB"]);
27516        assert!(f.server.search.lock().is_empty());
27517    }
27518
27519    /// An index whose schema has one tag field of each kind, plus a number so
27520    /// there is something for `FT.TAGVALS` to refuse.
27521    fn tagged() -> Fixture {
27522        let mut f = Fixture::new();
27523        f.run(&[
27524            b"FT.CREATE",
27525            b"tv",
27526            b"PREFIX",
27527            b"1",
27528            b"tv:",
27529            b"SCHEMA",
27530            b"g",
27531            b"AS",
27532            b"gg",
27533            b"TAG",
27534            b"h",
27535            b"TAG",
27536            b"SEPARATOR",
27537            b"|",
27538            b"CASESENSITIVE",
27539            b"n",
27540            b"NUMERIC",
27541        ]);
27542        f.run(&[
27543            b"HSET",
27544            b"tv:1",
27545            b"g",
27546            b"Red, BLUE ",
27547            b"h",
27548            b"Aa|bB",
27549            b"n",
27550            b"1",
27551        ]);
27552        f.run(&[b"HSET", b"tv:2", b"g", b"red", b"h", b"aa", b"n", b"2"]);
27553        f
27554    }
27555
27556    /// The values come back as they are stored, so an ordinary tag field
27557    /// answers them folded and trimmed and a `CASESENSITIVE` one answers what
27558    /// it was given. Byte order either way, which puts the capital first.
27559    #[test]
27560    fn tag_values_come_back_as_they_are_stored_and_sorted_by_their_bytes() {
27561        let mut f = tagged();
27562        assert_eq!(
27563            f.run(&[b"FT.TAGVALS", b"tv", b"gg"]),
27564            "*2\r\n$4\r\nblue\r\n$3\r\nred\r\n"
27565        );
27566        assert_eq!(
27567            f.run(&[b"FT.TAGVALS", b"tv", b"h"]),
27568            "*3\r\n$2\r\nAa\r\n$2\r\naa\r\n$2\r\nbB\r\n"
27569        );
27570    }
27571
27572    /// The name asked about is the attribute, so the identifier of a field
27573    /// declared `AS` is not a name this knows.
27574    #[test]
27575    fn tag_values_are_asked_for_by_the_attribute_and_not_the_identifier() {
27576        let mut f = tagged();
27577        for (name, want) in [
27578            (b"g".as_slice(), "-SEARCH_ATTR_BAD No such field\r\n"),
27579            (b"zz", "-SEARCH_ATTR_BAD No such field\r\n"),
27580            (b"n", "-SEARCH_ATTR_BAD Not a tag field\r\n"),
27581        ] {
27582            assert_eq!(f.run(&[b"FT.TAGVALS", b"tv", name]), want);
27583        }
27584        assert_eq!(
27585            f.run(&[b"FT.TAGVALS", b"nope", b"g"]),
27586            "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n"
27587        );
27588    }
27589
27590    /// Looking up the index counts as a use of it on the roads that refuse the
27591    /// field as well as on the one that answers, which is measured.
27592    #[test]
27593    fn asking_for_tag_values_counts_a_use_of_the_index() {
27594        let mut f = tagged();
27595        let uses = |f: &mut Fixture| {
27596            let reply = f.run(&[b"FT.INFO", b"tv"]);
27597            let at = reply.find("number_of_uses").expect("the field is reported");
27598            let value = reply[at..].split("\r\n").nth(1).unwrap();
27599            value.trim_start_matches(':').parse::<i64>().unwrap()
27600        };
27601        let before = uses(&mut f);
27602        f.run(&[b"FT.TAGVALS", b"tv", b"gg"]);
27603        f.run(&[b"FT.TAGVALS", b"tv", b"zz"]);
27604        // Three more than before: two tag lookups and the second `FT.INFO`.
27605        assert_eq!(uses(&mut f), before + 3);
27606    }
27607
27608    /// A tag field nothing was ever written to has no list at all, which
27609    /// answers the same empty set a list that has been emptied does.
27610    #[test]
27611    fn a_tag_field_with_nothing_in_it_answers_empty() {
27612        let mut f = Fixture::new();
27613        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"g", b"TAG"]);
27614        assert_eq!(f.run(&[b"FT.TAGVALS", b"e", b"g"]), "*0\r\n");
27615    }
27616
27617    /// A dictionary is module state and not a key, so nothing in the keyspace
27618    /// can see one.
27619    #[test]
27620    fn a_dictionary_is_not_a_key() {
27621        let mut f = Fixture::new();
27622        assert_eq!(f.run(&[b"FT.DICTADD", b"d", b"a", b"b"]), ":2\r\n");
27623        assert_eq!(f.run(&[b"TYPE", b"d"]), "+none\r\n");
27624        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
27625        assert_eq!(f.run(&[b"KEYS", b"d"]), "*0\r\n");
27626    }
27627
27628    /// The count is how many terms were new, an empty term is not a term, and
27629    /// the dump is sorted by bytes rather than folded.
27630    #[test]
27631    fn a_dictionary_counts_the_terms_it_had_not_seen() {
27632        let mut f = Fixture::new();
27633        assert_eq!(
27634            f.run(&[b"FT.DICTADD", b"d", b"zeta", b"alpha", b"Beta", b"alpha"]),
27635            ":3\r\n"
27636        );
27637        assert_eq!(f.run(&[b"FT.DICTADD", b"d", b"alpha"]), ":0\r\n");
27638        assert_eq!(f.run(&[b"FT.DICTADD", b"d", b""]), ":0\r\n");
27639        assert_eq!(
27640            f.run(&[b"FT.DICTDUMP", b"d"]),
27641            "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nzeta\r\n"
27642        );
27643        assert_eq!(f.run(&[b"FT.DICTDEL", b"d", b"alpha", b"nope"]), ":1\r\n");
27644    }
27645
27646    /// A name nobody ever added to is not an error on either of the two
27647    /// commands that will take one, which is the only place in the group where
27648    /// a missing name is forgiven.
27649    #[test]
27650    fn a_dictionary_nobody_made_dumps_empty_rather_than_failing() {
27651        let mut f = Fixture::new();
27652        assert_eq!(f.run(&[b"FT.DICTDUMP", b"nope"]), "*0\r\n");
27653        assert_eq!(f.run(&[b"FT.DICTDEL", b"nope", b"a"]), ":0\r\n");
27654    }
27655
27656    /// The dictionaries go when the keyspace does, the same way the indexes do.
27657    #[test]
27658    fn a_flush_drops_the_dictionaries() {
27659        for flush in [b"FLUSHALL".as_slice(), b"FLUSHDB"] {
27660            let mut f = Fixture::new();
27661            f.run(&[b"FT.DICTADD", b"d", b"a"]);
27662            f.run(&[flush]);
27663            assert_eq!(f.run(&[b"FT.DICTDUMP", b"d"]), "*0\r\n", "{flush:?}");
27664        }
27665    }
27666
27667    // -------------------------------------------------------------- profile
27668
27669    /// A fixture holding one index over three documents, two of which hold the
27670    /// first word and two the second.
27671    fn profiling() -> Fixture {
27672        let mut f = Fixture::new();
27673        f.run(&[
27674            b"FT.CREATE",
27675            b"ix",
27676            b"PREFIX",
27677            b"1",
27678            b"p:",
27679            b"SCHEMA",
27680            b"t",
27681            b"TEXT",
27682            b"n",
27683            b"NUMERIC",
27684        ]);
27685        f.run(&[b"HSET", b"p:1", b"t", b"alpha", b"n", b"1"]);
27686        f.run(&[b"HSET", b"p:2", b"t", b"alpha beta", b"n", b"2"]);
27687        f.run(&[b"HSET", b"p:3", b"t", b"beta", b"n", b"3"]);
27688        f
27689    }
27690
27691    /// The reply with every time taken out of it, since no two runs agree on
27692    /// those and everything else about a profile is exact.
27693    fn timeless(reply: &str) -> String {
27694        const KEYS: &[&str] = &[
27695            "+Total profile time",
27696            "+Parsing time",
27697            "+Workers queue time",
27698            "+Pipeline creation time",
27699            "+Time",
27700        ];
27701        let mut out = String::new();
27702        let mut parts = reply.split("\r\n").peekable();
27703        while let Some(part) = parts.next() {
27704            out.push_str(part);
27705            out.push_str("\r\n");
27706            if !KEYS.contains(&part) {
27707                continue;
27708            }
27709            // A double is one line on RESP3 and a bulk header and its digits on
27710            // RESP2, and both of them stand for the same one value.
27711            match parts.next() {
27712                Some(head) if head.starts_with('$') => {
27713                    parts.next();
27714                }
27715                _ => {}
27716            }
27717            out.push_str("<t>\r\n");
27718        }
27719        // The split leaves an empty piece past the last line ending.
27720        out.truncate(out.len() - 2);
27721        out
27722    }
27723
27724    /// The whole envelope on both protocols, which is a two element array on
27725    /// one and a two key map on the other.
27726    #[test]
27727    fn a_profile_wraps_the_reply_it_would_have_answered_anyway() {
27728        let mut f = profiling();
27729        assert_eq!(
27730            timeless(&f.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY", b"alpha"])),
27731            "*2\r\n\
27732             *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\
27733             $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\
27734             *4\r\n+Shards\r\n*1\r\n*14\r\n\
27735             +Total profile time\r\n<t>\r\n+Parsing time\r\n<t>\r\n\
27736             +Workers queue time\r\n<t>\r\n+Pipeline creation time\r\n<t>\r\n\
27737             +Warning\r\n*1\r\n+None\r\n\
27738             +Iterators profile\r\n*10\r\n+Type\r\n+TEXT\r\n+Term\r\n$5\r\nalpha\r\n\
27739             +Time\r\n<t>\r\n+Number of reading operations\r\n:2\r\n\
27740             +Estimated number of matches\r\n:2\r\n\
27741             +Result processors profile\r\n*4\r\n\
27742             *6\r\n+Type\r\n+Index\r\n+Time\r\n<t>\r\n+Results processed\r\n:2\r\n\
27743             *6\r\n+Type\r\n+Scorer\r\n+Time\r\n<t>\r\n+Results processed\r\n:2\r\n\
27744             *6\r\n+Type\r\n+Sorter\r\n+Time\r\n<t>\r\n+Results processed\r\n:2\r\n\
27745             *6\r\n+Type\r\n+Loader\r\n+Time\r\n<t>\r\n+Results processed\r\n:2\r\n\
27746             +Coordinator\r\n*0\r\n"
27747        );
27748        let mut g = profiling();
27749        g.run(&[b"HELLO", b"3"]);
27750        let three = timeless(&g.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY", b"alpha"]));
27751        assert!(three.starts_with("%2\r\n+Results\r\n"), "{three}");
27752        assert!(
27753            three.contains("+Profile\r\n%2\r\n+Shards\r\n*1\r\n%7\r\n"),
27754            "{three}"
27755        );
27756        assert!(three.ends_with("+Coordinator\r\n%0\r\n"), "{three}");
27757        assert!(
27758            three.contains(
27759                "+Iterators profile\r\n%5\r\n+Type\r\n+TEXT\r\n+Term\r\n$5\r\nalpha\r\n\
27760                 +Time\r\n<t>\r\n+Number of reading operations\r\n:2\r\n\
27761                 +Estimated number of matches\r\n:2\r\n"
27762            ),
27763            "{three}"
27764        );
27765    }
27766
27767    /// Every kind of step names itself, and the three that hold other steps say
27768    /// so in the singular or the plural depending on how many they hold.
27769    #[test]
27770    fn each_kind_of_step_writes_the_keys_that_belong_to_it() {
27771        let mut f = profiling();
27772        let tree = |f: &mut Fixture, query: &[u8]| {
27773            let reply = timeless(&f.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY", query]));
27774            let at = reply.find("+Iterators profile").expect("a tree");
27775            let end = reply.find("+Result processors").expect("a list of steps");
27776            reply[at..end].to_string()
27777        };
27778        assert_eq!(
27779            tree(&mut f, b"alpha beta"),
27780            "+Iterators profile\r\n*8\r\n+Type\r\n+INTERSECT\r\n+Time\r\n<t>\r\n\
27781             +Number of reading operations\r\n:1\r\n+Child iterators\r\n*2\r\n\
27782             *10\r\n+Type\r\n+TEXT\r\n+Term\r\n$5\r\nalpha\r\n+Time\r\n<t>\r\n\
27783             +Number of reading operations\r\n:2\r\n+Estimated number of matches\r\n:2\r\n\
27784             *10\r\n+Type\r\n+TEXT\r\n+Term\r\n$4\r\nbeta\r\n+Time\r\n<t>\r\n\
27785             +Number of reading operations\r\n:1\r\n+Estimated number of matches\r\n:2\r\n"
27786        );
27787        assert!(tree(&mut f, b"alpha|beta").starts_with(
27788            "+Iterators profile\r\n*10\r\n+Type\r\n+UNION\r\n+Query type\r\n+UNION\r\n\
27789             +Time\r\n<t>\r\n+Number of reading operations\r\n:3\r\n+Child iterators\r\n*2\r\n"
27790        ));
27791        // One thing under it, named in the singular, which is a different key
27792        // and not a list holding one.
27793        assert!(tree(&mut f, b"-alpha").starts_with(
27794            "+Iterators profile\r\n*8\r\n+Type\r\n+NOT\r\n+Time\r\n<t>\r\n\
27795             +Number of reading operations\r\n:1\r\n+Child iterator\r\n*10\r\n"
27796        ));
27797        assert!(tree(&mut f, b"~alpha").starts_with(
27798            "+Iterators profile\r\n*8\r\n+Type\r\n+OPTIONAL\r\n+Time\r\n<t>\r\n\
27799             +Number of reading operations\r\n:3\r\n+Child iterator\r\n*10\r\n"
27800        ));
27801        // No guess at how many, which is the one leaf that leaves it off.
27802        assert_eq!(
27803            tree(&mut f, b"*"),
27804            "+Iterators profile\r\n*6\r\n+Type\r\n+WILDCARD\r\n+Time\r\n<t>\r\n\
27805             +Number of reading operations\r\n:3\r\n"
27806        );
27807        assert!(tree(&mut f, b"@n:[1 2]").starts_with(
27808            "+Iterators profile\r\n*10\r\n+Type\r\n+NUMERIC\r\n+Term\r\n\
27809             $19\r\n1.000000 - 2.000000\r\n"
27810        ));
27811    }
27812
27813    /// A union an expansion made folds into a count of its branches and a union
27814    /// a client wrote with a bar does not.
27815    #[test]
27816    fn limited_folds_the_branches_an_expansion_made_and_leaves_a_bar_alone() {
27817        let mut f = profiling();
27818        f.run(&[b"HSET", b"p:4", b"t", b"alps"]);
27819        let tree = |f: &mut Fixture, words: &[&[u8]]| {
27820            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"ix", b"SEARCH"];
27821            argv.extend_from_slice(words);
27822            let reply = timeless(&f.run(&argv));
27823            let at = reply.find("+Iterators profile").expect("a tree");
27824            let end = reply.find("+Result processors").expect("a list of steps");
27825            reply[at..end].to_string()
27826        };
27827        assert_eq!(
27828            tree(&mut f, &[b"LIMITED", b"QUERY", b"al*"]),
27829            "+Iterators profile\r\n*10\r\n+Type\r\n+UNION\r\n\
27830             +Query type\r\n$11\r\nPREFIX - al\r\n+Time\r\n<t>\r\n\
27831             +Number of reading operations\r\n:3\r\n+Child iterators\r\n\
27832             +The number of iterators in the union is 2\r\n"
27833        );
27834        assert!(tree(&mut f, &[b"QUERY", b"al*"]).contains("+Child iterators\r\n*2\r\n"));
27835        assert!(
27836            tree(&mut f, &[b"LIMITED", b"QUERY", b"alpha|beta"])
27837                .contains("+Child iterators\r\n*2\r\n")
27838        );
27839        // A union that says nothing but its own name says it as a status, and
27840        // one that says what it stood for says that as a string. Measured, and
27841        // it is the one place in this reply where the two are told apart.
27842        assert!(tree(&mut f, &[b"QUERY", b"alpha|beta"]).contains("+Query type\r\n+UNION\r\n"));
27843        assert!(
27844            tree(&mut f, &[b"QUERY", b"al*"]).contains("+Query type\r\n$11\r\nPREFIX - al\r\n")
27845        );
27846    }
27847
27848    /// Which steps a search runs the rows through, which turns on the window,
27849    /// on whether anything asked for the fields and on what the order is.
27850    #[test]
27851    fn the_steps_a_search_runs_depend_on_what_was_asked_for() {
27852        let mut f = profiling();
27853        let steps = |f: &mut Fixture, words: &[&[u8]]| {
27854            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY", b"alpha"];
27855            argv.extend_from_slice(words);
27856            let reply = timeless(&f.run(&argv));
27857            let at = reply.find("+Result processors").expect("a list of steps");
27858            let end = reply.find("+Coordinator").expect("an end");
27859            let mut out = Vec::new();
27860            let mut parts = reply[at..end].split("\r\n").peekable();
27861            while let Some(part) = parts.next() {
27862                if part == "+Type" {
27863                    out.push(parts.next().unwrap_or_default().to_string());
27864                }
27865            }
27866            out
27867        };
27868        assert_eq!(
27869            steps(&mut f, &[]),
27870            ["+Index", "+Scorer", "+Sorter", "+Loader"]
27871        );
27872        assert_eq!(
27873            steps(&mut f, &[b"NOCONTENT"]),
27874            ["+Index", "+Scorer", "+Sorter"]
27875        );
27876        // A window of nothing is a client asking for the total and nothing
27877        // else, so nothing is scored and nothing is sorted.
27878        assert_eq!(
27879            steps(&mut f, &[b"LIMIT", b"0", b"0"]),
27880            ["+Index", "+Counter"]
27881        );
27882        // A sort by a field does not need a score, and asking for the scores
27883        // puts the step back.
27884        assert_eq!(
27885            steps(&mut f, &[b"SORTBY", b"n"]),
27886            ["+Index", "+Sorter", "+Loader"]
27887        );
27888        assert_eq!(
27889            steps(&mut f, &[b"SORTBY", b"n", b"WITHSCORES"]),
27890            ["+Index", "+Scorer", "+Sorter", "+Loader"]
27891        );
27892        assert_eq!(
27893            steps(&mut f, &[b"HIGHLIGHT"]),
27894            ["+Index", "+Scorer", "+Sorter", "+Loader", "+Highlighter"]
27895        );
27896        assert_eq!(
27897            steps(&mut f, &[b"SUMMARIZE", b"NOCONTENT"]),
27898            ["+Index", "+Scorer", "+Sorter"]
27899        );
27900    }
27901
27902    /// A pipeline names each of its steps after the expression it runs, which
27903    /// is what a real server prints beside them.
27904    #[test]
27905    fn a_pipeline_names_every_step_after_what_it_runs() {
27906        let mut f = profiling();
27907        let steps = |f: &mut Fixture, words: &[&[u8]]| {
27908            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"ix", b"AGGREGATE", b"QUERY", b"*"];
27909            argv.extend_from_slice(words);
27910            let reply = timeless(&f.run(&argv));
27911            let at = reply.find("+Result processors").expect("a list of steps");
27912            let end = reply.find("+Coordinator").expect("an end");
27913            let mut out = Vec::new();
27914            let mut parts = reply[at..end].split("\r\n").peekable();
27915            while let Some(part) = parts.next() {
27916                if part == "+Type" {
27917                    out.push(parts.next().unwrap_or_default().to_string());
27918                }
27919            }
27920            out
27921        };
27922        assert_eq!(steps(&mut f, &[]), ["+Index"]);
27923        assert_eq!(
27924            steps(&mut f, &[b"APPLY", b"1", b"AS", b"one"]),
27925            ["+Index", "+Projector - Literal 1"]
27926        );
27927        assert_eq!(
27928            steps(
27929                &mut f,
27930                &[b"LOAD", b"1", b"@n", b"APPLY", b"@n * 2", b"AS", b"d"]
27931            ),
27932            ["+Index", "+Loader", "+Projector - Operator *"]
27933        );
27934        assert_eq!(
27935            steps(&mut f, &[b"LOAD", b"1", b"@n", b"FILTER", b"@n > 1"]),
27936            ["+Index", "+Loader", "+Filter - Predicate >"]
27937        );
27938        assert_eq!(
27939            steps(
27940                &mut f,
27941                &[b"GROUPBY", b"1", b"@n", b"REDUCE", b"COUNT", b"0"]
27942            ),
27943            ["+Index", "+Loader", "+Grouper"]
27944        );
27945        assert_eq!(
27946            steps(&mut f, &[b"SORTBY", b"1", b"@n"]),
27947            ["+Index", "+Loader", "+Sorter"]
27948        );
27949        assert_eq!(
27950            steps(&mut f, &[b"LIMIT", b"0", b"2"]),
27951            ["+Index", "+Pager/Limiter"]
27952        );
27953        // Asking for the score by name is a step of its own, and it goes in
27954        // front of the read rather than after it.
27955        assert_eq!(
27956            steps(
27957                &mut f,
27958                &[
27959                    b"ADDSCORES",
27960                    b"LOAD",
27961                    b"1",
27962                    b"@n",
27963                    b"APPLY",
27964                    b"@__score",
27965                    b"AS",
27966                    b"s"
27967                ]
27968            ),
27969            [
27970                "+Index",
27971                "+Scorer",
27972                "+Loader",
27973                "+Projector - Property __score"
27974            ]
27975        );
27976    }
27977
27978    /// A field the schema marked sortable is held beside the document number,
27979    /// so a pipeline that only names those never opens a key and never reports
27980    /// a read.
27981    ///
27982    /// Measured: on a schema of `n NUMERIC SORTABLE g TAG`, `LOAD 1 @n` has no
27983    /// `Loader` step and `LOAD 1 @g` has one. So does `LOAD *`, because what a
27984    /// key turns out to hold is not knowable without opening it.
27985    #[test]
27986    fn a_sortable_field_is_read_without_the_key_being_opened() {
27987        let mut f = Fixture::new();
27988        f.run(&[
27989            b"FT.CREATE",
27990            b"sx",
27991            b"PREFIX",
27992            b"1",
27993            b"s:",
27994            b"SCHEMA",
27995            b"n",
27996            b"NUMERIC",
27997            b"SORTABLE",
27998            b"g",
27999            b"TAG",
28000        ]);
28001        f.run(&[b"HSET", b"s:1", b"n", b"1", b"g", b"one"]);
28002        f.run(&[b"HSET", b"s:2", b"n", b"2", b"g", b"two"]);
28003        let loads = |f: &mut Fixture, words: &[&[u8]]| {
28004            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"sx", b"AGGREGATE", b"QUERY", b"*"];
28005            argv.extend_from_slice(words);
28006            f.run(&argv).contains("+Loader")
28007        };
28008        assert!(!loads(&mut f, &[b"LOAD", b"1", b"@n"]));
28009        assert!(!loads(&mut f, &[b"SORTBY", b"1", b"@n"]));
28010        assert!(!loads(&mut f, &[b"APPLY", b"@n * 2", b"AS", b"d"]));
28011        assert!(loads(&mut f, &[b"LOAD", b"1", b"@g"]));
28012        assert!(loads(&mut f, &[b"LOAD", b"2", b"@n", b"@g"]));
28013        assert!(loads(
28014            &mut f,
28015            &[b"GROUPBY", b"1", b"@g", b"REDUCE", b"COUNT", b"0"]
28016        ));
28017        assert!(loads(&mut f, &[b"LOAD", b"*"]));
28018    }
28019
28020    /// The four ways the words can be wrong, none of which reaches the search
28021    /// underneath.
28022    #[test]
28023    fn a_profile_checks_its_own_words_before_it_runs_anything() {
28024        let mut f = profiling();
28025        assert_eq!(
28026            f.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"QUERY"]),
28027            "-ERR wrong number of arguments for 'FT.PROFILE' command\r\n"
28028        );
28029        assert_eq!(
28030            f.run(&[b"FT.PROFILE", b"ix", b"BOGUS", b"QUERY", b"alpha"]),
28031            "-No `SEARCH`, `AGGREGATE`, or `HYBRID` provided\r\n"
28032        );
28033        // The word goes between the two and nowhere else, so one written in
28034        // front of them is not the word at all.
28035        assert_eq!(
28036            f.run(&[
28037                b"FT.PROFILE",
28038                b"ix",
28039                b"LIMITED",
28040                b"SEARCH",
28041                b"QUERY",
28042                b"alpha"
28043            ]),
28044            "-No `SEARCH`, `AGGREGATE`, or `HYBRID` provided\r\n"
28045        );
28046        assert_eq!(
28047            f.run(&[b"FT.PROFILE", b"ix", b"SEARCH", b"BOGUS", b"alpha"]),
28048            "-The QUERY keyword is expected\r\n"
28049        );
28050        assert_eq!(
28051            f.run(&[
28052                b"FT.PROFILE",
28053                b"ix",
28054                b"AGGREGATE",
28055                b"QUERY",
28056                b"alpha",
28057                b"WITHCURSOR"
28058            ]),
28059            "-FT.PROFILE does not support cursor\r\n"
28060        );
28061        // And what the search itself complains about comes back on its own,
28062        // without an envelope around it saying the command worked.
28063        assert_eq!(
28064            f.run(&[b"FT.PROFILE", b"nope", b"SEARCH", b"QUERY", b"alpha"]),
28065            "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n"
28066        );
28067        assert_eq!(
28068            f.run(&[
28069                b"FT.PROFILE",
28070                b"ix",
28071                b"SEARCH",
28072                b"QUERY",
28073                b"alpha",
28074                b"extra"
28075            ]),
28076            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `extra` at position 1 for <main>\r\n"
28077        );
28078    }
28079
28080    /// Every word of the command's own is read without regard to case.
28081    #[test]
28082    fn the_words_of_a_profile_are_read_the_way_every_other_word_is() {
28083        let mut f = profiling();
28084        let one = f.run(&[
28085            b"FT.PROFILE",
28086            b"ix",
28087            b"search",
28088            b"limited",
28089            b"query",
28090            b"alpha",
28091        ]);
28092        let two = f.run(&[
28093            b"FT.PROFILE",
28094            b"ix",
28095            b"SEARCH",
28096            b"LIMITED",
28097            b"QUERY",
28098            b"alpha",
28099        ]);
28100        assert_eq!(timeless(&one), timeless(&two));
28101    }
28102
28103    // -------------------------------------------------------------- dropping
28104
28105    /// The two spellings take opposite defaults, which is measured and is the
28106    /// only difference between them that a client can see.
28107    #[test]
28108    fn the_two_ways_of_dropping_an_index_disagree_about_the_documents() {
28109        let mut f = profiling();
28110        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix"]), "+OK\r\n");
28111        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":3\r\n");
28112
28113        let mut f = profiling();
28114        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix", b"DD"]), "+OK\r\n");
28115        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":0\r\n");
28116
28117        let mut f = profiling();
28118        assert_eq!(f.run(&[b"FT.DROP", b"ix"]), "+OK\r\n");
28119        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":0\r\n");
28120
28121        let mut f = profiling();
28122        assert_eq!(f.run(&[b"FT.DROP", b"ix", b"KEEPDOCS"]), "+OK\r\n");
28123        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":3\r\n");
28124    }
28125
28126    /// Each spelling takes its own word and refuses the other one's, which
28127    /// reads as an oversight and is what a real server answers.
28128    #[test]
28129    fn neither_way_of_dropping_an_index_takes_the_other_ones_word() {
28130        let mut f = profiling();
28131        let line = "-SEARCH_ARG_UNRECOGNIZED Unknown argument\r\n";
28132        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix", b"KEEPDOCS"]), line);
28133        assert_eq!(f.run(&[b"FT.DROP", b"ix", b"DD"]), line);
28134        // Refused rather than half done, so the index is still there.
28135        assert_eq!(f.run(&[b"FT._LIST"]), "*1\r\n+ix\r\n");
28136    }
28137
28138    /// Only what the index read is deleted, which is not the same as
28139    /// everything under its prefix.
28140    #[test]
28141    fn dropping_the_documents_leaves_a_key_the_index_never_read() {
28142        let mut f = profiling();
28143        f.run(&[b"SET", b"p:4", b"alpha"]);
28144        f.run(&[b"HSET", b"q:1", b"t", b"alpha"]);
28145        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix", b"DD"]), "+OK\r\n");
28146        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":0\r\n");
28147        assert_eq!(f.run(&[b"EXISTS", b"p:4", b"q:1"]), ":2\r\n");
28148    }
28149
28150    /// An index still standing over the same keys hears about them going,
28151    /// rather than answering later with keys that are not there.
28152    #[test]
28153    fn another_index_over_the_same_keys_loses_the_documents_too() {
28154        let mut f = profiling();
28155        f.run(&[
28156            b"FT.CREATE",
28157            b"other",
28158            b"PREFIX",
28159            b"1",
28160            b"p:",
28161            b"SCHEMA",
28162            b"t",
28163            b"TEXT",
28164        ]);
28165        assert_eq!(f.run(&[b"FT.DROPINDEX", b"ix", b"DD"]), "+OK\r\n");
28166        assert_eq!(
28167            f.run(&[b"FT.SEARCH", b"other", b"alpha", b"NOCONTENT"]),
28168            "*1\r\n:0\r\n"
28169        );
28170    }
28171
28172    /// A drop that found nothing to drop deletes nothing either, which is the
28173    /// one case where the shortcut spelling answers `OK` without a sweep.
28174    #[test]
28175    fn a_drop_of_an_index_that_is_not_there_touches_no_keys() {
28176        let mut f = profiling();
28177        assert_eq!(f.run(&[b"FT._DROPINDEXIFX", b"nope", b"DD"]), "+OK\r\n");
28178        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":3\r\n");
28179        assert_eq!(f.run(&[b"FT._DROPIFX", b"nope"]), "+OK\r\n");
28180        assert_eq!(f.run(&[b"EXISTS", b"p:1", b"p:2", b"p:3"]), ":3\r\n");
28181    }
28182
28183    // --------------------------------------------------------------- config
28184
28185    /// The two shapes a dump comes back in, which are the one mix of simple
28186    /// strings and bulk strings the group sends.
28187    #[test]
28188    fn a_setting_reads_back_as_a_pair_on_one_protocol_and_a_map_on_the_other() {
28189        let mut f = Fixture::new();
28190        assert_eq!(
28191            f.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
28192            "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n500\r\n"
28193        );
28194        assert_eq!(
28195            f.run(&[b"FT.CONFIG", b"GET", b"EXTLOAD"]),
28196            "*1\r\n*2\r\n+EXTLOAD\r\n$-1\r\n"
28197        );
28198        let mut g = Fixture::new();
28199        g.run(&[b"HELLO", b"3"]);
28200        assert_eq!(
28201            g.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
28202            "%1\r\n+TIMEOUT\r\n$3\r\n500\r\n"
28203        );
28204        assert_eq!(
28205            g.run(&[b"FT.CONFIG", b"GET", b"EXTLOAD"]),
28206            "%1\r\n+EXTLOAD\r\n_\r\n"
28207        );
28208    }
28209
28210    /// The help text rides along in the middle of the same row, flat on RESP2
28211    /// and as a map of its own on RESP3.
28212    #[test]
28213    fn a_help_row_carries_the_description_and_the_value_together() {
28214        let mut f = Fixture::new();
28215        assert_eq!(
28216            f.run(&[b"FT.CONFIG", b"HELP", b"TIMEOUT"]),
28217            "*1\r\n*5\r\n+TIMEOUT\r\n+Description\r\n+Query (search) timeout\r\n\
28218             +Value\r\n$3\r\n500\r\n"
28219        );
28220        let mut g = Fixture::new();
28221        g.run(&[b"HELLO", b"3"]);
28222        assert_eq!(
28223            g.run(&[b"FT.CONFIG", b"HELP", b"TIMEOUT"]),
28224            "%1\r\n+TIMEOUT\r\n%2\r\n+Description\r\n+Query (search) timeout\r\n\
28225             +Value\r\n$3\r\n500\r\n"
28226        );
28227    }
28228
28229    /// A name is matched whole, ignoring case, and the single word star is the
28230    /// only thing that means all of them.
28231    #[test]
28232    fn only_a_bare_star_asks_for_every_setting_and_nothing_else_globs() {
28233        let mut f = Fixture::new();
28234        assert_eq!(
28235            f.run(&[b"FT.CONFIG", b"GET", b"timeout"]),
28236            "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n500\r\n"
28237        );
28238        for name in [
28239            b"TIMEOUT*".as_slice(),
28240            b"?IMEOUT",
28241            b"*TIMEOUT*",
28242            b"TIME",
28243            b"NOSUCH",
28244            b"",
28245        ] {
28246            assert_eq!(f.run(&[b"FT.CONFIG", b"GET", name]), "*0\r\n", "{name:?}");
28247        }
28248        assert!(f.run(&[b"FT.CONFIG", b"GET", b"*"]).starts_with("*69\r\n"));
28249        assert!(f.run(&[b"FT.CONFIG", b"HELP", b"*"]).starts_with("*69\r\n"));
28250    }
28251
28252    /// Words after the name are stepped over rather than refused, on both of
28253    /// the two reads.
28254    #[test]
28255    fn a_read_ignores_whatever_follows_the_name() {
28256        let mut f = Fixture::new();
28257        assert_eq!(
28258            f.run(&[b"FT.CONFIG", b"GET", b"timeout", b"extra", b"more"]),
28259            "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n500\r\n"
28260        );
28261        assert_eq!(
28262            f.run(&[b"FT.CONFIG", b"HELP", b"timeout", b"extra"]),
28263            "*1\r\n*5\r\n+TIMEOUT\r\n+Description\r\n+Query (search) timeout\r\n\
28264             +Value\r\n$3\r\n500\r\n"
28265        );
28266    }
28267
28268    /// The container reports its own name and the subcommand it was given in
28269    /// the two lines the dispatcher writes.
28270    #[test]
28271    fn a_missing_subcommand_and_a_missing_name_are_told_apart() {
28272        let mut f = Fixture::new();
28273        assert_eq!(
28274            f.run(&[b"FT.CONFIG"]),
28275            "-ERR wrong number of arguments for 'FT.CONFIG' command\r\n"
28276        );
28277        for sub in [b"GET".as_slice(), b"SET", b"HELP"] {
28278            let want = format!(
28279                "-ERR wrong number of arguments for 'FT.CONFIG|{}' command\r\n",
28280                String::from_utf8_lossy(sub)
28281            );
28282            assert_eq!(f.run(&[b"FT.CONFIG", sub]), want);
28283        }
28284        assert_eq!(
28285            f.run(&[b"ft.config", b"get"]),
28286            "-ERR wrong number of arguments for 'FT.CONFIG|GET' command\r\n"
28287        );
28288        assert_eq!(
28289            f.run(&[b"FT.CONFIG", b"bogus"]),
28290            "-ERR unknown subcommand 'bogus'. Try FT.CONFIG HELP.\r\n"
28291        );
28292    }
28293
28294    /// The name, then whether it can move, then the value, then the count of
28295    /// words, and each of the first three answers before the next is looked at.
28296    #[test]
28297    fn a_write_checks_the_name_then_the_setting_then_the_value() {
28298        let mut f = Fixture::new();
28299        for tail in [vec![b"1".as_slice()], vec![], vec![b"1", b"2", b"3"]] {
28300            let mut cmd: Vec<&[u8]> = vec![b"FT.CONFIG", b"SET", b"NOSUCH"];
28301            cmd.extend(tail);
28302            assert_eq!(f.run(&cmd), "-SEARCH_OPTION_INVALID Invalid option\r\n");
28303        }
28304        for tail in [vec![b"1000".as_slice()], vec![], vec![b"x", b"y"]] {
28305            let mut cmd: Vec<&[u8]> = vec![b"FT.CONFIG", b"SET", b"MAXDOCTABLESIZE"];
28306            cmd.extend(tail);
28307            assert_eq!(
28308                f.run(&cmd),
28309                "-SEARCH_OPTION_BAD Not modifiable at runtime\r\n"
28310            );
28311        }
28312        assert_eq!(
28313            f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", b"x", b"y", b"z"]),
28314            "-SEARCH_PARSE_ARGS Could not convert argument to expected type\r\n"
28315        );
28316    }
28317
28318    /// Too many words is a status and not an error, and the value has already
28319    /// been written by the time it goes out.
28320    #[test]
28321    fn an_excess_of_words_is_noticed_after_the_value_is_kept() {
28322        let mut f = Fixture::new();
28323        assert_eq!(
28324            f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", b"500"]),
28325            "+OK\r\n"
28326        );
28327        assert_eq!(
28328            f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", b"600", b"junk"]),
28329            "+EXCESSARGS\r\n"
28330        );
28331        assert_eq!(
28332            f.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
28333            "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n600\r\n"
28334        );
28335    }
28336
28337    /// Strictly first and loosely second, so a hexadecimal and a leading zero
28338    /// and an exponent all land and a fraction does not.
28339    #[test]
28340    fn a_number_is_read_the_strict_way_and_then_the_loose_one() {
28341        let mut f = Fixture::new();
28342        for (given, want) in [
28343            (b"0x10".as_slice(), "16"),
28344            (b"0X1f", "31"),
28345            (b"+0x10", "16"),
28346            (b"+5", "5"),
28347            (b"010", "10"),
28348            (b"08", "8"),
28349            (b"0777", "777"),
28350            (b"1e3", "1000"),
28351            (b"0.0", "0"),
28352            (b"-0.0", "0"),
28353        ] {
28354            assert_eq!(
28355                f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", given]),
28356                "+OK\r\n",
28357                "{given:?}"
28358            );
28359            let want = format!("*1\r\n*2\r\n+TIMEOUT\r\n${}\r\n{want}\r\n", want.len());
28360            assert_eq!(
28361                f.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
28362                want,
28363                "{given:?}"
28364            );
28365        }
28366        for given in [
28367            b" 5".as_slice(),
28368            b"5 ",
28369            b"1.5",
28370            b"1e-3",
28371            b"x",
28372            b"",
28373            b"0b11",
28374            b"0xg",
28375            b"nan",
28376            b"inf",
28377            b"1e100",
28378            b"99999999999999999999",
28379        ] {
28380            assert_eq!(
28381                f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", given]),
28382                "-SEARCH_PARSE_ARGS Could not convert argument to expected type\r\n",
28383                "{given:?}"
28384            );
28385        }
28386    }
28387
28388    /// Which of the two readers found a negative decides what it is told, and
28389    /// on a setting with no range at all neither of them is refused.
28390    #[test]
28391    fn a_negative_is_answered_by_whichever_reader_found_it() {
28392        let mut f = Fixture::new();
28393        for given in [b"-1".as_slice(), b"-16"] {
28394            assert_eq!(
28395                f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", given]),
28396                "-SEARCH_PARSE_ARGS Value is outside acceptable bounds\r\n",
28397                "{given:?}"
28398            );
28399        }
28400        for given in [b"-0x10".as_slice(), b"-1e3", b"-010", b"-2.0"] {
28401            assert_eq!(
28402                f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", given]),
28403                "-SEARCH_PARSE_ARGS Could not convert argument to expected type\r\n",
28404                "{given:?}"
28405            );
28406        }
28407        let unlimited = "*1\r\n*2\r\n+MAXSEARCHRESULTS\r\n$9\r\nunlimited\r\n";
28408        for given in [b"-1".as_slice(), b"-0x10", b"-1e3", b"-010"] {
28409            assert_eq!(
28410                f.run(&[b"FT.CONFIG", b"SET", b"MAXSEARCHRESULTS", given]),
28411                "+OK\r\n",
28412                "{given:?}"
28413            );
28414            assert_eq!(
28415                f.run(&[b"FT.CONFIG", b"GET", b"MAXSEARCHRESULTS"]),
28416                unlimited,
28417                "{given:?}"
28418            );
28419        }
28420    }
28421
28422    /// The two settings with no range truncate into a signed thirty two bit
28423    /// slot and say so once the number has gone under.
28424    #[test]
28425    fn a_wide_setting_wraps_into_its_slot_before_it_is_read_back() {
28426        let mut f = Fixture::new();
28427        for (given, want) in [
28428            (b"2147483647".as_slice(), "2147483647"),
28429            (b"2147483648", "unlimited"),
28430            (b"4294967295", "unlimited"),
28431            (b"9223372036854775806", "unlimited"),
28432            (b"0", "0"),
28433        ] {
28434            assert_eq!(
28435                f.run(&[b"FT.CONFIG", b"SET", b"MAXSEARCHRESULTS", given]),
28436                "+OK\r\n",
28437                "{given:?}"
28438            );
28439            let want = format!(
28440                "*1\r\n*2\r\n+MAXSEARCHRESULTS\r\n${}\r\n{want}\r\n",
28441                want.len()
28442            );
28443            assert_eq!(
28444                f.run(&[b"FT.CONFIG", b"GET", b"MAXSEARCHRESULTS"]),
28445                want,
28446                "{given:?}"
28447            );
28448        }
28449    }
28450
28451    /// A number past what a setting will take says which way it went, and the
28452    /// ones with a softer roof of their own say what that roof is about.
28453    #[test]
28454    fn a_number_out_of_range_names_the_limit_it_crossed() {
28455        let mut f = Fixture::new();
28456        let bounds = "-SEARCH_PARSE_ARGS Value is outside acceptable bounds\r\n";
28457        for (name, given) in [
28458            (b"MINPREFIX".as_slice(), b"0".as_slice()),
28459            (b"MAX_AGGREGATE_GROUPS", b"0"),
28460            (b"BM25STD_TANH_FACTOR", b"0"),
28461            (b"DEFAULT_DIALECT", b"0"),
28462            (b"MINSTEMLEN", b"4294967296"),
28463            (b"_BG_INDEX_OOM_PAUSE_TIME", b"4294967296"),
28464            (b"INDEXER_YIELD_EVERY_OPS", b"4294967296"),
28465            (b"CONNECT_TIMEOUT", b"2147483648"),
28466        ] {
28467            assert_eq!(
28468                f.run(&[b"FT.CONFIG", b"SET", name, given]),
28469                bounds,
28470                "{name:?}"
28471            );
28472        }
28473        for (name, given, want) in [
28474            (
28475                b"MINSTEMLEN".as_slice(),
28476                b"1".as_slice(),
28477                "-SEARCH_SYNTAX Minimum stem length cannot be lower than 2\r\n",
28478            ),
28479            (
28480                b"MAX_AGGREGATE_GROUPS",
28481                b"67108865",
28482                "-SEARCH_LIMIT_OVER Value exceeds maximum possible aggregate groups\r\n",
28483            ),
28484            (
28485                b"WORKERS",
28486                b"17",
28487                "-SEARCH_LIMIT_OVER Number of worker threads cannot exceed 16\r\n",
28488            ),
28489            (
28490                b"_NUMERIC_RANGES_PARENTS",
28491                b"3",
28492                "-SEARCH_PARSE_ARGS Max depth for range cannot be higher than max \
28493                 depth for balance\r\n",
28494            ),
28495            (
28496                b"DEFAULT_DIALECT",
28497                b"5",
28498                "-SEARCH_VALUE_BAD Default dialect version cannot be higher than 4\r\n",
28499            ),
28500            (
28501                b"_BG_INDEX_MEM_PCT_THR",
28502                b"101",
28503                "-SEARCH_LIMIT_OVER Memory limit for indexing cannot be greater then \
28504                 100%\r\n",
28505            ),
28506            (
28507                b"BM25STD_TANH_FACTOR",
28508                b"10001",
28509                "-SEARCH_LIMIT_OVER BM25STD_TANH_FACTOR must be between 1 and 10000 \
28510                 inclusive\r\n",
28511            ),
28512            (
28513                b"BG_INDEX_SLEEP_DURATION_US",
28514                b"1000000",
28515                "-SEARCH_LIMIT_OVER BG_INDEX_SLEEP_DURATION_US must be between 1 and \
28516                 999999 (usleep POSIX limit)\r\n",
28517            ),
28518        ] {
28519            assert_eq!(
28520                f.run(&[b"FT.CONFIG", b"SET", name, given]),
28521                want,
28522                "{name:?}"
28523            );
28524        }
28525    }
28526
28527    /// The two trimming delays are measured against each other, and the answer
28528    /// names both settings and both numbers.
28529    #[test]
28530    fn the_trimming_delays_are_checked_against_one_another() {
28531        let mut f = Fixture::new();
28532        assert_eq!(
28533            f.run(&[b"FT.CONFIG", b"SET", b"_MIN_TRIM_DELAY_MS", b"5000"]),
28534            "-SEARCH_PARSE_ARGS _MIN_TRIM_DELAY_MS (5000) must be less than \
28535             _MAX_TRIM_DELAY_MS (5000)\r\n"
28536        );
28537        assert_eq!(
28538            f.run(&[b"FT.CONFIG", b"SET", b"_MAX_TRIM_DELAY_MS", b"1999"]),
28539            "-SEARCH_PARSE_ARGS _MAX_TRIM_DELAY_MS (1999) must be greater than \
28540             _MIN_TRIM_DELAY_MS (2000)\r\n"
28541        );
28542        assert_eq!(
28543            f.run(&[b"FT.CONFIG", b"SET", b"_MIN_TRIM_DELAY_MS", b"4999"]),
28544            "+OK\r\n"
28545        );
28546    }
28547
28548    /// Two of the word settings fold the spelling on the way in and the scorer
28549    /// does not, which is the one place in the table case counts.
28550    #[test]
28551    fn a_word_setting_folds_where_a_real_server_folds_and_not_otherwise() {
28552        let mut f = Fixture::new();
28553        assert_eq!(
28554            f.run(&[b"FT.CONFIG", b"SET", b"ON_TIMEOUT", b"RETURN"]),
28555            "+OK\r\n"
28556        );
28557        assert_eq!(
28558            f.run(&[b"FT.CONFIG", b"GET", b"ON_TIMEOUT"]),
28559            "*1\r\n*2\r\n+ON_TIMEOUT\r\n$6\r\nreturn\r\n"
28560        );
28561        assert_eq!(
28562            f.run(&[b"FT.CONFIG", b"SET", b"ON_TIMEOUT", b"nope"]),
28563            "-SEARCH_VALUE_BAD Invalid ON_TIMEOUT value\r\n"
28564        );
28565        assert_eq!(
28566            f.run(&[b"FT.CONFIG", b"SET", b"ON_OOM", b"IGNORE"]),
28567            "+OK\r\n"
28568        );
28569        assert_eq!(
28570            f.run(&[b"FT.CONFIG", b"GET", b"ON_OOM"]),
28571            "*1\r\n*2\r\n+ON_OOM\r\n$6\r\nignore\r\n"
28572        );
28573        assert_eq!(
28574            f.run(&[b"FT.CONFIG", b"SET", b"ON_OOM", b"nope"]),
28575            "-SEARCH_VALUE_BAD Invalid ON_OOM value\r\n"
28576        );
28577        let bad = "-SEARCH_VALUE_BAD Invalid default scorer value\r\n";
28578        for given in [b"bm25std".as_slice(), b"Bm25", b"TFIDF.docnorm", b""] {
28579            assert_eq!(
28580                f.run(&[b"FT.CONFIG", b"SET", b"DEFAULT_SCORER", given]),
28581                bad,
28582                "{given:?}"
28583            );
28584        }
28585        assert_eq!(
28586            f.run(&[b"FT.CONFIG", b"SET", b"DEFAULT_SCORER", b"TFIDF.DOCNORM"]),
28587            "+OK\r\n"
28588        );
28589    }
28590
28591    /// True and false, either case, and none of the other words a client might
28592    /// reach for.
28593    #[test]
28594    fn a_yes_or_no_setting_takes_those_two_words_only() {
28595        let mut f = Fixture::new();
28596        assert_eq!(
28597            f.run(&[b"FT.CONFIG", b"SET", b"_NUMERIC_COMPRESS", b"TRUE"]),
28598            "+OK\r\n"
28599        );
28600        assert_eq!(
28601            f.run(&[b"FT.CONFIG", b"GET", b"_NUMERIC_COMPRESS"]),
28602            "*1\r\n*2\r\n+_NUMERIC_COMPRESS\r\n$4\r\ntrue\r\n"
28603        );
28604        for given in [b"yes".as_slice(), b"no", b"1", b"0", b"enabled", b""] {
28605            assert_eq!(
28606                f.run(&[b"FT.CONFIG", b"SET", b"_NUMERIC_COMPRESS", given]),
28607                "-SEARCH_PARSE_ARGS Could not convert argument to expected type\r\n",
28608                "{given:?}"
28609            );
28610        }
28611    }
28612
28613    /// Two pairs of names sit over one number each, and one of that second pair
28614    /// takes no value at all.
28615    #[test]
28616    fn two_names_for_one_setting_move_together() {
28617        let mut f = Fixture::new();
28618        f.run(&[b"FT.CONFIG", b"SET", b"MAXEXPANSIONS", b"300"]);
28619        assert_eq!(
28620            f.run(&[b"FT.CONFIG", b"GET", b"MAXPREFIXEXPANSIONS"]),
28621            "*1\r\n*2\r\n+MAXPREFIXEXPANSIONS\r\n$3\r\n300\r\n"
28622        );
28623        f.run(&[b"FT.CONFIG", b"SET", b"MAXPREFIXEXPANSIONS", b"200"]);
28624        assert_eq!(
28625            f.run(&[b"FT.CONFIG", b"GET", b"MAXEXPANSIONS"]),
28626            "*1\r\n*2\r\n+MAXEXPANSIONS\r\n$3\r\n200\r\n"
28627        );
28628        let long = b"_FORK_GC_CLEAN_NUMERIC_EMPTY_NODES".as_slice();
28629        let short = b"FORK_GC_CLEAN_NUMERIC_EMPTY_NODES".as_slice();
28630        f.run(&[b"FT.CONFIG", b"SET", long, b"false"]);
28631        assert_eq!(
28632            f.run(&[b"FT.CONFIG", b"GET", short]),
28633            "*1\r\n*2\r\n+FORK_GC_CLEAN_NUMERIC_EMPTY_NODES\r\n$5\r\nfalse\r\n"
28634        );
28635        assert_eq!(f.run(&[b"FT.CONFIG", b"SET", short]), "+OK\r\n");
28636        assert_eq!(
28637            f.run(&[b"FT.CONFIG", b"GET", long]),
28638            "*1\r\n*2\r\n+_FORK_GC_CLEAN_NUMERIC_EMPTY_NODES\r\n$4\r\ntrue\r\n"
28639        );
28640    }
28641
28642    /// The one setting that takes a write and never gives it back.
28643    #[test]
28644    fn a_password_reads_back_as_stars_whatever_was_written() {
28645        let mut f = Fixture::new();
28646        assert_eq!(
28647            f.run(&[b"FT.CONFIG", b"SET", b"OSS_GLOBAL_PASSWORD", b"hunter2"]),
28648            "+OK\r\n"
28649        );
28650        assert_eq!(
28651            f.run(&[b"FT.CONFIG", b"GET", b"OSS_GLOBAL_PASSWORD"]),
28652            "*1\r\n*2\r\n+OSS_GLOBAL_PASSWORD\r\n$17\r\nPassword: *******\r\n"
28653        );
28654    }
28655
28656    /// The settings are not in the keyspace, so unlike the dictionaries and the
28657    /// synonym groups beside them they live through an emptied one.
28658    #[test]
28659    fn a_flush_leaves_the_settings_alone() {
28660        for flush in [b"FLUSHALL".as_slice(), b"FLUSHDB"] {
28661            let mut f = Fixture::new();
28662            f.run(&[b"FT.CONFIG", b"SET", b"TIMEOUT", b"777"]);
28663            f.run(&[flush]);
28664            assert_eq!(
28665                f.run(&[b"FT.CONFIG", b"GET", b"TIMEOUT"]),
28666                "*1\r\n*2\r\n+TIMEOUT\r\n$3\r\n777\r\n",
28667                "{flush:?}"
28668            );
28669        }
28670    }
28671
28672    // ---------------------------------------------------------------- debug
28673
28674    /// A small index with one of everything a dump can read, so the tests below
28675    /// all name the same three documents and the same four fields.
28676    fn debugging() -> Fixture {
28677        let mut f = Fixture::new();
28678        f.run(&[
28679            b"FT.CREATE",
28680            b"dx",
28681            b"PREFIX",
28682            b"1",
28683            b"d:",
28684            b"SCHEMA",
28685            b"t",
28686            b"TEXT",
28687            b"g",
28688            b"TAG",
28689            b"n",
28690            b"NUMERIC",
28691            b"s",
28692            b"TEXT",
28693            b"SORTABLE",
28694        ]);
28695        f.run(&[
28696            b"HSET",
28697            b"d:1",
28698            b"t",
28699            b"running dogs",
28700            b"g",
28701            b"red,blue",
28702            b"n",
28703            b"1",
28704            b"s",
28705            b"Alpha",
28706        ]);
28707        f.run(&[
28708            b"HSET", b"d:2", b"t", b"running", b"g", b"red", b"n", b"2", b"s", b"beta",
28709        ]);
28710        f.run(&[
28711            b"HSET",
28712            b"d:3",
28713            b"t",
28714            b"dogs alpha",
28715            b"g",
28716            b"green",
28717            b"n",
28718            b"3",
28719        ]);
28720        f
28721    }
28722
28723    /// The whole dictionary in byte order, with the stems in it as entries of
28724    /// their own rather than hidden behind the words they came from.
28725    #[test]
28726    fn a_term_dump_lists_the_stems_beside_the_words() {
28727        let mut f = debugging();
28728        assert_eq!(
28729            f.run(&[b"_FT.DEBUG", b"DUMP_TERMS", b"dx"]),
28730            "*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\
28731             $4\r\ndogs\r\n$7\r\nrunning\r\n"
28732        );
28733    }
28734
28735    /// A posting list is looked up on the bytes given and nothing folds them, so
28736    /// the term that a query would have found is not the term a dump wants.
28737    #[test]
28738    fn a_posting_list_is_read_by_the_bytes_and_not_by_the_word() {
28739        let mut f = debugging();
28740        assert_eq!(
28741            f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", b"running"]),
28742            "*2\r\n:1\r\n:2\r\n"
28743        );
28744        assert_eq!(
28745            f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", b"+run"]),
28746            "*2\r\n:1\r\n:2\r\n"
28747        );
28748        for term in [b"RUNNING".as_slice(), b"nosuchterm", b""] {
28749            assert_eq!(
28750                f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", term]),
28751                "-Can not find the inverted index\r\n",
28752                "{term:?}"
28753            );
28754        }
28755    }
28756
28757    /// Tag values come back folded and in byte order, each with the documents
28758    /// that hold it, and a document with two values is under both of them.
28759    #[test]
28760    fn a_tag_dump_pairs_every_value_with_its_documents() {
28761        let mut f = debugging();
28762        assert_eq!(
28763            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"dx", b"g"]),
28764            "*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\
28765             *2\r\n$3\r\nred\r\n*2\r\n:1\r\n:2\r\n"
28766        );
28767    }
28768
28769    /// One list holding every document in the field, which is D-96: a range tree
28770    /// answers one list per range and this answers the one it keeps.
28771    #[test]
28772    fn a_number_dump_answers_a_single_range() {
28773        let mut f = debugging();
28774        assert_eq!(
28775            f.run(&[b"_FT.DEBUG", b"DUMP_NUMIDX", b"dx", b"n"]),
28776            "*1\r\n*3\r\n:1\r\n:2\r\n:3\r\n"
28777        );
28778    }
28779
28780    /// A point is a number underneath, so the field that holds points answers
28781    /// the subcommand that dumps numbers and not the one that dumps tags.
28782    #[test]
28783    fn a_geo_field_is_dumped_as_a_numeric_one() {
28784        let mut f = Fixture::new();
28785        f.run(&[
28786            b"FT.CREATE",
28787            b"gx",
28788            b"PREFIX",
28789            b"1",
28790            b"q:",
28791            b"SCHEMA",
28792            b"loc",
28793            b"GEO",
28794            b"gg",
28795            b"AS",
28796            b"tag",
28797            b"TAG",
28798        ]);
28799        f.run(&[b"HSET", b"q:1", b"loc", b"1,2", b"gg", b"red"]);
28800        f.run(&[b"HSET", b"q:2", b"loc", b"3,4", b"gg", b"BLUE"]);
28801        assert_eq!(
28802            f.run(&[b"_FT.DEBUG", b"DUMP_NUMIDX", b"gx", b"loc"]),
28803            "*1\r\n*2\r\n:1\r\n:2\r\n"
28804        );
28805        assert_eq!(
28806            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"gx", b"loc"]),
28807            "-Could not find given field in index spec\r\n"
28808        );
28809    }
28810
28811    /// A field is named the way a query names it, so the attribute is the name
28812    /// and the identifier the value was read from is not one.
28813    #[test]
28814    fn a_dump_takes_the_attribute_and_not_the_identifier() {
28815        let mut f = Fixture::new();
28816        f.run(&[
28817            b"FT.CREATE",
28818            b"zx",
28819            b"PREFIX",
28820            b"1",
28821            b"z:",
28822            b"SCHEMA",
28823            b"gg",
28824            b"AS",
28825            b"tag",
28826            b"TAG",
28827        ]);
28828        f.run(&[b"HSET", b"z:1", b"gg", b"red"]);
28829        assert_eq!(
28830            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"zx", b"tag"]),
28831            "*1\r\n*2\r\n$3\r\nred\r\n*1\r\n:1\r\n"
28832        );
28833        assert_eq!(
28834            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"zx", b"gg"]),
28835            "-Could not find given field in index spec\r\n"
28836        );
28837    }
28838
28839    /// The seven keys, with the score as a bulk string here and a double there,
28840    /// and the whole row flat on one protocol and a map on the other.
28841    #[test]
28842    fn a_document_row_is_flat_on_one_protocol_and_a_map_on_the_other() {
28843        let mut f = debugging();
28844        assert_eq!(
28845            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"REVEAL"]),
28846            "*14\r\n+internal_id\r\n:1\r\n$5\r\nflags\r\n\
28847             $36\r\n(0xc):HasSortVector,HasOffsetVector,\r\n+score\r\n$1\r\n1\r\n\
28848             +num_tokens\r\n:3\r\n+max_freq\r\n:1\r\n+refcount\r\n:1\r\n\
28849             +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\
28850             $5\r\nvalue\r\n$5\r\nalpha\r\n"
28851        );
28852        let mut g = debugging();
28853        g.run(&[b"HELLO", b"3"]);
28854        assert_eq!(
28855            g.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"REVEAL"]),
28856            "%7\r\n+internal_id\r\n:1\r\n$5\r\nflags\r\n\
28857             $36\r\n(0xc):HasSortVector,HasOffsetVector,\r\n+score\r\n,1\r\n\
28858             +num_tokens\r\n:3\r\n+max_freq\r\n:1\r\n+refcount\r\n:1\r\n\
28859             +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\
28860             $5\r\nvalue\r\n$5\r\nalpha\r\n"
28861        );
28862    }
28863
28864    /// A document that wrote nothing into a sortable slot has no sortables key
28865    /// at all, so the row is a key shorter rather than carrying an empty list.
28866    #[test]
28867    fn a_document_with_no_sortable_value_drops_the_key() {
28868        let mut f = debugging();
28869        assert_eq!(
28870            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:3", b"REVEAL"]),
28871            "*12\r\n+internal_id\r\n:3\r\n$5\r\nflags\r\n$22\r\n(0x8):HasOffsetVector,\r\n\
28872             +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"
28873        );
28874    }
28875
28876    /// The flag word is the number and then the names it stands for, and an
28877    /// index built without offsets has none of the three set.
28878    #[test]
28879    fn the_flag_word_spells_out_the_bits_it_carries() {
28880        let mut f = Fixture::new();
28881        f.run(&[
28882            b"FT.CREATE",
28883            b"nx",
28884            b"NOOFFSETS",
28885            b"PREFIX",
28886            b"1",
28887            b"o:",
28888            b"SCHEMA",
28889            b"t",
28890            b"TEXT",
28891        ]);
28892        f.run(&[b"HSET", b"o:1", b"t", b"alpha"]);
28893        assert!(
28894            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"nx", b"o:1", b"REVEAL"])
28895                .contains("$6\r\n(0x0):\r\n")
28896        );
28897    }
28898
28899    /// Obfuscation replaces the field name with where the field sits in the
28900    /// whole schema, which is not where its value sits among the sortables.
28901    #[test]
28902    fn obfuscation_numbers_a_field_by_its_place_in_the_schema() {
28903        let mut f = debugging();
28904        assert!(
28905            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"OBFUSCATE"])
28906                .contains("$22\r\nFieldPath@3 AS Field@3\r\n")
28907        );
28908    }
28909
28910    /// The keyword is read where it belongs and anything after it is stepped
28911    /// over, whatever the line that complains about it says.
28912    #[test]
28913    fn a_document_row_reads_its_keyword_at_a_fixed_place() {
28914        let mut f = debugging();
28915        let want = f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"REVEAL"]);
28916        assert_eq!(
28917            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"REVEAL", b"more"]),
28918            want
28919        );
28920        assert_eq!(
28921            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"more", b"REVEAL"]),
28922            "-Invalid argument. Expected REVEAL or OBFUSCATE as the last argument\r\n"
28923        );
28924        assert_eq!(
28925            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1"]),
28926            "-ERR wrong number of arguments for '_FT.DEBUG|DOCINFO' command\r\n"
28927        );
28928    }
28929
28930    /// The key is looked up before the keyword is read, so a key nobody indexed
28931    /// beats a keyword nobody wrote.
28932    #[test]
28933    fn a_document_row_looks_the_key_up_before_it_reads_the_keyword() {
28934        let mut f = debugging();
28935        assert_eq!(
28936            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"nope", b"zz"]),
28937            "-Document not found in index\r\n"
28938        );
28939        assert_eq!(
28940            f.run(&[b"_FT.DEBUG", b"DOCINFO", b"dx", b"d:1", b"zz"]),
28941            "-Invalid argument. Expected REVEAL or OBFUSCATE as the last argument\r\n"
28942        );
28943    }
28944
28945    /// The two directions of the document table, and the number nobody handed
28946    /// out reads as one that was given up rather than as one that never was.
28947    #[test]
28948    fn a_document_number_goes_both_ways() {
28949        let mut f = debugging();
28950        assert_eq!(
28951            f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", b"2"]),
28952            "$3\r\nd:2\r\n"
28953        );
28954        assert_eq!(
28955            f.run(&[b"_FT.DEBUG", b"DOCIDTOID", b"dx", b"d:2"]),
28956            ":2\r\n"
28957        );
28958        assert_eq!(
28959            f.run(&[b"_FT.DEBUG", b"DOCIDTOID", b"dx", b"nope"]),
28960            ":0\r\n"
28961        );
28962        assert_eq!(f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"dx"]), ":3\r\n");
28963        for id in [b"9".as_slice(), b"0", b"-1", b"9223372036854775807"] {
28964            assert_eq!(
28965                f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", id]),
28966                "-document was removed\r\n",
28967                "{id:?}"
28968            );
28969        }
28970    }
28971
28972    /// A document number is read the strict way Redis reads an integer, so a
28973    /// leading zero, a leading plus and a leading space are all refused.
28974    #[test]
28975    fn a_document_number_is_read_the_strict_way() {
28976        let mut f = debugging();
28977        for id in [
28978            b"x".as_slice(),
28979            b"1.5",
28980            b" 1",
28981            b"+1",
28982            b"01",
28983            b"0x1",
28984            b"",
28985            b"9223372036854775808",
28986            b"18446744073709551615",
28987        ] {
28988            assert_eq!(
28989                f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", id]),
28990                "-bad id given\r\n",
28991                "{id:?}"
28992            );
28993        }
28994    }
28995
28996    /// A number a document has given up is still in every list it was in, so a
28997    /// dump names documents that the table says are gone.
28998    #[test]
28999    fn a_dump_keeps_a_number_the_table_has_given_up() {
29000        let mut f = debugging();
29001        f.run(&[b"DEL", b"d:2"]);
29002        assert_eq!(
29003            f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", b"running"]),
29004            "*2\r\n:1\r\n:2\r\n"
29005        );
29006        assert_eq!(
29007            f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", b"2"]),
29008            "-document was removed\r\n"
29009        );
29010        assert_eq!(
29011            f.run(&[b"_FT.DEBUG", b"DOCIDTOID", b"dx", b"d:2"]),
29012            ":0\r\n"
29013        );
29014    }
29015
29016    /// A rewrite hands out a new number and leaves the old one behind, so the
29017    /// counter climbs past the number of documents there are.
29018    #[test]
29019    fn a_rewrite_takes_a_number_of_its_own() {
29020        let mut f = debugging();
29021        f.run(&[b"HSET", b"d:1", b"t", b"cats"]);
29022        assert_eq!(
29023            f.run(&[b"_FT.DEBUG", b"DOCIDTOID", b"dx", b"d:1"]),
29024            ":4\r\n"
29025        );
29026        assert_eq!(f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"dx"]), ":4\r\n");
29027        assert_eq!(
29028            f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"dx", b"1"]),
29029            "-document was removed\r\n"
29030        );
29031        assert_eq!(
29032            f.run(&[b"_FT.DEBUG", b"DUMP_INVIDX", b"dx", b"running"]),
29033            "*2\r\n:1\r\n:2\r\n"
29034        );
29035    }
29036
29037    /// An alias reads the index it stands for, the same as a query does.
29038    #[test]
29039    fn a_dump_follows_an_alias() {
29040        let mut f = debugging();
29041        f.run(&[b"FT.ALIASADD", b"da", b"dx"]);
29042        assert_eq!(f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"da"]), ":3\r\n");
29043        assert_eq!(
29044            f.run(&[b"_FT.DEBUG", b"IDTODOCID", b"da", b"1"]),
29045            "$3\r\nd:1\r\n"
29046        );
29047    }
29048
29049    /// The index name is matched as written and the subcommand name is not, and
29050    /// an index nobody made is reported as a context that could not be built.
29051    #[test]
29052    fn an_index_name_is_case_sensitive_and_a_subcommand_name_is_not() {
29053        let mut f = debugging();
29054        assert_eq!(f.run(&[b"_FT.DEBUG", b"get_max_doc_id", b"dx"]), ":3\r\n");
29055        assert_eq!(
29056            f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"DX"]),
29057            "-Can not create a search ctx\r\n"
29058        );
29059        assert_eq!(
29060            f.run(&[b"_FT.DEBUG", b"DUMP_TERMS", b"nope"]),
29061            "-Can not create a search ctx\r\n"
29062        );
29063    }
29064
29065    /// A field with nothing written into it answers an empty dump rather than an
29066    /// error, since the field is in the schema and only the values are missing.
29067    #[test]
29068    fn an_empty_field_dumps_as_nothing_at_all() {
29069        let mut f = Fixture::new();
29070        f.run(&[
29071            b"FT.CREATE",
29072            b"ex",
29073            b"PREFIX",
29074            b"1",
29075            b"e:",
29076            b"SCHEMA",
29077            b"t",
29078            b"TEXT",
29079            b"g",
29080            b"TAG",
29081            b"n",
29082            b"NUMERIC",
29083        ]);
29084        assert_eq!(f.run(&[b"_FT.DEBUG", b"DUMP_TERMS", b"ex"]), "*0\r\n");
29085        assert_eq!(
29086            f.run(&[b"_FT.DEBUG", b"DUMP_TAGIDX", b"ex", b"g"]),
29087            "*0\r\n"
29088        );
29089        assert_eq!(
29090            f.run(&[b"_FT.DEBUG", b"DUMP_NUMIDX", b"ex", b"n"]),
29091            "*0\r\n"
29092        );
29093        assert_eq!(f.run(&[b"_FT.DEBUG", b"GET_MAX_DOC_ID", b"ex"]), ":0\r\n");
29094    }
29095
29096    /// The two lines the dispatcher owns are the two that carry a code word, and
29097    /// every subcommand but `DOCINFO` counts its arguments exactly.
29098    #[test]
29099    fn the_two_lines_with_a_code_word_are_the_arity_and_the_unknown_one() {
29100        let mut f = debugging();
29101        for (sub, extra) in [
29102            (b"DUMP_TERMS".as_slice(), 1),
29103            (b"GET_MAX_DOC_ID", 1),
29104            (b"DUMP_INVIDX", 2),
29105            (b"DUMP_TAGIDX", 2),
29106            (b"DUMP_NUMIDX", 2),
29107            (b"IDTODOCID", 2),
29108            (b"DOCIDTOID", 2),
29109        ] {
29110            let want = format!(
29111                "-ERR wrong number of arguments for '_FT.DEBUG|{}' command\r\n",
29112                str::from_utf8(sub).unwrap()
29113            );
29114            for given in [extra - 1, extra + 1] {
29115                let mut cmd: Vec<&[u8]> = vec![b"_FT.DEBUG", sub];
29116                cmd.extend(std::iter::repeat_n(b"dx".as_slice(), given));
29117                assert_eq!(f.run(&cmd), want, "{sub:?} {given}");
29118            }
29119            let mut right: Vec<&[u8]> = vec![b"_FT.DEBUG", sub, b"dx"];
29120            right.extend(std::iter::repeat_n(b"g".as_slice(), extra - 1));
29121            assert_ne!(f.run(&right), want, "{sub:?}");
29122        }
29123        assert_eq!(
29124            f.run(&[b"_FT.DEBUG", b"bogus", b"dx"]),
29125            "-ERR unknown subcommand 'bogus'. Try _FT.DEBUG HELP.\r\n"
29126        );
29127    }
29128
29129    /// The eight names that answer rather than the sixty two a real server
29130    /// registers, which is D-97, and anything after the name is stepped over.
29131    #[test]
29132    fn the_help_names_the_subcommands_that_answer() {
29133        let mut f = Fixture::new();
29134        let want = "*8\r\n$11\r\nDUMP_INVIDX\r\n$11\r\nDUMP_NUMIDX\r\n$11\r\nDUMP_TAGIDX\r\n\
29135             $9\r\nIDTODOCID\r\n$9\r\nDOCIDTOID\r\n$7\r\nDOCINFO\r\n$10\r\nDUMP_TERMS\r\n\
29136             $14\r\nGET_MAX_DOC_ID\r\n";
29137        assert_eq!(f.run(&[b"_FT.DEBUG", b"HELP"]), want);
29138        assert_eq!(f.run(&[b"_FT.DEBUG", b"HELP", b"extra"]), want);
29139    }
29140
29141    // ------------------------------------------------------------- synonyms
29142
29143    /// The terms are folded on the way in and the group ids are not, and one
29144    /// term can be in more than one group.
29145    #[test]
29146    fn a_synonym_dump_folds_the_terms_and_keeps_the_ids_as_given() {
29147        let mut f = Fixture::new();
29148        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT"]);
29149        assert_eq!(
29150            f.run(&[b"FT.SYNUPDATE", b"e", b"G1", b"BOY", b"kid"]),
29151            "+OK\r\n"
29152        );
29153        assert_eq!(f.run(&[b"FT.SYNUPDATE", b"e", b"g2", b"boy"]), "+OK\r\n");
29154        assert_eq!(
29155            f.run(&[b"FT.SYNDUMP", b"e"]),
29156            "*4\r\n$3\r\nboy\r\n*2\r\n$2\r\nG1\r\n$2\r\ng2\r\n\
29157             $3\r\nkid\r\n*1\r\n$2\r\nG1\r\n"
29158        );
29159    }
29160
29161    /// A group is not a comparison made at query time. It is a term of its
29162    /// own, so a word in a group reads as a union of the word, the groups it
29163    /// is in and its stem.
29164    #[test]
29165    fn a_word_in_a_group_reads_as_a_union_with_the_group_term() {
29166        let mut f = Fixture::new();
29167        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT"]);
29168        f.run(&[b"FT.SYNUPDATE", b"e", b"gr", b"jogging"]);
29169        assert_eq!(
29170            f.run(&[b"FT.EXPLAIN", b"e", b"jogging"]),
29171            "$69\r\nUNION {\n  jogging\n  ~gr(expanded)\n  +jog(expanded)\n  jog(expanded)\n}\n\r\n"
29172        );
29173    }
29174
29175    /// The lookup on the document side is on the word and never on the stem,
29176    /// and a group written after the documents were still finds them because
29177    /// the index is read again.
29178    ///
29179    /// The group holds `running` and `d2` says `runs`, so a query for another
29180    /// word of the group finds `d1` and leaves `d2` where it is. A query for
29181    /// `running` itself does find `d2`, through the stem branch of the union
29182    /// rather than through the group, which is why the two asserts differ.
29183    #[test]
29184    fn a_group_matches_the_word_it_holds_and_not_a_stem_of_it() {
29185        let mut f = Fixture::new();
29186        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT"]);
29187        f.run(&[b"HSET", b"d1", b"t", b"boy"]);
29188        f.run(&[b"HSET", b"d2", b"t", b"runs"]);
29189        f.run(&[b"FT.SYNUPDATE", b"e", b"gr", b"boy", b"child", b"running"]);
29190        assert_eq!(
29191            f.run(&[b"FT.SEARCH", b"e", b"child", b"NOCONTENT"]),
29192            "*2\r\n:1\r\n$2\r\nd1\r\n"
29193        );
29194        assert_eq!(
29195            f.run(&[b"FT.SEARCH", b"e", b"running", b"NOCONTENT"]),
29196            "*3\r\n:2\r\n$2\r\nd1\r\n$2\r\nd2\r\n"
29197        );
29198    }
29199
29200    /// Neither command makes an index and neither forgives a name that is not
29201    /// there, in the same words the rest of the group uses.
29202    #[test]
29203    fn a_synonym_command_on_a_name_that_is_not_there_fails() {
29204        let mut f = Fixture::new();
29205        let missing = "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n";
29206        assert_eq!(f.run(&[b"FT.SYNDUMP", b"nope"]), missing);
29207        assert_eq!(f.run(&[b"FT.SYNUPDATE", b"nope", b"g", b"a"]), missing);
29208    }
29209
29210    /// The words after `PARAMS n` are counted before their shape is looked at,
29211    /// so a count that reaches past the end of the command and a count that is
29212    /// merely odd are two different errors.
29213    #[test]
29214    fn params_counts_the_words_before_it_pairs_them_up() {
29215        let mut f = Fixture::new();
29216        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT"]);
29217        let none = "-SEARCH_PARSE_ARGS Bad arguments for PARAMS: \
29218                    Expected an argument, but none provided\r\n";
29219        let odd = "-SEARCH_ADD_ARGS Parameters must be specified in PARAM VALUE pairs\r\n";
29220        assert_eq!(f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"1"]), none);
29221        assert_eq!(
29222            f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"3", b"a", b"b"]),
29223            none
29224        );
29225        assert_eq!(
29226            f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"1", b"a"]),
29227            odd
29228        );
29229        assert_eq!(f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"0"]), odd);
29230        assert_eq!(
29231            f.run(&[b"FT.SEARCH", b"e", b"x", b"PARAMS", b"-1"]),
29232            "-SEARCH_PARSE_ARGS Bad arguments for PARAMS: Value is outside acceptable bounds\r\n"
29233        );
29234    }
29235
29236    // --------------------------------------------------------------- vectors
29237
29238    /// Five documents a unit apart along one axis, written in the opposite
29239    /// order to the one they sit in, so a reply in document order and a reply
29240    /// in distance order are two different replies.
29241    ///
29242    /// `d1` is furthest from the origin and `d5` is on it. The text field
29243    /// splits them so a query can narrow before it measures: `d1`, `d2` and
29244    /// `d4` say `alpha` and the other two say `beta`.
29245    fn vectored(f: &mut Fixture) {
29246        f.run(&[
29247            b"FT.CREATE",
29248            b"h",
29249            b"SCHEMA",
29250            b"t",
29251            b"TEXT",
29252            b"v",
29253            b"VECTOR",
29254            b"FLAT",
29255            b"6",
29256            b"TYPE",
29257            b"FLOAT32",
29258            b"DIM",
29259            b"2",
29260            b"DISTANCE_METRIC",
29261            b"L2",
29262        ]);
29263        let at: [&[u8]; 5] = [
29264            b"\x00\x00\x80\x40\x00\x00\x00\x00",
29265            b"\x00\x00\x40\x40\x00\x00\x00\x00",
29266            b"\x00\x00\x00\x40\x00\x00\x00\x00",
29267            b"\x00\x00\x80\x3f\x00\x00\x00\x00",
29268            b"\x00\x00\x00\x00\x00\x00\x00\x00",
29269        ];
29270        for (n, point) in at.iter().enumerate() {
29271            let key = format!("d{}", n + 1);
29272            let word: &[u8] = match n {
29273                0 | 1 | 3 => b"alpha",
29274                _ => b"beta",
29275            };
29276            f.run(&[b"HSET", key.as_bytes(), b"t", word, b"v", point]);
29277        }
29278    }
29279
29280    /// The origin, which every query below asks about.
29281    const ORIGIN: &[u8] = b"\x00\x00\x00\x00\x00\x00\x00\x00";
29282
29283    /// A `KNN` picks the k nearest and then answers them in document order,
29284    /// which is measured: asking for three of five that were written furthest
29285    /// first answers the last three written and not the first three.
29286    #[test]
29287    fn a_knn_picks_the_nearest_and_answers_them_in_document_order() {
29288        let mut f = Fixture::new();
29289        vectored(&mut f);
29290        assert_eq!(
29291            f.run(&[
29292                b"FT.SEARCH",
29293                b"h",
29294                b"*=>[KNN 5 @v $vec]",
29295                b"PARAMS",
29296                b"2",
29297                b"vec",
29298                ORIGIN,
29299                b"DIALECT",
29300                b"2",
29301                b"NOCONTENT",
29302            ]),
29303            "*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"
29304        );
29305        assert_eq!(
29306            f.run(&[
29307                b"FT.SEARCH",
29308                b"h",
29309                b"*=>[KNN 3 @v $vec]",
29310                b"PARAMS",
29311                b"2",
29312                b"vec",
29313                ORIGIN,
29314                b"DIALECT",
29315                b"2",
29316                b"NOCONTENT",
29317            ]),
29318            "*4\r\n:3\r\n$2\r\nd3\r\n$2\r\nd4\r\n$2\r\nd5\r\n"
29319        );
29320    }
29321
29322    /// A range takes what is really inside it, where the distances are squared
29323    /// so the five documents sit at 16, 9, 4, 1 and 0.
29324    #[test]
29325    fn a_range_takes_what_is_inside_it_and_the_distance_is_squared() {
29326        let mut f = Fixture::new();
29327        vectored(&mut f);
29328        for (radius, want) in [
29329            ("0", "*2\r\n:1\r\n$2\r\nd5\r\n"),
29330            ("2", "*3\r\n:2\r\n$2\r\nd4\r\n$2\r\nd5\r\n"),
29331            (
29332                "9",
29333                "*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",
29334            ),
29335        ] {
29336            let query = format!("@v:[VECTOR_RANGE {radius} $vec]");
29337            assert_eq!(
29338                f.run(&[
29339                    b"FT.SEARCH",
29340                    b"h",
29341                    query.as_bytes(),
29342                    b"PARAMS",
29343                    b"2",
29344                    b"vec",
29345                    ORIGIN,
29346                    b"DIALECT",
29347                    b"2",
29348                    b"NOCONTENT",
29349                ]),
29350                want,
29351                "radius {radius}"
29352            );
29353        }
29354    }
29355
29356    /// A `KNN` behind a query is the nearest of what the query matched, so
29357    /// asking for two of the three documents that say `alpha` answers the two
29358    /// of those three that are nearest and not the two nearest overall.
29359    #[test]
29360    fn a_knn_measures_what_the_query_in_front_of_it_matched() {
29361        let mut f = Fixture::new();
29362        vectored(&mut f);
29363        assert_eq!(
29364            f.run(&[
29365                b"FT.SEARCH",
29366                b"h",
29367                b"alpha=>[KNN 2 @v $vec]",
29368                b"PARAMS",
29369                b"2",
29370                b"vec",
29371                ORIGIN,
29372                b"DIALECT",
29373                b"2",
29374                b"NOCONTENT",
29375            ]),
29376            "*3\r\n:2\r\n$2\r\nd2\r\n$2\r\nd4\r\n"
29377        );
29378    }
29379
29380    /// A `KNN` counts in whole numbers and a range measures from zero, and the
29381    /// two are refused in their own words.
29382    ///
29383    /// The count is a token of its own and is checked where it stands, ahead of
29384    /// the field and ahead of the vector. A count that arrives through `PARAMS`
29385    /// is read by looser rules than one written into the query, which is
29386    /// measured: a leading plus is fine in a parameter and a syntax error in
29387    /// the query text.
29388    #[test]
29389    fn a_count_and_a_radius_are_refused_in_their_own_words() {
29390        let mut f = Fixture::new();
29391        vectored(&mut f);
29392        let ask = |f: &mut Fixture, query: &str| {
29393            f.run(&[
29394                b"FT.SEARCH",
29395                b"h",
29396                query.as_bytes(),
29397                b"PARAMS",
29398                b"2",
29399                b"vec",
29400                ORIGIN,
29401                b"DIALECT",
29402                b"2",
29403                b"NOCONTENT",
29404            ])
29405        };
29406        for (query, at, near) in [
29407            ("*=>[KNN -1 @v $vec]", 8, "-1"),
29408            ("*=>[KNN 1.5 @v $vec]", 8, "1.5"),
29409            ("*=>[KNN +3 @v $vec]", 8, "+3"),
29410            ("*=>[KNN 0x10 @v $vec]", 8, "0x10"),
29411            ("*=>[KNN abc @v $vec]", 8, "abc"),
29412            ("*=>[KNN 3 $vec]", 10, "vec"),
29413            ("*=>[KNN 3 @v vec]", 13, "vec"),
29414            ("@v:[VECTOR_RANGE 2 -1]", 19, "-1"),
29415        ] {
29416            assert_eq!(
29417                ask(&mut f, query),
29418                format!("-SEARCH_SYNTAX Syntax error at offset {at} near {near}\r\n"),
29419                "{query}"
29420            );
29421        }
29422
29423        // Read as a double the way a real server reads it, so the bound plus
29424        // thirty two rounds back onto the bound and gets in.
29425        let large = "-SEARCH_QUERY_BAD Error parsing vector similarity query: \
29426                     query KNN K parameter is too large, must not exceed 288230376151711744\r\n";
29427        assert_eq!(
29428            ask(&mut f, "*=>[KNN 288230376151711776 @v $vec]"),
29429            "*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"
29430        );
29431        assert_eq!(ask(&mut f, "*=>[KNN 288230376151711777 @v $vec]"), large);
29432        assert_eq!(ask(&mut f, "*=>[KNN 99999999999999999999 @v $vec]"), large);
29433
29434        for (radius, printed) in [("-1", "-1"), ("-0.5", "-0.5"), ("-1e2", "-100")] {
29435            let query = format!("@v:[VECTOR_RANGE {radius} $vec]");
29436            assert_eq!(
29437                ask(&mut f, &query),
29438                format!(
29439                    "-SEARCH_QUERY_BAD Error parsing vector similarity query: \
29440                     negative radius ({printed}) given in a range query\r\n"
29441                ),
29442                "{query}"
29443            );
29444        }
29445        // A radius of minus zero is not below zero and is a radius of zero.
29446        assert_eq!(
29447            ask(&mut f, "@v:[VECTOR_RANGE -0 $vec]"),
29448            "*2\r\n:1\r\n$2\r\nd5\r\n"
29449        );
29450    }
29451
29452    /// A count passed with `PARAMS` is read the way a real server reads one,
29453    /// which is not the way the same digits are read in the query text.
29454    #[test]
29455    fn a_count_that_came_from_params_is_read_by_its_own_rules() {
29456        let mut f = Fixture::new();
29457        vectored(&mut f);
29458        let ask = |f: &mut Fixture, count: &[u8]| {
29459            f.run(&[
29460                b"FT.SEARCH",
29461                b"h",
29462                b"*=>[KNN $k @v $vec]",
29463                b"PARAMS",
29464                b"4",
29465                b"vec",
29466                ORIGIN,
29467                b"k",
29468                count,
29469                b"DIALECT",
29470                b"2",
29471                b"NOCONTENT",
29472            ])
29473        };
29474        let three = "*4\r\n:3\r\n$2\r\nd3\r\n$2\r\nd4\r\n$2\r\nd5\r\n";
29475        assert_eq!(ask(&mut f, b"3"), three);
29476        assert_eq!(ask(&mut f, b"  3"), three);
29477        assert_eq!(ask(&mut f, b"+3"), three);
29478        for bad in [
29479            &b"3.0"[..],
29480            b"0x3",
29481            b"-1",
29482            b"abc",
29483            b"",
29484            b"99999999999999999999",
29485        ] {
29486            let value = String::from_utf8_lossy(bad).into_owned();
29487            assert_eq!(
29488                ask(&mut f, bad),
29489                format!(
29490                    "-SEARCH_NUMERIC_VALUE_INVALID Invalid numeric value ({value}) \
29491                     for parameter `k`\r\n"
29492                ),
29493                "{value}"
29494            );
29495        }
29496        assert_eq!(
29497            ask(&mut f, b"288230376151711777"),
29498            "-SEARCH_QUERY_BAD Error parsing vector similarity query: \
29499             query KNN K parameter is too large, must not exceed 288230376151711744\r\n"
29500        );
29501    }
29502
29503    /// A vector the wrong size is refused against the field it was passed to,
29504    /// naming both sizes in bytes.
29505    #[test]
29506    fn a_vector_the_wrong_size_is_refused_by_the_field_it_reached() {
29507        let mut f = Fixture::new();
29508        vectored(&mut f);
29509        assert_eq!(
29510            f.run(&[
29511                b"FT.SEARCH",
29512                b"h",
29513                b"*=>[KNN 5 @v $vec]",
29514                b"PARAMS",
29515                b"2",
29516                b"vec",
29517                b"abc",
29518                b"DIALECT",
29519                b"2",
29520                b"NOCONTENT",
29521            ]),
29522            "-SEARCH_QUERY_BAD Error parsing vector similarity query: \
29523             query vector blob size (3) does not match index's expected size (8).\r\n"
29524        );
29525    }
29526
29527    /// A nearest neighbour clause puts its distance on every row it answers,
29528    /// under `__v_score` unless the query renamed it. A range clause puts
29529    /// nothing there at all unless the query named it, which is what
29530    /// `YIELD_DISTANCE_AS` is for.
29531    #[test]
29532    fn a_vector_clause_yields_its_distance_under_the_name_it_was_given() {
29533        let mut f = Fixture::new();
29534        vectored(&mut f);
29535        let ask = |f: &mut Fixture, query: &str| {
29536            f.run(&[
29537                b"FT.SEARCH",
29538                b"h",
29539                query.as_bytes(),
29540                b"PARAMS",
29541                b"2",
29542                b"vec",
29543                ORIGIN,
29544                b"DIALECT",
29545                b"2",
29546                b"LIMIT",
29547                b"0",
29548                b"1",
29549            ])
29550        };
29551        assert_eq!(
29552            ask(&mut f, "*=>[KNN 3 @v $vec]"),
29553            "*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"
29554        );
29555        assert_eq!(
29556            ask(&mut f, "*=>[KNN 3 @v $vec AS d]"),
29557            "*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"
29558        );
29559        assert_eq!(
29560            ask(&mut f, "@v:[VECTOR_RANGE 4 $vec]"),
29561            "*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"
29562        );
29563        assert_eq!(
29564            ask(&mut f, "@v:[VECTOR_RANGE 4 $vec]=>{$YIELD_DISTANCE_AS: d}"),
29565            "*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"
29566        );
29567    }
29568
29569    /// What decides whether a `RETURN` answers the distance is the name the row
29570    /// would carry it under and not the field it would have been read from,
29571    /// because it is on the row before any key is read.
29572    ///
29573    /// So naming it answers it, renaming it answers nothing at all, and giving
29574    /// its name to another field answers the distance under that name.
29575    #[test]
29576    fn a_return_answers_the_distance_by_the_name_the_row_carries_it_under() {
29577        let mut f = Fixture::new();
29578        vectored(&mut f);
29579        let ask = |f: &mut Fixture, ret: &[&[u8]]| {
29580            let mut args: Vec<&[u8]> = vec![b"FT.SEARCH", b"h", b"*=>[KNN 1 @v $vec]"];
29581            args.extend_from_slice(ret);
29582            args.extend_from_slice(&[b"PARAMS", b"2", b"vec", ORIGIN, b"DIALECT", b"2"]);
29583            f.run(&args)
29584        };
29585        assert_eq!(
29586            ask(&mut f, &[b"RETURN", b"1", b"__v_score"]),
29587            "*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"
29588        );
29589        assert_eq!(
29590            ask(&mut f, &[b"RETURN", b"3", b"__v_score", b"AS", b"x"]),
29591            "*3\r\n:1\r\n$2\r\nd5\r\n*0\r\n"
29592        );
29593        assert_eq!(
29594            ask(&mut f, &[b"RETURN", b"3", b"t", b"AS", b"__v_score"]),
29595            "*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"
29596        );
29597        assert_eq!(
29598            ask(&mut f, &[b"RETURN", b"1", b"t"]),
29599            "*3\r\n:1\r\n$2\r\nd5\r\n*2\r\n$1\r\nt\r\n$4\r\nbeta\r\n"
29600        );
29601        // The distance goes in front of the rest whatever order they were
29602        // named in, and `NOCONTENT` takes it away with everything else.
29603        assert_eq!(
29604            ask(&mut f, &[b"RETURN", b"2", b"t", b"__v_score"]),
29605            "*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"
29606        );
29607        assert_eq!(ask(&mut f, &[b"NOCONTENT"]), "*2\r\n:1\r\n$2\r\nd5\r\n");
29608    }
29609
29610    /// A `SORTBY` can name a distance the query yielded, which sorts by the
29611    /// number rather than by anything the key holds. A name the query did not
29612    /// yield is refused the way any other unknown property is.
29613    #[test]
29614    fn a_sortby_can_name_a_distance_the_query_yielded() {
29615        let mut f = Fixture::new();
29616        vectored(&mut f);
29617        let ask = |f: &mut Fixture, query: &str, by: &[u8], desc: bool| {
29618            let mut args: Vec<&[u8]> = vec![b"FT.SEARCH", b"h", query.as_bytes(), b"SORTBY", by];
29619            if desc {
29620                args.push(b"DESC");
29621            }
29622            args.extend_from_slice(&[
29623                b"PARAMS",
29624                b"2",
29625                b"vec",
29626                ORIGIN,
29627                b"DIALECT",
29628                b"2",
29629                b"NOCONTENT",
29630            ]);
29631            f.run(&args)
29632        };
29633        assert_eq!(
29634            ask(&mut f, "*=>[KNN 3 @v $vec]", b"__v_score", false),
29635            "*4\r\n:3\r\n$2\r\nd5\r\n$2\r\nd4\r\n$2\r\nd3\r\n"
29636        );
29637        assert_eq!(
29638            ask(&mut f, "*=>[KNN 3 @v $vec]", b"__v_score", true),
29639            "*4\r\n:3\r\n$2\r\nd3\r\n$2\r\nd4\r\n$2\r\nd5\r\n"
29640        );
29641        assert_eq!(
29642            ask(&mut f, "*=>[KNN 3 @v $vec AS d]", b"d", false),
29643            "*4\r\n:3\r\n$2\r\nd5\r\n$2\r\nd4\r\n$2\r\nd3\r\n"
29644        );
29645        // Renaming it takes the old name away, and a query with no vector
29646        // clause in it never had the property at all.
29647        let missing = "-SEARCH_PROP_NOT_FOUND Property `__v_score` \
29648                       not loaded nor in schema\r\n";
29649        assert_eq!(
29650            ask(&mut f, "*=>[KNN 3 @v $vec AS d]", b"__v_score", false),
29651            missing
29652        );
29653        assert_eq!(ask(&mut f, "alpha", b"__v_score", false), missing);
29654        // The query is read before the property is looked up, which is
29655        // measured: a query that will not parse is answered first.
29656        assert_eq!(
29657            ask(&mut f, "foo(", b"zz", false),
29658            "-SEARCH_SYNTAX Syntax error at offset 3 near foo\r\n"
29659        );
29660    }
29661
29662    /// Two vector clauses in one query answer two distances, outermost first.
29663    #[test]
29664    fn two_vector_clauses_answer_two_distances() {
29665        let mut f = Fixture::new();
29666        vectored(&mut f);
29667        assert_eq!(
29668            f.run(&[
29669                b"FT.SEARCH",
29670                b"h",
29671                b"@v:[VECTOR_RANGE 9 $vec]=>{$YIELD_DISTANCE_AS: rr}=>[KNN 2 @v $vec]",
29672                b"RETURN",
29673                b"2",
29674                b"rr",
29675                b"__v_score",
29676                b"PARAMS",
29677                b"2",
29678                b"vec",
29679                ORIGIN,
29680                b"DIALECT",
29681                b"2",
29682            ]),
29683            "*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"
29684        );
29685    }
29686
29687    /// An aggregation carries the distance on every row whether or not the
29688    /// pipeline ever mentions it, and carries it in front of everything a
29689    /// `LOAD` asked for.
29690    #[test]
29691    fn an_aggregation_answers_a_distance_nothing_asked_for() {
29692        let mut f = Fixture::new();
29693        vectored(&mut f);
29694        let ask = |f: &mut Fixture, query: &str, rest: &[&[u8]]| {
29695            let mut args: Vec<&[u8]> = vec![b"FT.AGGREGATE", b"h", query.as_bytes()];
29696            args.extend_from_slice(rest);
29697            args.extend_from_slice(&[b"PARAMS", b"2", b"vec", ORIGIN, b"DIALECT", b"2"]);
29698            f.run(&args)
29699        };
29700        assert_eq!(
29701            ask(&mut f, "*=>[KNN 2 @v $vec]", &[]),
29702            "*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"
29703        );
29704        assert_eq!(
29705            ask(&mut f, "*=>[KNN 2 @v $vec]", &[b"LOAD", b"1", b"@t"]),
29706            "*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"
29707        );
29708        assert_eq!(
29709            ask(&mut f, "*=>[KNN 2 @v $vec AS d]", &[]),
29710            "*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"
29711        );
29712        // A range shows nothing until the query names it.
29713        assert_eq!(
29714            ask(&mut f, "@v:[VECTOR_RANGE 1 $vec]", &[]),
29715            "*3\r\n:1\r\n*0\r\n*0\r\n"
29716        );
29717        assert_eq!(
29718            ask(
29719                &mut f,
29720                "@v:[VECTOR_RANGE 1 $vec]=>{$YIELD_DISTANCE_AS: rr}",
29721                &[]
29722            ),
29723            "*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"
29724        );
29725    }
29726
29727    /// A nearest neighbour clause hands its documents back nearest first and an
29728    /// aggregation keeps them that way, where a search sorts them into document
29729    /// order. A tie goes to the document written first.
29730    #[test]
29731    fn an_aggregation_keeps_the_order_a_nearest_neighbour_clause_made() {
29732        let mut f = Fixture::new();
29733        vectored(&mut f);
29734        // Sitting on `d3`, so `d2` and `d4` are the same distance away.
29735        const MIDDLE: &[u8] = b"\x00\x00\x00\x40\x00\x00\x00\x00";
29736        let ask = |f: &mut Fixture, query: &str, vec: &[u8]| {
29737            f.run(&[
29738                b"FT.AGGREGATE",
29739                b"h",
29740                query.as_bytes(),
29741                b"LOAD",
29742                b"1",
29743                b"@t",
29744                b"PARAMS",
29745                b"2",
29746                b"vec",
29747                vec,
29748                b"DIALECT",
29749                b"2",
29750            ])
29751        };
29752        assert_eq!(
29753            ask(&mut f, "*=>[KNN 3 @v $vec]", MIDDLE),
29754            "*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"
29755        );
29756        // A range does no ordering, so those rows stay in document order.
29757        assert_eq!(
29758            ask(
29759                &mut f,
29760                "@v:[VECTOR_RANGE 1 $vec]=>{$YIELD_DISTANCE_AS: rr}",
29761                MIDDLE
29762            ),
29763            "*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"
29764        );
29765    }
29766
29767    /// Every step of the pipeline can name a distance the query yielded, and a
29768    /// query with no vector clause in it is refused for the name three
29769    /// different ways depending on which step asked.
29770    #[test]
29771    fn a_pipeline_step_can_name_a_distance_the_query_yielded() {
29772        let mut f = Fixture::new();
29773        vectored(&mut f);
29774        let ask = |f: &mut Fixture, query: &str, rest: &[&[u8]]| {
29775            let mut args: Vec<&[u8]> = vec![b"FT.AGGREGATE", b"h", query.as_bytes()];
29776            args.extend_from_slice(rest);
29777            args.extend_from_slice(&[b"PARAMS", b"2", b"vec", ORIGIN, b"DIALECT", b"2"]);
29778            f.run(&args)
29779        };
29780        let knn = "*=>[KNN 2 @v $vec]";
29781        assert_eq!(
29782            ask(&mut f, knn, &[b"APPLY", b"@__v_score * 2", b"AS", b"x"]),
29783            "*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"
29784        );
29785        assert_eq!(
29786            ask(&mut f, knn, &[b"FILTER", b"@__v_score > 0"]),
29787            "*2\r\n:1\r\n*2\r\n$9\r\n__v_score\r\n$1\r\n1\r\n"
29788        );
29789        assert_eq!(
29790            ask(&mut f, knn, &[b"SORTBY", b"2", b"@__v_score", b"DESC"]),
29791            "*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"
29792        );
29793        assert_eq!(
29794            ask(
29795                &mut f,
29796                knn,
29797                &[
29798                    b"GROUPBY",
29799                    b"1",
29800                    b"@t",
29801                    b"REDUCE",
29802                    b"MAX",
29803                    b"1",
29804                    b"@__v_score",
29805                    b"AS",
29806                    b"m"
29807                ]
29808            ),
29809            "*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"
29810        );
29811        assert_eq!(
29812            ask(&mut f, "*", &[b"APPLY", b"@__v_score", b"AS", b"x"]),
29813            "-SEARCH_PROP_NOT_FOUND Property not loaded nor in pipeline: \
29814             `__v_score`\r\n"
29815        );
29816        assert_eq!(
29817            ask(&mut f, "*", &[b"GROUPBY", b"1", b"@__v_score"]),
29818            "-SEARCH_PROP_NOT_FOUND No such property `__v_score`\r\n"
29819        );
29820        assert_eq!(
29821            ask(&mut f, "*", &[b"SORTBY", b"2", b"@__v_score", b"ASC"]),
29822            "-SEARCH_PROP_NOT_FOUND Property `__v_score` not loaded nor in \
29823             schema\r\n"
29824        );
29825    }
29826
29827    /// An aggregation reads every word before it reads the query, and reads the
29828    /// query before it ties anything on the pipeline to a place on the row.
29829    ///
29830    /// So a command with a fault in all three answers the one about the words,
29831    /// a command with a fault in the last two answers the one about the query,
29832    /// and the pipeline speaks last. That is measured, and it is the whole
29833    /// reason the arguments are read twice.
29834    #[test]
29835    fn the_words_come_before_the_query_and_the_query_before_the_pipeline() {
29836        let mut f = Fixture::new();
29837        vectored(&mut f);
29838        let ask = |f: &mut Fixture, rest: &[&[u8]]| {
29839            let mut args: Vec<&[u8]> = vec![b"FT.AGGREGATE", b"h"];
29840            args.extend_from_slice(rest);
29841            f.run(&args)
29842        };
29843        assert_eq!(
29844            ask(
29845                &mut f,
29846                &[b"foo(", b"APPLY", b"@zz", b"AS", b"x", b"LIMIT", b"x", b"1"]
29847            ),
29848            "-SEARCH_PARSE_ARGS LIMIT needs two numeric arguments\r\n"
29849        );
29850        assert_eq!(
29851            ask(&mut f, &[b"foo(", b"APPLY", b"@zz", b"AS", b"x"]),
29852            "-SEARCH_SYNTAX Syntax error at offset 3 near foo\r\n"
29853        );
29854        assert_eq!(
29855            ask(&mut f, &[b"*", b"APPLY", b"@zz", b"AS", b"x"]),
29856            "-SEARCH_PROP_NOT_FOUND Property not loaded nor in pipeline: `zz`\r\n"
29857        );
29858        // An expression that will not read is the pipeline's fault too, so it
29859        // speaks after the query and after a property named before it.
29860        assert_eq!(
29861            ask(&mut f, &[b"foo(", b"APPLY", b"@@@", b"AS", b"x"]),
29862            "-SEARCH_SYNTAX Syntax error at offset 3 near foo\r\n"
29863        );
29864        assert_eq!(
29865            ask(
29866                &mut f,
29867                &[
29868                    b"*", b"APPLY", b"@zz", b"AS", b"x", b"APPLY", b"@@@", b"AS", b"y"
29869                ]
29870            ),
29871            "-SEARCH_PROP_NOT_FOUND Property not loaded nor in pipeline: `zz`\r\n"
29872        );
29873        assert_eq!(
29874            ask(&mut f, &[b"*", b"APPLY", b"@@@", b"AS", b"x"]),
29875            "-SEARCH_EXPR Syntax error at offset 0 near ''\r\n"
29876        );
29877    }
29878
29879    /// A vector clause says which of the ways of answering one it took, and a
29880    /// range says nothing at all when there is no distance to hand back.
29881    #[test]
29882    fn a_vector_step_says_which_way_it_was_answered() {
29883        let mut f = Fixture::new();
29884        vectored(&mut f);
29885        let tree = |f: &mut Fixture, query: &[u8]| {
29886            let reply = timeless(&f.run(&[
29887                b"FT.PROFILE",
29888                b"h",
29889                b"AGGREGATE",
29890                b"QUERY",
29891                query,
29892                b"PARAMS",
29893                b"2",
29894                b"vec",
29895                ORIGIN,
29896                b"DIALECT",
29897                b"2",
29898            ]));
29899            let at = reply.find("+Iterators profile").expect("a tree");
29900            let end = reply.find("+Result processors").expect("a list of steps");
29901            reply[at..end].to_string()
29902        };
29903        assert_eq!(
29904            tree(&mut f, b"*=>[KNN 3 @v $vec]"),
29905            "+Iterators profile\r\n*8\r\n+Type\r\n+VECTOR\r\n+Time\r\n<t>\r\n\
29906             +Number of reading operations\r\n:3\r\n\
29907             +Vector search mode\r\n+STANDARD_KNN\r\n"
29908        );
29909        // Renaming the distance changes nothing about how it was answered.
29910        assert_eq!(
29911            tree(&mut f, b"*=>[KNN 3 @v $vec AS d]"),
29912            tree(&mut f, b"*=>[KNN 3 @v $vec]")
29913        );
29914        // A range with nothing to yield is not a vector step at all, and one
29915        // that yields names the distance in its own type.
29916        assert_eq!(
29917            tree(&mut f, b"@v:[VECTOR_RANGE 9 $vec]"),
29918            "+Iterators profile\r\n*6\r\n+Type\r\n+ID-LIST-SORTED\r\n+Time\r\n<t>\r\n\
29919             +Number of reading operations\r\n:4\r\n"
29920        );
29921        assert_eq!(
29922            tree(
29923                &mut f,
29924                b"@v:[VECTOR_RANGE 9 $vec]=>{$YIELD_DISTANCE_AS: rr}"
29925            ),
29926            "+Iterators profile\r\n*8\r\n\
29927             +Type\r\n+METRIC SORTED BY ID - VECTOR DISTANCE\r\n+Time\r\n<t>\r\n\
29928             +Number of reading operations\r\n:4\r\n\
29929             +Vector search mode\r\n+RANGE_QUERY\r\n"
29930        );
29931    }
29932
29933    /// What a vector clause narrowed itself down with hangs under it as a
29934    /// single child, and the step that works the distances out is behind the
29935    /// index whenever the query yields one.
29936    #[test]
29937    fn a_clause_in_front_of_a_vector_hangs_under_it_as_one_child() {
29938        let mut f = Fixture::new();
29939        vectored(&mut f);
29940        let ask = |f: &mut Fixture, query: &[u8]| {
29941            timeless(&f.run(&[
29942                b"FT.PROFILE",
29943                b"h",
29944                b"AGGREGATE",
29945                b"QUERY",
29946                query,
29947                b"PARAMS",
29948                b"2",
29949                b"vec",
29950                ORIGIN,
29951                b"DIALECT",
29952                b"2",
29953            ]))
29954        };
29955        let cut = |reply: &str| {
29956            let at = reply.find("+Iterators profile").expect("a tree");
29957            reply[at..].to_string()
29958        };
29959        assert_eq!(
29960            cut(&ask(&mut f, b"@t:alpha=>[KNN 3 @v $vec]")),
29961            "+Iterators profile\r\n*10\r\n+Type\r\n+VECTOR\r\n+Time\r\n<t>\r\n\
29962             +Number of reading operations\r\n:3\r\n\
29963             +Vector search mode\r\n+HYBRID_ADHOC_BF\r\n+Child iterator\r\n\
29964             *10\r\n+Type\r\n+TEXT\r\n+Term\r\n$5\r\nalpha\r\n+Time\r\n<t>\r\n\
29965             +Number of reading operations\r\n:3\r\n\
29966             +Estimated number of matches\r\n:3\r\n\
29967             +Result processors profile\r\n*2\r\n\
29968             *6\r\n+Type\r\n+Index\r\n+Time\r\n<t>\r\n+Results processed\r\n:3\r\n\
29969             *6\r\n+Type\r\n+Metrics Applier\r\n+Time\r\n<t>\r\n\
29970             +Results processed\r\n:3\r\n+Coordinator\r\n*0\r\n"
29971        );
29972        // A range nobody named yields nothing, so nothing works a distance out
29973        // and the step is not there.
29974        assert!(ask(&mut f, b"@v:[VECTOR_RANGE 9 $vec]").ends_with(
29975            "+Result processors profile\r\n*1\r\n*6\r\n+Type\r\n+Index\r\n\
29976             +Time\r\n<t>\r\n+Results processed\r\n:4\r\n+Coordinator\r\n*0\r\n"
29977        ));
29978        // A nearest neighbour clause with nothing in front of it yields all
29979        // the same, so the step is there without a child above it.
29980        assert!(ask(&mut f, b"*=>[KNN 3 @v $vec]").contains("+Type\r\n+Metrics Applier\r\n"));
29981    }
29982
29983    /// A `LIMIT 0 0` on an aggregation is a client asking for the total and
29984    /// nothing else, so the step that would have paged the rows counts them
29985    /// instead, whether or not a `SORTBY` put an order in front of it.
29986    #[test]
29987    fn a_window_of_nothing_on_an_aggregation_counts_rather_than_pages() {
29988        let mut f = profiling();
29989        let steps = |f: &mut Fixture, words: &[&[u8]]| {
29990            let mut argv: Vec<&[u8]> = vec![b"FT.PROFILE", b"ix", b"AGGREGATE", b"QUERY", b"*"];
29991            argv.extend_from_slice(words);
29992            let reply = timeless(&f.run(&argv));
29993            let at = reply.find("+Result processors").expect("a list of steps");
29994            reply[at..].to_string()
29995        };
29996        assert_eq!(
29997            steps(&mut f, &[b"LIMIT", b"0", b"0"]),
29998            "+Result processors profile\r\n*2\r\n\
29999             *6\r\n+Type\r\n+Index\r\n+Time\r\n<t>\r\n+Results processed\r\n:3\r\n\
30000             *6\r\n+Type\r\n+Counter\r\n+Time\r\n<t>\r\n+Results processed\r\n:1\r\n\
30001             +Coordinator\r\n*0\r\n"
30002        );
30003        assert!(
30004            steps(
30005                &mut f,
30006                &[b"SORTBY", b"2", b"@n", b"ASC", b"LIMIT", b"0", b"0"]
30007            )
30008            .contains("+Type\r\n+Counter\r\n")
30009        );
30010        // A window that keeps something is still a window.
30011        assert!(steps(&mut f, &[b"LIMIT", b"0", b"2"]).contains(
30012            "+Type\r\n+Pager/Limiter\r\n+Time\r\n<t>\r\n\
30013             +Results processed\r\n:2\r\n"
30014        ));
30015    }
30016
30017    // ----------------------------------------------------------- spellcheck
30018
30019    /// The score is how many documents hold the suggestion over how many
30020    /// documents there are, and how close the suggestion is to the word does
30021    /// not come into it at all, so the nearer of the two words here is second.
30022    #[test]
30023    fn a_spellcheck_scores_a_suggestion_by_how_common_it_is() {
30024        let mut f = Fixture::new();
30025        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT", b"NOSTEM"]);
30026        f.run(&[b"HSET", b"d1", b"t", b"hello"]);
30027        f.run(&[b"HSET", b"d2", b"t", b"hallo hello"]);
30028        assert_eq!(
30029            f.run(&[b"FT.SPELLCHECK", b"e", b"hellp", b"DISTANCE", b"2"]),
30030            "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nhellp\r\n\
30031             *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"
30032        );
30033    }
30034
30035    /// On RESP3 the whole thing is wrapped in a map under one name, a word
30036    /// carries a list of one pair maps, and the score is a double rather than
30037    /// a string.
30038    #[test]
30039    fn a_spellcheck_answers_a_map_of_maps_on_resp3() {
30040        let mut f = Fixture::new();
30041        f.run(&[b"HELLO", b"3"]);
30042        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT", b"NOSTEM"]);
30043        f.run(&[b"HSET", b"d1", b"t", b"hello"]);
30044        assert_eq!(
30045            f.run(&[b"FT.SPELLCHECK", b"e", b"hellp"]),
30046            "%1\r\n$7\r\nresults\r\n%1\r\n$5\r\nhellp\r\n\
30047             *1\r\n%1\r\n$5\r\nhello\r\n,1\r\n"
30048        );
30049    }
30050
30051    /// A word the index already holds is not a mistake and is left out of the
30052    /// answer, and that check never looks at the field the query named, while
30053    /// the search for candidates does.
30054    #[test]
30055    fn a_word_the_index_holds_is_never_asked_about_whatever_field_it_names() {
30056        let mut f = Fixture::new();
30057        f.run(&[
30058            b"FT.CREATE",
30059            b"e",
30060            b"SCHEMA",
30061            b"a",
30062            b"TEXT",
30063            b"NOSTEM",
30064            b"b",
30065            b"TEXT",
30066            b"NOSTEM",
30067        ]);
30068        f.run(&[b"HSET", b"d1", b"b", b"world"]);
30069        assert_eq!(f.run(&[b"FT.SPELLCHECK", b"e", b"@a:world"]), "*0\r\n");
30070        assert_eq!(
30071            f.run(&[b"FT.SPELLCHECK", b"e", b"@a:worlt"]),
30072            "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nworlt\r\n*0\r\n"
30073        );
30074    }
30075
30076    /// A dictionary named by `INCLUDE` adds words the index never read, scored
30077    /// zero and reported in the spelling the dictionary was given, and one
30078    /// named by `EXCLUDE` says a word is spelled right after all.
30079    #[test]
30080    fn a_spellcheck_reads_the_dictionaries_it_is_pointed_at() {
30081        let mut f = Fixture::new();
30082        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT", b"NOSTEM"]);
30083        f.run(&[b"FT.DICTADD", b"d", b"Hellp", b"hellq"]);
30084        assert_eq!(
30085            f.run(&[b"FT.SPELLCHECK", b"e", b"hellz", b"TERMS", b"INCLUDE", b"d"]),
30086            "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nhellz\r\n\
30087             *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"
30088        );
30089        assert_eq!(
30090            f.run(&[b"FT.SPELLCHECK", b"e", b"hellq", b"TERMS", b"EXCLUDE", b"d"]),
30091            "*0\r\n"
30092        );
30093        assert_eq!(
30094            f.run(&[b"FT.SPELLCHECK", b"e", b"x", b"TERMS", b"INCLUDE", b"nope"]),
30095            "-Dict does not exist: nope\r\n"
30096        );
30097    }
30098
30099    /// The first `DISTANCE` counts and the rest are dropped, an argument
30100    /// nobody recognises is stepped over rather than refused, and a distance
30101    /// outside one to four is the one thing here that does fail.
30102    #[test]
30103    fn a_spellcheck_reads_its_arguments_leniently() {
30104        let mut f = Fixture::new();
30105        f.run(&[b"FT.CREATE", b"e", b"SCHEMA", b"t", b"TEXT", b"NOSTEM"]);
30106        f.run(&[b"HSET", b"d1", b"t", b"hello"]);
30107        let one = "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nhellp\r\n\
30108                   *1\r\n*2\r\n$1\r\n1\r\n$5\r\nhello\r\n";
30109        assert_eq!(f.run(&[b"FT.SPELLCHECK", b"e", b"hellp", b"BOGUS"]), one);
30110        let none = "*1\r\n*3\r\n$4\r\nTERM\r\n$5\r\nhelqp\r\n*0\r\n";
30111        let args: &[&[u8]] = &[
30112            b"FT.SPELLCHECK",
30113            b"e",
30114            b"helqp",
30115            b"DISTANCE",
30116            b"1",
30117            b"DISTANCE",
30118            b"4",
30119        ];
30120        assert_eq!(f.run(args), none);
30121        assert_eq!(
30122            f.run(&[b"FT.SPELLCHECK", b"e", b"hellp", b"DISTANCE", b"5"]),
30123            "-bad distance given, distance must be a natural number between 1 to 4\r\n"
30124        );
30125        assert_eq!(
30126            f.run(&[b"FT.SPELLCHECK", b"nope", b"hellp"]),
30127            "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n"
30128        );
30129    }
30130
30131    // -------------------------------------------------------------- suggest
30132
30133    /// The reply is the size of the dictionary afterwards, which is neither
30134    /// what was added nor whether anything changed.
30135    #[test]
30136    fn an_add_answers_how_many_suggestions_are_in_there_now() {
30137        let mut f = Fixture::new();
30138        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]), ":1\r\n");
30139        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"one", b"9"]), ":1\r\n");
30140        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"only", b"2"]), ":2\r\n");
30141        assert_eq!(f.run(&[b"FT.SUGLEN", b"s"]), ":2\r\n");
30142        assert_eq!(f.run(&[b"FT.SUGLEN", b"nokey"]), ":0\r\n");
30143    }
30144
30145    /// A suggestion dictionary is the one thing the search module puts in the
30146    /// keyspace, so every keyspace command reaches it.
30147    #[test]
30148    fn a_suggestion_dictionary_is_a_key_with_a_type_of_its_own() {
30149        let mut f = Fixture::new();
30150        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30151        assert_eq!(f.run(&[b"TYPE", b"s"]), "+trietype0\r\n");
30152        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$3\r\nraw\r\n");
30153        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
30154        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\ns\r\n");
30155        assert_eq!(f.run(&[b"EXPIRE", b"s", b"100"]), ":1\r\n");
30156        assert_eq!(f.run(&[b"TTL", b"s"]), ":100\r\n");
30157        assert_eq!(f.run(&[b"DEL", b"s"]), ":1\r\n");
30158        assert_eq!(f.run(&[b"FT.SUGLEN", b"s"]), ":0\r\n");
30159    }
30160
30161    /// The last suggestion out takes the key with it, which most module types
30162    /// do not do.
30163    #[test]
30164    fn deleting_the_last_suggestion_deletes_the_key() {
30165        let mut f = Fixture::new();
30166        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30167        assert_eq!(f.run(&[b"FT.SUGDEL", b"s", b"nope"]), ":0\r\n");
30168        assert_eq!(f.run(&[b"FT.SUGDEL", b"s", b"one"]), ":1\r\n");
30169        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
30170        assert_eq!(f.run(&[b"FT.SUGDEL", b"nokey", b"a"]), ":0\r\n");
30171    }
30172
30173    /// A key holding anything else is refused rather than overwritten, on all
30174    /// four of them.
30175    #[test]
30176    fn a_suggestion_command_on_another_kind_of_key_is_wrongtype() {
30177        let mut f = Fixture::new();
30178        f.run(&[b"SET", b"s", b"x"]);
30179        for cmd in [
30180            vec![&b"FT.SUGADD"[..], b"s", b"t", b"1"],
30181            vec![&b"FT.SUGGET"[..], b"s", b"t"],
30182            vec![&b"FT.SUGDEL"[..], b"s", b"t"],
30183            vec![&b"FT.SUGLEN"[..], b"s"],
30184        ] {
30185            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{cmd:?}");
30186        }
30187        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nx\r\n");
30188    }
30189
30190    /// The scores in here were read off a real server, single precision and
30191    /// all. An exact match answers a sentinel so it sorts in front.
30192    #[test]
30193    fn a_lookup_answers_a_score_it_works_out_rather_than_the_one_stored() {
30194        let mut f = Fixture::new();
30195        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30196        f.run(&[b"FT.SUGADD", b"s", b"only", b"2"]);
30197        f.run(&[b"FT.SUGADD", b"s", b"ontario", b"3"]);
30198        assert_eq!(
30199            f.run(&[b"FT.SUGGET", b"s", b"on", b"WITHSCORES"]),
30200            "*6\r\n$7\r\nontario\r\n$18\r\n1.2247449159622192\r\n\
30201             $4\r\nonly\r\n$17\r\n1.154700517654419\r\n\
30202             $3\r\none\r\n$18\r\n0.7071067690849304\r\n"
30203        );
30204        assert_eq!(
30205            f.run(&[b"FT.SUGGET", b"s", b"one", b"WITHSCORES"]),
30206            "*2\r\n$3\r\none\r\n$10\r\n2147483648\r\n"
30207        );
30208        assert_eq!(f.run(&[b"FT.SUGGET", b"nokey", b"a"]), "*0\r\n");
30209    }
30210
30211    /// `FUZZY` is one edit, and the edit is a rune rather than a byte.
30212    #[test]
30213    fn fuzzy_allows_one_edit_and_nothing_allows_two() {
30214        let mut f = Fixture::new();
30215        f.run(&[b"FT.SUGADD", b"s", b"only", b"2"]);
30216        assert_eq!(f.run(&[b"FT.SUGGET", b"s", b"one"]), "*0\r\n");
30217        assert_eq!(
30218            f.run(&[b"FT.SUGGET", b"s", b"one", b"FUZZY", b"WITHSCORES"]),
30219            "*2\r\n$4\r\nonly\r\n$19\r\n0.19139298796653748\r\n"
30220        );
30221        assert_eq!(f.run(&[b"FT.SUGGET", b"s", b"xyz", b"FUZZY"]), "*0\r\n");
30222    }
30223
30224    /// Five without a `MAX`, and the terms come back in score order.
30225    #[test]
30226    fn a_lookup_answers_five_unless_it_is_told_otherwise() {
30227        let mut f = Fixture::new();
30228        for (term, score) in [
30229            (&b"a1"[..], &b"1"[..]),
30230            (b"a2", b"2"),
30231            (b"a3", b"3"),
30232            (b"a4", b"4"),
30233            (b"a5", b"5"),
30234            (b"a6", b"6"),
30235        ] {
30236            f.run(&[b"FT.SUGADD", b"s", term, score]);
30237        }
30238        assert_eq!(
30239            f.run(&[b"FT.SUGGET", b"s", b"a"]),
30240            "*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"
30241        );
30242        assert_eq!(
30243            f.run(&[b"FT.SUGGET", b"s", b"a", b"MAX", b"2"]),
30244            "*2\r\n$2\r\na6\r\n$2\r\na5\r\n"
30245        );
30246        // A `MAX` larger than the dictionary answers what there is.
30247        assert!(
30248            f.run(&[b"FT.SUGGET", b"s", b"a", b"MAX", b"100"])
30249                .starts_with("*6\r\n")
30250        );
30251    }
30252
30253    /// A payload is replaced only when one is given, and an empty one is no
30254    /// payload at all.
30255    #[test]
30256    fn a_payload_comes_back_beside_the_term_or_a_null_does() {
30257        let mut f = Fixture::new();
30258        f.run(&[b"FT.SUGADD", b"s", b"one", b"1", b"PAYLOAD", b"p"]);
30259        assert_eq!(
30260            f.run(&[b"FT.SUGGET", b"s", b"o", b"WITHPAYLOADS"]),
30261            "*2\r\n$3\r\none\r\n$1\r\np\r\n"
30262        );
30263        f.run(&[b"FT.SUGADD", b"s", b"one", b"2"]);
30264        assert_eq!(
30265            f.run(&[b"FT.SUGGET", b"s", b"o", b"WITHPAYLOADS"]),
30266            "*2\r\n$3\r\none\r\n$1\r\np\r\n"
30267        );
30268        // An empty payload is the same as not having given one at all, so it
30269        // leaves the payload where it is rather than clearing it.
30270        f.run(&[b"FT.SUGADD", b"s", b"one", b"2", b"PAYLOAD", b""]);
30271        assert_eq!(
30272            f.run(&[b"FT.SUGGET", b"s", b"o", b"WITHPAYLOADS"]),
30273            "*2\r\n$3\r\none\r\n$1\r\np\r\n"
30274        );
30275        // A term that never had one answers a null.
30276        f.run(&[b"FT.SUGADD", b"s", b"other", b"1", b"PAYLOAD", b""]);
30277        assert_eq!(
30278            f.run(&[b"FT.SUGGET", b"s", b"ot", b"WITHPAYLOADS"]),
30279            "*2\r\n$5\r\nother\r\n$-1\r\n"
30280        );
30281    }
30282
30283    /// `INCR` adds to the score that is there rather than replacing it, and
30284    /// three tenths a tenth at a time is the reading that shows the score is
30285    /// held in single precision.
30286    #[test]
30287    fn incr_adds_to_the_score_that_is_already_there() {
30288        let mut f = Fixture::new();
30289        for _ in 0..3 {
30290            f.run(&[b"FT.SUGADD", b"s", b"xxx", b"0.1", b"INCR"]);
30291        }
30292        assert_eq!(
30293            f.run(&[b"FT.SUGGET", b"s", b"xx", b"WITHSCORES"]),
30294            "*2\r\n$3\r\nxxx\r\n$18\r\n0.2121320366859436\r\n"
30295        );
30296    }
30297
30298    /// The five error sentences, none of which are written the same way.
30299    #[test]
30300    fn the_suggestion_errors_are_the_lines_the_module_sends() {
30301        let mut f = Fixture::new();
30302        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30303        assert_eq!(
30304            f.run(&[b"FT.SUGADD", b"s", b"t", b"abc"]),
30305            "-ERR invalid score\r\n"
30306        );
30307        // The unknown word is complained about before the score is converted.
30308        assert_eq!(
30309            f.run(&[b"FT.SUGADD", b"s", b"t", b"abc", b"NOPE"]),
30310            "-Unknown argument `NOPE`\r\n"
30311        );
30312        assert_eq!(
30313            f.run(&[b"FT.SUGADD", b"s", b"t", b"1", b"PAYLOAD"]),
30314            "-Invalid payload: Expected an argument, but none provided\r\n"
30315        );
30316        // Too many words is an arity error and not an unknown argument.
30317        assert!(
30318            f.run(&[
30319                b"FT.SUGADD",
30320                b"s",
30321                b"t",
30322                b"1",
30323                b"PAYLOAD",
30324                b"a",
30325                b"PAYLOAD",
30326                b"b"
30327            ])
30328            .contains("wrong number of arguments")
30329        );
30330        assert_eq!(
30331            f.run(&[b"FT.SUGGET", b"s", b"o", b"NOPE"]),
30332            "-SEARCH_PARSE_ARGS Unrecognized argument: NOPE\r\n"
30333        );
30334        // A count read as a whole number and then found to be out of range,
30335        // against one that had to be read as a double first, where anything
30336        // under one is a conversion that failed rather than a range that did.
30337        for max in [&b"0"[..], b"-1", b"4294967296", b"1e10", b"inf"] {
30338            assert_eq!(
30339                f.run(&[b"FT.SUGGET", b"s", b"o", b"MAX", max]),
30340                "-SEARCH_PARSE_ARGS MAX: Value is outside acceptable bounds\r\n",
30341                "{}",
30342                String::from_utf8_lossy(max)
30343            );
30344        }
30345        for max in [
30346            &b"abc"[..],
30347            b"0.0",
30348            b"00",
30349            b"-0",
30350            b"+0",
30351            b"0.5",
30352            b"-1.5",
30353            b"1e400",
30354        ] {
30355            assert_eq!(
30356                f.run(&[b"FT.SUGGET", b"s", b"o", b"MAX", max]),
30357                "-SEARCH_PARSE_ARGS MAX: Could not convert argument to expected type\r\n",
30358                "{}",
30359                String::from_utf8_lossy(max)
30360            );
30361        }
30362        for max in [&b"01"[..], b"+1", b"1.5", b"0x10", b"1e2"] {
30363            assert_eq!(
30364                f.run(&[b"FT.SUGGET", b"s", b"o", b"MAX", max]),
30365                "*1\r\n$3\r\none\r\n",
30366                "{}",
30367                String::from_utf8_lossy(max)
30368            );
30369        }
30370        assert_eq!(
30371            f.run(&[b"FT.SUGGET", b"s", b"o", b"MAX"]),
30372            "-SEARCH_PARSE_ARGS MAX: Expected an argument, but none provided\r\n"
30373        );
30374        // A score too large for a double is refused where one spelled out is
30375        // taken, which is the module reading errno after the conversion.
30376        assert_eq!(
30377            f.run(&[b"FT.SUGADD", b"s", b"t", b"1e400"]),
30378            "-ERR invalid score\r\n"
30379        );
30380        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"t", b"inf"]), ":2\r\n");
30381    }
30382
30383    /// An empty term is taken and not stored, so the reply is the length that
30384    /// was already there and nothing new comes back. The key is still made,
30385    /// and a delete that finds nothing is what clears it away again.
30386    #[test]
30387    fn an_empty_suggestion_is_taken_and_dropped_but_still_makes_the_key() {
30388        let mut f = Fixture::new();
30389        f.run(&[b"FT.SUGADD", b"s", b"one", b"1"]);
30390        assert_eq!(f.run(&[b"FT.SUGADD", b"s", b"", b"1"]), ":1\r\n");
30391        assert_eq!(f.run(&[b"FT.SUGGET", b"s", b""]), "*1\r\n$3\r\none\r\n");
30392        assert_eq!(f.run(&[b"FT.SUGADD", b"e", b"", b"1"]), ":0\r\n");
30393        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":1\r\n");
30394        assert_eq!(f.run(&[b"TYPE", b"e"]), "+trietype0\r\n");
30395        assert_eq!(f.run(&[b"FT.SUGDEL", b"e", b"nothing"]), ":0\r\n");
30396        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
30397    }
30398
30399    /// A key that will not read is counted against the index and against the
30400    /// field, and `FT.INFO` says so.
30401    #[test]
30402    fn a_hash_that_will_not_read_is_counted_where_ft_info_reports_it() {
30403        let mut f = Fixture::new();
30404        f.run(&[
30405            b"FT.CREATE",
30406            b"ix",
30407            b"PREFIX",
30408            b"1",
30409            b"p:",
30410            b"SCHEMA",
30411            b"n",
30412            b"NUMERIC",
30413        ]);
30414        f.run(&[b"HSET", b"p:1", b"n", b"notanumber"]);
30415        assert_eq!(held(&f, b"ix"), (0, 0));
30416
30417        let reply = f.run(&[b"FT.INFO", b"ix"]);
30418        assert!(
30419            reply.contains("SEARCH_NUMERIC_VALUE_INVALID Invalid numeric value: 'notanumber'"),
30420            "{reply}"
30421        );
30422        assert!(reply.contains("hash_indexing_failures"), "{reply}");
30423    }
30424
30425    /// An index can only be made on database zero, and the check comes after
30426    /// the `IFNX` shortcut and before everything else.
30427    #[test]
30428    fn an_index_can_only_be_made_on_database_zero() {
30429        let mut f = Fixture::new();
30430        f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]);
30431        f.run(&[b"SELECT", b"1"]);
30432        let refused = "-Cannot create index on db != 0\r\n";
30433        assert_eq!(
30434            f.run(&[b"FT.CREATE", b"jx", b"SCHEMA", b"t", b"TEXT"]),
30435            refused
30436        );
30437        // The name is taken, and it still answers about the database.
30438        assert_eq!(
30439            f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]),
30440            refused
30441        );
30442        // And so does one whose arguments are nonsense.
30443        assert_eq!(
30444            f.run(&[b"FT.CREATE", b"zz", b"BOGUS", b"SCHEMA", b"t", b"TEXT"]),
30445            refused
30446        );
30447        // `IFNX` over a name that is taken is the one that gets through.
30448        assert_eq!(
30449            f.run(&[b"FT._CREATEIFNX", b"ix", b"SCHEMA", b"t", b"TEXT"]),
30450            "+OK\r\n"
30451        );
30452        assert_eq!(f.server.search.lock().len(), 1);
30453    }
30454
30455    /// The scan reads the database the create was run on, and after that the
30456    /// index follows its keys in every database.
30457    ///
30458    /// The asymmetry is a real server's, measured, and it is the sort of thing
30459    /// nobody would arrive at by choosing.
30460    #[test]
30461    fn the_scan_is_one_database_and_the_following_is_all_of_them() {
30462        let mut f = Fixture::new();
30463        f.run(&[b"SELECT", b"1"]);
30464        f.run(&[b"HSET", b"p:9", b"t", b"on one"]);
30465        f.run(&[b"SELECT", b"0"]);
30466        f.run(&[b"HSET", b"p:0", b"t", b"on zero"]);
30467        f.run(&[
30468            b"FT.CREATE",
30469            b"ix",
30470            b"PREFIX",
30471            b"1",
30472            b"p:",
30473            b"SCHEMA",
30474            b"t",
30475            b"TEXT",
30476        ]);
30477        assert_eq!(held(&f, b"ix"), (1, 1), "the scan read database zero only");
30478
30479        f.run(&[b"SELECT", b"1"]);
30480        f.run(&[b"HSET", b"p:8", b"t", b"later"]);
30481        assert_eq!(
30482            held(&f, b"ix"),
30483            (2, 2),
30484            "and then it follows every database"
30485        );
30486    }
30487
30488    /// Four documents over the two kinds of field a query can ask about, which
30489    /// is the corpus the searches below read.
30490    fn corpus(f: &mut Fixture) {
30491        f.run(&[
30492            b"FT.CREATE",
30493            b"sx",
30494            b"PREFIX",
30495            b"1",
30496            b"d:",
30497            b"SCHEMA",
30498            b"t",
30499            b"TEXT",
30500            b"g",
30501            b"TAG",
30502            b"n",
30503            b"NUMERIC",
30504        ]);
30505        for (key, text, tag, number) in [
30506            (b"d:1".as_slice(), "alpha beta", "aa,bb", "1"),
30507            (b"d:2", "alpha gamma", "bb", "2"),
30508            (b"d:3", "delta", "cc", "3"),
30509            (b"d:4", "alpha beta gamma", "aa,cc", "4"),
30510        ] {
30511            f.run(&[
30512                b"HSET",
30513                key,
30514                b"t",
30515                text.as_bytes(),
30516                b"g",
30517                tag.as_bytes(),
30518                b"n",
30519                number.as_bytes(),
30520            ]);
30521        }
30522    }
30523
30524    /// A corpus with something to sort by: a text field the index keeps a copy
30525    /// of, a number, the same text field under another name, and a text field
30526    /// the index keeps nothing of.
30527    fn sortable(f: &mut Fixture) {
30528        f.run(&[
30529            b"FT.CREATE",
30530            b"sy",
30531            b"PREFIX",
30532            b"1",
30533            b"s:",
30534            b"SCHEMA",
30535            b"t",
30536            b"TEXT",
30537            b"SORTABLE",
30538            b"n",
30539            b"NUMERIC",
30540            b"SORTABLE",
30541            b"body",
30542            b"AS",
30543            b"b",
30544            b"TEXT",
30545            b"SORTABLE",
30546            b"p",
30547            b"TEXT",
30548        ]);
30549        for (key, text, number) in [
30550            (b"s:1".as_slice(), "Banana Split", "2"),
30551            (b"s:2", "apple", "10"),
30552        ] {
30553            f.run(&[
30554                b"HSET",
30555                key,
30556                b"t",
30557                text.as_bytes(),
30558                b"n",
30559                number.as_bytes(),
30560                b"body",
30561                text.as_bytes(),
30562                b"p",
30563                b"alpha",
30564            ]);
30565        }
30566        // A key with nothing under either sortable field, which is what sorts
30567        // last whichever way round the sort runs.
30568        f.run(&[b"HSET", b"s:3", b"p", b"alpha"]);
30569    }
30570
30571    /// A sort runs off the copy of the value the index keeps, and a row with no
30572    /// value at all is last both ways round.
30573    #[test]
30574    fn a_search_sorts_by_a_field_the_index_keeps_a_copy_of() {
30575        let mut f = Fixture::new();
30576        sortable(&mut f);
30577        assert_eq!(
30578            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"n", b"NOCONTENT"]),
30579            "*4\r\n:3\r\n$3\r\ns:1\r\n$3\r\ns:2\r\n$3\r\ns:3\r\n"
30580        );
30581        assert_eq!(
30582            f.run(&[
30583                b"FT.SEARCH",
30584                b"sy",
30585                b"alpha",
30586                b"SORTBY",
30587                b"n",
30588                b"DESC",
30589                b"NOCONTENT"
30590            ]),
30591            "*4\r\n:3\r\n$3\r\ns:2\r\n$3\r\ns:1\r\n$3\r\ns:3\r\n"
30592        );
30593        // The copy of a text field is folded, so `apple` sorts before
30594        // `Banana Split` where a comparison of the bytes would not.
30595        assert_eq!(
30596            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"t", b"NOCONTENT"]),
30597            "*4\r\n:3\r\n$3\r\ns:2\r\n$3\r\ns:1\r\n$3\r\ns:3\r\n"
30598        );
30599    }
30600
30601    /// A field the index keeps no copy of is sorted by the value read off the
30602    /// key, which happens after the walk rather than during it.
30603    #[test]
30604    fn a_search_sorts_by_a_field_it_has_to_read_the_key_for() {
30605        let mut f = Fixture::new();
30606        sortable(&mut f);
30607        f.run(&[b"HSET", b"s:1", b"p", b"alpha zulu"]);
30608        assert_eq!(
30609            f.run(&[
30610                b"FT.SEARCH",
30611                b"sy",
30612                b"alpha",
30613                b"SORTBY",
30614                b"p",
30615                b"NOCONTENT",
30616                b"LIMIT",
30617                b"0",
30618                b"2"
30619            ]),
30620            "*3\r\n:3\r\n$3\r\ns:2\r\n$3\r\ns:3\r\n"
30621        );
30622        // Nothing is folded on this side, because the schema never asked for a
30623        // copy to fold, so the value goes into the sort as it was written.
30624        assert_eq!(
30625            f.run(&[
30626                b"FT.SEARCH",
30627                b"sy",
30628                b"alpha",
30629                b"SORTBY",
30630                b"p",
30631                b"WITHSORTKEYS",
30632                b"NOCONTENT",
30633                b"LIMIT",
30634                b"2",
30635                b"1"
30636            ]),
30637            "*3\r\n:3\r\n$3\r\ns:1\r\n$11\r\n$alpha zulu\r\n"
30638        );
30639    }
30640
30641    /// The value the sort compared goes beside every row, as a number after a
30642    /// hash, as text after a dollar, and as a null on a row that had none.
30643    #[test]
30644    fn a_search_can_send_the_value_it_sorted_by_back() {
30645        let mut f = Fixture::new();
30646        sortable(&mut f);
30647        assert_eq!(
30648            f.run(&[
30649                b"FT.SEARCH",
30650                b"sy",
30651                b"alpha",
30652                b"SORTBY",
30653                b"n",
30654                b"WITHSORTKEYS",
30655                b"NOCONTENT"
30656            ]),
30657            concat!(
30658                "*7\r\n:3\r\n",
30659                "$3\r\ns:1\r\n$2\r\n#2\r\n",
30660                "$3\r\ns:2\r\n$3\r\n#10\r\n",
30661                "$3\r\ns:3\r\n$-1\r\n"
30662            )
30663        );
30664        assert_eq!(
30665            f.run(&[
30666                b"FT.SEARCH",
30667                b"sy",
30668                b"alpha",
30669                b"SORTBY",
30670                b"t",
30671                b"WITHSORTKEYS",
30672                b"NOCONTENT"
30673            ]),
30674            concat!(
30675                "*7\r\n:3\r\n",
30676                "$3\r\ns:2\r\n$6\r\n$apple\r\n",
30677                "$3\r\ns:1\r\n$13\r\n$banana split\r\n",
30678                "$3\r\ns:3\r\n$-1\r\n"
30679            )
30680        );
30681        // Asking for a sort key without sorting is taken and answers a null on
30682        // every row, which is what a real server does.
30683        assert_eq!(
30684            f.run(&[
30685                b"FT.SEARCH",
30686                b"sy",
30687                b"banana",
30688                b"WITHSORTKEYS",
30689                b"NOCONTENT"
30690            ]),
30691            "*3\r\n:1\r\n$3\r\ns:1\r\n$-1\r\n"
30692        );
30693    }
30694
30695    /// The field a search sorted by is written in front of the fields of the
30696    /// key, and the key's own value for it wins when the two share a name.
30697    #[test]
30698    fn a_sort_puts_the_field_it_sorted_by_in_front_of_the_row() {
30699        let mut f = Fixture::new();
30700        sortable(&mut f);
30701        // `b` is what the schema calls the field the key calls `body`, so the
30702        // folded copy comes back under one name and the value as it was written
30703        // comes back under the other.
30704        assert_eq!(
30705            f.run(&[
30706                b"FT.SEARCH",
30707                b"sy",
30708                b"alpha",
30709                b"SORTBY",
30710                b"b",
30711                b"LIMIT",
30712                b"0",
30713                b"1"
30714            ]),
30715            concat!(
30716                "*3\r\n:3\r\n$3\r\ns:2\r\n*10\r\n",
30717                "$1\r\nb\r\n$5\r\napple\r\n",
30718                "$1\r\nt\r\n$5\r\napple\r\n",
30719                "$1\r\nn\r\n$2\r\n10\r\n",
30720                "$4\r\nbody\r\n$5\r\napple\r\n",
30721                "$1\r\np\r\n$5\r\nalpha\r\n"
30722            )
30723        );
30724        // With a `RETURN` list there is nothing to put in, so the field is moved
30725        // to the front of the names that were asked for instead.
30726        assert_eq!(
30727            f.run(&[
30728                b"FT.SEARCH",
30729                b"sy",
30730                b"alpha",
30731                b"SORTBY",
30732                b"b",
30733                b"RETURN",
30734                b"2",
30735                b"p",
30736                b"b",
30737                b"LIMIT",
30738                b"0",
30739                b"1"
30740            ]),
30741            concat!(
30742                "*3\r\n:3\r\n$3\r\ns:2\r\n*4\r\n",
30743                "$1\r\nb\r\n$5\r\napple\r\n",
30744                "$1\r\np\r\n$5\r\nalpha\r\n"
30745            )
30746        );
30747    }
30748
30749    /// The four ways a `SORTBY` on a search is refused.
30750    #[test]
30751    fn a_search_refuses_the_sorts_it_cannot_run() {
30752        let mut f = Fixture::new();
30753        sortable(&mut f);
30754        assert_eq!(
30755            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY"]),
30756            "-SEARCH_PARSE_ARGS Bad SORTBY arguments\r\n"
30757        );
30758        assert_eq!(
30759            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"n", b"SORTBY"]),
30760            "-SEARCH_PARSE_ARGS Multiple SORTBY steps are not allowed\r\n"
30761        );
30762        assert_eq!(
30763            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"n", b"MAX", b"2"]),
30764            "-SEARCH_PARSE_ARGS SORTBY MAX is not supported by FT.SEARCH\r\n"
30765        );
30766        assert_eq!(
30767            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"zz"]),
30768            "-SEARCH_PROP_NOT_FOUND Property `zz` not loaded nor in schema\r\n"
30769        );
30770        // The property is looked up once the whole list has read cleanly, so a
30771        // word after it that nobody knows is the error that comes back.
30772        assert_eq!(
30773            f.run(&[b"FT.SEARCH", b"sy", b"alpha", b"SORTBY", b"zz", b"NOPE"]),
30774            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `NOPE` at position 3 for <main>\r\n"
30775        );
30776    }
30777
30778    /// An index over two text fields, a number and a tag, holding one key whose
30779    /// `a` runs long enough to be worth cutting down and whose `b` and `g` hold
30780    /// nothing the query matches.
30781    fn marking(f: &mut Fixture) {
30782        f.run(&[
30783            b"FT.CREATE",
30784            b"mk",
30785            b"ON",
30786            b"HASH",
30787            b"PREFIX",
30788            b"1",
30789            b"m:",
30790            b"SCHEMA",
30791            b"a",
30792            b"TEXT",
30793            b"b",
30794            b"TEXT",
30795            b"n",
30796            b"NUMERIC",
30797            b"g",
30798            b"TAG",
30799        ]);
30800        f.run(&[
30801            b"HSET",
30802            b"m:1",
30803            b"a",
30804            b"c1 c2 c3 fox d1 d2 d3 d4 d5 d6 d7 d8 d9 fox e1 e2 e3",
30805            b"b",
30806            b"t1 t2 t3 t4 t5 t6 t7 t8",
30807            b"n",
30808            b"1",
30809            b"g",
30810            b"red",
30811        ]);
30812    }
30813
30814    /// A field the query matched comes back as fragments and a field it did not
30815    /// comes back as its own front.
30816    #[test]
30817    fn a_summarize_cuts_a_field_down_to_what_matched() {
30818        let mut f = Fixture::new();
30819        marking(&mut f);
30820        let got = f.run(&[b"FT.SEARCH", b"mk", b"fox", b"SUMMARIZE", b"LEN", b"2"]);
30821        assert!(got.contains("c3 fox d1 d2... d9 fox e1 e2... "), "{got}");
30822        // `b` holds no match, so it keeps its front and loses its last word.
30823        assert!(got.contains("t1 t2 t3 t4 t5 t6 t7\r\n"), "{got}");
30824        // And so does the tag, which is a value like any other to this clause.
30825        assert!(got.contains("$1\r\nr\r\n"), "{got}");
30826    }
30827
30828    /// `FRAGS` is applied before the context either side of a fragment is worked
30829    /// out, so the fragment that is left runs over the match of the one that was
30830    /// dropped rather than stopping on it.
30831    #[test]
30832    fn a_dropped_fragment_stops_bounding_the_one_that_was_kept() {
30833        let mut f = Fixture::new();
30834        marking(&mut f);
30835        let got = f.run(&[
30836            b"FT.SEARCH",
30837            b"mk",
30838            b"fox",
30839            b"SUMMARIZE",
30840            b"FRAGS",
30841            b"1",
30842            b"LEN",
30843            b"20",
30844        ]);
30845        assert!(
30846            got.contains("c2 c3 fox d1 d2 d3 d4 d5 d6 d7 d8 d9 fox e1 e2... "),
30847            "{got}"
30848        );
30849        // Keep both and the first stops on the second rather than running over
30850        // it, on the same query and the same budget.
30851        let two = f.run(&[
30852            b"FT.SEARCH",
30853            b"mk",
30854            b"fox",
30855            b"SUMMARIZE",
30856            b"FRAGS",
30857            b"2",
30858            b"LEN",
30859            b"20",
30860        ]);
30861        assert!(
30862            two.contains("c2 c3 fox d1 d2 d3 d4 d5 d6 d7 d8 d9... d1"),
30863            "{two}"
30864        );
30865    }
30866
30867    /// A `HIGHLIGHT` wraps every match, and on a field with no match in it the
30868    /// clause also calls off the cutting down a `SUMMARIZE` would have done.
30869    #[test]
30870    fn a_highlight_marks_the_matches_and_leaves_the_rest_of_the_field_alone() {
30871        let mut f = Fixture::new();
30872        marking(&mut f);
30873        let got = f.run(&[b"FT.SEARCH", b"mk", b"fox", b"HIGHLIGHT"]);
30874        assert!(got.contains("<b>fox</b> d1 d2"), "{got}");
30875        let both = f.run(&[
30876            b"FT.SEARCH",
30877            b"mk",
30878            b"fox",
30879            b"SUMMARIZE",
30880            b"LEN",
30881            b"2",
30882            b"HIGHLIGHT",
30883        ]);
30884        assert!(both.contains("c3 <b>fox</b> d1 d2... "), "{both}");
30885        // `b` still holds no match, and this time it comes back whole.
30886        assert!(both.contains("t1 t2 t3 t4 t5 t6 t7 t8\r\n"), "{both}");
30887        assert!(both.contains("$3\r\nred\r\n"), "{both}");
30888        // Naming a field one clause does not cover leaves it cut down again.
30889        let split = f.run(&[
30890            b"FT.SEARCH",
30891            b"mk",
30892            b"fox",
30893            b"SUMMARIZE",
30894            b"FIELDS",
30895            b"1",
30896            b"b",
30897            b"LEN",
30898            b"2",
30899            b"HIGHLIGHT",
30900            b"FIELDS",
30901            b"1",
30902            b"a",
30903        ]);
30904        assert!(split.contains("t1 t2 t3 t4 t5 t6 t7\r\n"), "{split}");
30905    }
30906
30907    /// A tag is never marked, in its own field or in a text field beside it.
30908    #[test]
30909    fn a_highlight_does_not_mark_a_tag() {
30910        let mut f = Fixture::new();
30911        marking(&mut f);
30912        f.run(&[b"HSET", b"m:1", b"b", b"red and blue"]);
30913        let got = f.run(&[b"FT.SEARCH", b"mk", b"@g:{red}", b"HIGHLIGHT"]);
30914        assert!(!got.contains("<b>"), "{got}");
30915        assert!(got.contains("red and blue"), "{got}");
30916    }
30917
30918    /// A search answers a total and then a row for every key in the window,
30919    /// with the fields of that key after it.
30920    #[test]
30921    fn a_search_answers_a_total_and_then_the_rows() {
30922        let mut f = Fixture::new();
30923        corpus(&mut f);
30924        assert_eq!(
30925            f.run(&[b"FT.SEARCH", b"sx", b"delta"]),
30926            "*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"
30927        );
30928        // The fields are what the key holds and not what the schema names, so
30929        // a field nobody indexed comes back too.
30930        f.run(&[b"HSET", b"d:3", b"extra", b"more"]);
30931        assert!(f.run(&[b"FT.SEARCH", b"sx", b"delta"]).contains("extra"));
30932        // `NOCONTENT` leaves the keys on their own, and `LIMIT 0 0` leaves
30933        // the total on its own.
30934        assert_eq!(
30935            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
30936            "*2\r\n:1\r\n$3\r\nd:3\r\n"
30937        );
30938        assert_eq!(
30939            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
30940            "*1\r\n:3\r\n"
30941        );
30942    }
30943
30944    /// The window is ten rows when nobody said, and the cap is on how wide it
30945    /// is rather than on where it starts.
30946    #[test]
30947    fn the_window_is_ten_rows_and_a_million_wide_at_most() {
30948        let mut f = Fixture::new();
30949        corpus(&mut f);
30950        assert_eq!(
30951            f.run(&[
30952                b"FT.SEARCH",
30953                b"sx",
30954                b"alpha",
30955                b"NOCONTENT",
30956                b"LIMIT",
30957                b"1",
30958                b"1"
30959            ]),
30960            "*2\r\n:3\r\n$3\r\nd:2\r\n"
30961        );
30962        assert_eq!(
30963            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0"]),
30964            "-SEARCH_PARSE_ARGS LIMIT requires two arguments\r\n"
30965        );
30966        assert_eq!(
30967            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"-1"]),
30968            "-SEARCH_PARSE_ARGS LIMIT needs two numeric arguments\r\n"
30969        );
30970        assert_eq!(
30971            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"1000001"]),
30972            "-SEARCH_LIMIT_OVER LIMIT exceeds maximum of 1000000\r\n"
30973        );
30974        assert_eq!(
30975            f.run(&[
30976                b"FT.SEARCH",
30977                b"sx",
30978                b"alpha",
30979                b"NOCONTENT",
30980                b"LIMIT",
30981                b"999999",
30982                b"1000000"
30983            ]),
30984            "*1\r\n:3\r\n"
30985        );
30986    }
30987
30988    /// `RETURN 0` reads on the wire like `NOCONTENT` and is not the same
30989    /// thing, because a later `RETURN` puts the fields back and a later
30990    /// `RETURN` after a `NOCONTENT` does not.
30991    #[test]
30992    fn a_return_of_nothing_is_not_the_same_as_nocontent() {
30993        let mut f = Fixture::new();
30994        corpus(&mut f);
30995        let bare = "*2\r\n:1\r\n$3\r\nd:3\r\n";
30996        assert_eq!(
30997            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"0"]),
30998            bare
30999        );
31000        assert_eq!(
31001            f.run(&[
31002                b"FT.SEARCH",
31003                b"sx",
31004                b"delta",
31005                b"NOCONTENT",
31006                b"RETURN",
31007                b"1",
31008                b"t"
31009            ]),
31010            bare
31011        );
31012        assert_eq!(
31013            f.run(&[
31014                b"FT.SEARCH",
31015                b"sx",
31016                b"delta",
31017                b"RETURN",
31018                b"0",
31019                b"RETURN",
31020                b"1",
31021                b"t"
31022            ]),
31023            "*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"
31024        );
31025    }
31026
31027    /// The count after `RETURN` counts words and not fields, so the `AS` and
31028    /// the name after it are two of them.
31029    #[test]
31030    fn the_count_after_return_counts_words() {
31031        let mut f = Fixture::new();
31032        corpus(&mut f);
31033        // Two words is one renamed field, and the name is the one it comes
31034        // back under.
31035        assert_eq!(
31036            f.run(&[
31037                b"FT.SEARCH",
31038                b"sx",
31039                b"delta",
31040                b"RETURN",
31041                b"3",
31042                b"t",
31043                b"AS",
31044                b"x"
31045            ]),
31046            "*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"
31047        );
31048        // A count that stops on the `AS` has nothing to rename to, and one
31049        // that reaches past the last word is short an argument.
31050        assert_eq!(
31051            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"2", b"t", b"AS"]),
31052            "-SEARCH_PARSE_ARGS RETURN path AS name - must be accompanied with NAME\r\n"
31053        );
31054        assert_eq!(
31055            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"3", b"t", b"AS"]),
31056            "-SEARCH_PARSE_ARGS Bad arguments for RETURN: Expected an argument, but none provided\r\n"
31057        );
31058        // A count that stops before the `AS` asks for a field called `AS`,
31059        // which no key holds, and a field the key does not hold is left out
31060        // rather than sent empty.
31061        assert_eq!(
31062            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"AS"]),
31063            "*3\r\n:1\r\n$3\r\nd:3\r\n*0\r\n"
31064        );
31065    }
31066
31067    /// A `FILTER` is a numeric range written outside the query, and it is only
31068    /// the wrong way round on a field the schema holds as a number.
31069    #[test]
31070    fn a_filter_is_a_range_written_outside_the_query() {
31071        let mut f = Fixture::new();
31072        corpus(&mut f);
31073        assert_eq!(
31074            f.run(&[
31075                b"FT.SEARCH",
31076                b"sx",
31077                b"alpha",
31078                b"NOCONTENT",
31079                b"FILTER",
31080                b"n",
31081                b"2",
31082                b"4"
31083            ]),
31084            "*3\r\n:2\r\n$3\r\nd:2\r\n$3\r\nd:4\r\n"
31085        );
31086        assert_eq!(
31087            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2"]),
31088            "-SEARCH_PARSE_ARGS FILTER requires 3 arguments\r\n"
31089        );
31090        assert_eq!(
31091            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"x", b"1"]),
31092            "-SEARCH_PARSE_ARGS Bad lower range: x\r\n"
31093        );
31094        assert_eq!(
31095            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2", b"1"]),
31096            "-SEARCH_SYNTAX Invalid numeric range (min > max): @n:[2.000000 1.000000]\r\n"
31097        );
31098        // The same range on a field that is not a number at all, and on a
31099        // field that is not there, answers nothing rather than refusing.
31100        for field in [b"g".as_slice(), b"nope"] {
31101            assert_eq!(
31102                f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", field, b"2", b"1"]),
31103                "*1\r\n:0\r\n"
31104            );
31105        }
31106    }
31107
31108    /// The index is resolved before the arguments after it are read, so a name
31109    /// that is not there answers about the name whatever else is wrong.
31110    #[test]
31111    fn the_index_is_found_before_the_arguments_are_read() {
31112        let mut f = Fixture::new();
31113        corpus(&mut f);
31114        let missing = "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n";
31115        assert_eq!(f.run(&[b"FT.SEARCH", b"nope", b"alpha", b"BOGUS"]), missing);
31116        assert_eq!(
31117            f.run(&[b"FT.EXPLAIN", b"nope", b"alpha", b"BOGUS"]),
31118            missing
31119        );
31120        // And the arguments are read before the query is, so a query that
31121        // will not parse still answers about the argument.
31122        assert_eq!(
31123            f.run(&[b"FT.SEARCH", b"sx", b"@@@", b"BOGUS"]),
31124            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `BOGUS` at position 1 for <main>\r\n"
31125        );
31126    }
31127
31128    /// `INKEYS` filters the answer before the total is taken, which is not
31129    /// where a client would guess it happens.
31130    #[test]
31131    fn inkeys_comes_off_the_total() {
31132        let mut f = Fixture::new();
31133        corpus(&mut f);
31134        assert_eq!(
31135            f.run(&[
31136                b"FT.SEARCH",
31137                b"sx",
31138                b"alpha",
31139                b"NOCONTENT",
31140                b"INKEYS",
31141                b"1",
31142                b"d:1"
31143            ]),
31144            "*2\r\n:1\r\n$3\r\nd:1\r\n"
31145        );
31146        assert_eq!(
31147            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"NOCONTENT", b"INKEYS", b"0"]),
31148            "*1\r\n:0\r\n"
31149        );
31150    }
31151
31152    /// The fields come from the database the session is on, and a row whose
31153    /// key will not load there is dropped from the reply and taken off the
31154    /// total.
31155    ///
31156    /// Measured against a real server, which follows a key on every database
31157    /// and then loads it from one.
31158    #[test]
31159    fn the_fields_are_read_from_the_session_database() {
31160        let mut f = Fixture::new();
31161        corpus(&mut f);
31162        f.run(&[b"SELECT", b"1"]);
31163        f.run(&[b"HSET", b"d:9", b"t", b"delta", b"n", b"9"]);
31164        // Both documents are in the index, and only one of them is in this
31165        // database.
31166        assert_eq!(
31167            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
31168            "*3\r\n:2\r\n$3\r\nd:3\r\n$3\r\nd:9\r\n"
31169        );
31170        assert_eq!(
31171            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
31172            "*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"
31173        );
31174    }
31175
31176    /// The deeper protocol answers a map of five rather than an array, with
31177    /// every row a map of its own.
31178    #[test]
31179    fn the_third_protocol_answers_a_map_of_five() {
31180        let mut f = Fixture::new();
31181        corpus(&mut f);
31182        f.out = Out::new(Proto::Resp3);
31183        assert_eq!(
31184            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
31185            concat!(
31186                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
31187                "%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",
31188                "+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
31189            )
31190        );
31191    }
31192
31193    /// A window of nothing is a client asking for the count on its own, and a
31194    /// window of nothing that starts somewhere else is a contradiction all
31195    /// three commands refuse in the same words.
31196    #[test]
31197    fn a_window_of_nothing_has_to_start_at_the_top() {
31198        let mut f = Fixture::new();
31199        corpus(&mut f);
31200        let refused = "-SEARCH_LIMIT_OVER The `offset` of the LIMIT must be 0 when `num` is 0\r\n";
31201        assert_eq!(
31202            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
31203            refused
31204        );
31205        assert_eq!(
31206            f.run(&[b"FT.EXPLAIN", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
31207            refused
31208        );
31209        assert_eq!(
31210            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
31211            refused
31212        );
31213        assert_eq!(
31214            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
31215            "*1\r\n:3\r\n"
31216        );
31217    }
31218
31219    /// An aggregation answers a count and then a list of properties for every
31220    /// row, which is empty until something asks for a field.
31221    #[test]
31222    fn an_aggregation_answers_a_count_and_then_the_properties() {
31223        let mut f = Fixture::new();
31224        corpus(&mut f);
31225        assert_eq!(
31226            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha"]),
31227            "*4\r\n:1\r\n*0\r\n*0\r\n*0\r\n"
31228        );
31229        // Every row, and not the ten a search would have cut it down to. The
31230        // count in front of them is one because that is how far the reply had
31231        // got when it was written, which is measured against a real server.
31232        assert_eq!(
31233            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"1", b"@t"]),
31234            concat!(
31235                "*4\r\n:1\r\n*2\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
31236                "*2\r\n$1\r\nt\r\n$11\r\nalpha gamma\r\n",
31237                "*2\r\n$1\r\nt\r\n$16\r\nalpha beta gamma\r\n"
31238            )
31239        );
31240        // Ascending document number, because nothing sorts the answer. The
31241        // second and fourth documents are the ones the window lands on and the
31242        // best scoring one is not among them.
31243        assert_eq!(
31244            f.run(&[
31245                b"FT.AGGREGATE",
31246                b"sx",
31247                b"alpha",
31248                b"LOAD",
31249                b"1",
31250                b"@n",
31251                b"LIMIT",
31252                b"1",
31253                b"2"
31254            ]),
31255            "*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"
31256        );
31257        // A query nothing answers is a count of nothing and no rows at all.
31258        assert_eq!(
31259            f.run(&[b"FT.AGGREGATE", b"sx", b"nope", b"LOAD", b"1", b"@t"]),
31260            "*1\r\n:0\r\n"
31261        );
31262    }
31263
31264    /// `LOAD` counts words rather than fields, names the property after the
31265    /// path unless an `AS` renames it, and reads everything the key holds when
31266    /// it is given a star.
31267    #[test]
31268    fn a_load_counts_words_and_can_rename_what_it_reads() {
31269        let mut f = Fixture::new();
31270        corpus(&mut f);
31271        // Three words, which are the path, the `AS` and the name.
31272        assert_eq!(
31273            f.run(&[
31274                b"FT.AGGREGATE",
31275                b"sx",
31276                b"alpha",
31277                b"LOAD",
31278                b"3",
31279                b"@t",
31280                b"AS",
31281                b"text"
31282            ]),
31283            concat!(
31284                "*4\r\n:1\r\n*2\r\n$4\r\ntext\r\n$10\r\nalpha beta\r\n",
31285                "*2\r\n$4\r\ntext\r\n$11\r\nalpha gamma\r\n",
31286                "*2\r\n$4\r\ntext\r\n$16\r\nalpha beta gamma\r\n"
31287            )
31288        );
31289        assert_eq!(
31290            f.run(&[
31291                b"FT.AGGREGATE",
31292                b"sx",
31293                b"alpha",
31294                b"LOAD",
31295                b"*",
31296                b"LIMIT",
31297                b"0",
31298                b"1"
31299            ]),
31300            concat!(
31301                "*2\r\n:1\r\n*6\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
31302                "$1\r\ng\r\n$5\r\naa,bb\r\n$1\r\nn\r\n$1\r\n1\r\n"
31303            )
31304        );
31305        // A field the key does not hold is left out rather than sent empty.
31306        assert_eq!(
31307            f.run(&[
31308                b"FT.AGGREGATE",
31309                b"sx",
31310                b"alpha",
31311                b"LOAD",
31312                b"2",
31313                b"@n",
31314                b"@nope",
31315                b"LIMIT",
31316                b"0",
31317                b"2"
31318            ]),
31319            "*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"
31320        );
31321    }
31322
31323    /// The `LOAD` grammar, which has four ways to go wrong and one of them is
31324    /// only reported once the rest of the argument list has read cleanly.
31325    #[test]
31326    fn a_load_refuses_a_count_it_cannot_use() {
31327        let mut f = Fixture::new();
31328        corpus(&mut f);
31329        let head = "-SEARCH_PARSE_ARGS Bad arguments for LOAD: ";
31330        assert_eq!(
31331            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"x"]),
31332            format!("{head}Expected number of fields or `*`\r\n")
31333        );
31334        assert_eq!(
31335            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"-1", b"@t"]),
31336            format!("{head}Value is outside acceptable bounds\r\n")
31337        );
31338        assert_eq!(
31339            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"5", b"@t"]),
31340            format!("{head}Expected an argument, but none provided\r\n")
31341        );
31342        assert_eq!(
31343            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD"]),
31344            format!("{head}Expected an argument, but none provided\r\n")
31345        );
31346        // A count that runs out on the `AS` is held back, because the word
31347        // after it is read as an argument of its own and may be worth an error
31348        // of its own. Nothing follows here, so the held back line is the one.
31349        assert_eq!(
31350            f.run(&[
31351                b"FT.AGGREGATE",
31352                b"sx",
31353                b"alpha",
31354                b"LOAD",
31355                b"2",
31356                b"@t",
31357                b"AS"
31358            ]),
31359            "-SEARCH_PARSE_ARGS LOAD path AS name - must be accompanied with NAME\r\n"
31360        );
31361        // And here the word after it is one an aggregation stops taking once a
31362        // step has been read, so that is what the client hears about.
31363        assert_eq!(
31364            f.run(&[
31365                b"FT.AGGREGATE",
31366                b"sx",
31367                b"alpha",
31368                b"LOAD",
31369                b"2",
31370                b"@t",
31371                b"AS",
31372                b"VERBATIM"
31373            ]),
31374            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 5 for <main>\r\n"
31375        );
31376        // A `LOAD 0` is a step that names nothing. It shuts the same door
31377        // without becoming a loader, so the count stays the one a query with no
31378        // `LOAD` gets.
31379        assert_eq!(
31380            f.run(&[
31381                b"FT.AGGREGATE",
31382                b"sx",
31383                b"alpha",
31384                b"LOAD",
31385                b"0",
31386                b"LIMIT",
31387                b"0",
31388                b"1"
31389            ]),
31390            "*2\r\n:1\r\n*0\r\n"
31391        );
31392    }
31393
31394    /// Reading a step of the pipeline stops the words about the search itself
31395    /// being taken, and `LIMIT` and `TIMEOUT` are not steps.
31396    #[test]
31397    fn a_pipeline_step_closes_the_door_on_the_search_words() {
31398        let mut f = Fixture::new();
31399        corpus(&mut f);
31400        assert_eq!(
31401            f.run(&[
31402                b"FT.AGGREGATE",
31403                b"sx",
31404                b"alpha",
31405                b"LOAD",
31406                b"1",
31407                b"@t",
31408                b"VERBATIM"
31409            ]),
31410            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 4 for <main>\r\n"
31411        );
31412        assert_eq!(
31413            f.run(&[
31414                b"FT.AGGREGATE",
31415                b"sx",
31416                b"alpha",
31417                b"LIMIT",
31418                b"0",
31419                b"1",
31420                b"VERBATIM"
31421            ]),
31422            "*2\r\n:1\r\n*0\r\n"
31423        );
31424        // Three words a search takes that this command names in its refusal
31425        // rather than calling them unknown.
31426        for word in [b"RETURN".as_slice(), b"SUMMARIZE", b"HIGHLIGHT"] {
31427            let name = core::str::from_utf8(word).expect("the three words are text");
31428            assert_eq!(
31429                f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", word]),
31430                format!("-SEARCH_PARSE_ARGS {name} is not supported on FT.AGGREGATE\r\n")
31431            );
31432        }
31433    }
31434
31435    /// `ADDSCORES` writes the score as a property to twelve significant digits
31436    /// where `WITHSCORES` writes it beside the row in full.
31437    #[test]
31438    fn addscores_writes_a_shorter_score_than_withscores() {
31439        let mut f = Fixture::new();
31440        corpus(&mut f);
31441        assert_eq!(
31442            f.run(&[
31443                b"FT.AGGREGATE",
31444                b"sx",
31445                b"alpha",
31446                b"ADDSCORES",
31447                b"LOAD",
31448                b"1",
31449                b"@n",
31450                b"LIMIT",
31451                b"0",
31452                b"2"
31453            ]),
31454            concat!(
31455                "*3\r\n:1\r\n",
31456                "*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",
31457                "*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"
31458            )
31459        );
31460        // `NOCONTENT` takes the properties away and leaves whatever was asked
31461        // for beside them, and a sort key is always null because nothing sorts
31462        // by one yet.
31463        assert_eq!(
31464            f.run(&[
31465                b"FT.AGGREGATE",
31466                b"sx",
31467                b"alpha",
31468                b"NOCONTENT",
31469                b"WITHSCORES",
31470                b"LIMIT",
31471                b"0",
31472                b"2"
31473            ]),
31474            "*3\r\n:1\r\n$18\r\n0.3566749439387324\r\n$18\r\n0.3566749439387324\r\n"
31475        );
31476        assert_eq!(
31477            f.run(&[
31478                b"FT.AGGREGATE",
31479                b"sx",
31480                b"alpha",
31481                b"WITHSORTKEYS",
31482                b"LOAD",
31483                b"1",
31484                b"@n",
31485                b"LIMIT",
31486                b"0",
31487                b"1"
31488            ]),
31489            "*3\r\n:1\r\n$-1\r\n*2\r\n$1\r\nn\r\n$1\r\n1\r\n"
31490        );
31491    }
31492
31493    /// The one scorer that has to see the whole answer first turns the count
31494    /// into the real total and hands the rows back backwards.
31495    #[test]
31496    fn a_normalising_scorer_answers_the_rows_backwards() {
31497        let mut f = Fixture::new();
31498        corpus(&mut f);
31499        assert_eq!(
31500            f.run(&[
31501                b"FT.AGGREGATE",
31502                b"sx",
31503                b"alpha",
31504                b"SCORER",
31505                b"BM25STD.NORM",
31506                b"ADDSCORES",
31507                b"LOAD",
31508                b"1",
31509                b"@n",
31510                b"LIMIT",
31511                b"1",
31512                b"2"
31513            ]),
31514            concat!(
31515                "*3\r\n:3\r\n",
31516                "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n2\r\n",
31517                "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n1\r\n"
31518            )
31519        );
31520        // Without `ADDSCORES` nothing on the row needs the score, so the rows
31521        // come back the way every other query answers them.
31522        assert_eq!(
31523            f.run(&[
31524                b"FT.AGGREGATE",
31525                b"sx",
31526                b"alpha",
31527                b"SCORER",
31528                b"BM25STD.NORM",
31529                b"LOAD",
31530                b"1",
31531                b"@n",
31532                b"LIMIT",
31533                b"1",
31534                b"2"
31535            ]),
31536            "*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"
31537        );
31538    }
31539
31540    /// The deeper protocol answers the same map of five a search answers, with
31541    /// the `id` gone because an aggregation is about the properties.
31542    #[test]
31543    fn an_aggregation_answers_a_map_of_five_as_well() {
31544        let mut f = Fixture::new();
31545        corpus(&mut f);
31546        f.out = Out::new(Proto::Resp3);
31547        assert_eq!(
31548            f.run(&[
31549                b"FT.AGGREGATE",
31550                b"sx",
31551                b"alpha",
31552                b"ADDSCORES",
31553                b"WITHSCORES",
31554                b"WITHSORTKEYS",
31555                b"LOAD",
31556                b"1",
31557                b"@n",
31558                b"LIMIT",
31559                b"0",
31560                b"1"
31561            ]),
31562            concat!(
31563                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
31564                "%4\r\n+score\r\n,0.3566749439387324\r\n+sortkey\r\n_\r\n",
31565                "+extra_attributes\r\n%2\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n",
31566                "$1\r\nn\r\n$1\r\n1\r\n+values\r\n*0\r\n",
31567                "+total_results\r\n:1\r\n+warning\r\n*0\r\n"
31568            )
31569        );
31570        // The count is worked out from the rows the reply reached under this
31571        // protocol, where under RESP2 it is worked out from the first of them.
31572        assert_eq!(
31573            f.run(&[
31574                b"FT.AGGREGATE",
31575                b"sx",
31576                b"alpha",
31577                b"NOCONTENT",
31578                b"LIMIT",
31579                b"0",
31580                b"1"
31581            ]),
31582            concat!(
31583                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
31584                "%1\r\n+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
31585            )
31586        );
31587    }
31588    // ------------------------------------------------------------- CLIENT
31589
31590    /// The field names `CLIENT INFO` reports, in the order 8.10.1 reports them.
31591    ///
31592    /// Written out rather than derived, because the whole point of the command
31593    /// is that a parser somewhere else knows this list, so a change to it is a
31594    /// change a test should have to be edited for.
31595    const INFO_FIELDS: &[&str] = &[
31596        "id",
31597        "addr",
31598        "laddr",
31599        "fd",
31600        "name",
31601        "age",
31602        "idle",
31603        "flags",
31604        "db",
31605        "sub",
31606        "psub",
31607        "ssub",
31608        "multi",
31609        "watch",
31610        "qbuf",
31611        "qbuf-free",
31612        "argv-mem",
31613        "multi-mem",
31614        "rbs",
31615        "rbp",
31616        "obl",
31617        "oll",
31618        "omem",
31619        "omem-shared",
31620        "omem-unshared",
31621        "tot-mem",
31622        "events",
31623        "cmd",
31624        "user",
31625        "redir",
31626        "resp",
31627        "lib-name",
31628        "lib-ver",
31629        "io-thread",
31630        "tot-net-in",
31631        "tot-net-out",
31632        "tot-cmds",
31633        "read-events",
31634        "avg-pipeline-len-sum",
31635        "avg-pipeline-len-cnt",
31636    ];
31637
31638    /// The report as a list of name and value pairs, taken out of the bulk
31639    /// string the reply is on RESP2.
31640    fn client_info(f: &mut Fixture) -> Vec<(String, String)> {
31641        let reply = f.run(&[b"CLIENT", b"INFO"]);
31642        let body = reply.split_once("\r\n").expect("a bulk header").1;
31643        // A verbatim string on RESP3 carries its format in front of the text,
31644        // and the same reply is a plain bulk string on RESP2.
31645        let line = body.trim_end_matches("\r\n").trim_start_matches("txt:");
31646        assert!(
31647            line.ends_with('\n'),
31648            "the report ends in a newline: {line:?}"
31649        );
31650        line.trim_end()
31651            .split(' ')
31652            .map(|pair| {
31653                let (name, value) = pair.split_once('=').expect("name=value");
31654                (name.to_string(), value.to_string())
31655            })
31656            .collect()
31657    }
31658
31659    /// One field of the report.
31660    fn client_field(f: &mut Fixture, name: &str) -> String {
31661        client_info(f)
31662            .into_iter()
31663            .find(|(n, _)| n == name)
31664            .map(|(_, v)| v)
31665            .unwrap_or_else(|| panic!("no {name} field"))
31666    }
31667
31668    #[test]
31669    fn client_info_names_every_field_a_real_server_names() {
31670        let mut f = Fixture::new();
31671        let got: Vec<String> = client_info(&mut f).into_iter().map(|(n, _)| n).collect();
31672        assert_eq!(got, INFO_FIELDS);
31673    }
31674
31675    /// A session nobody told about a socket is what an embedded caller gets, and
31676    /// it has to answer rather than pretend to have an address.
31677    #[test]
31678    fn a_connection_with_no_socket_reports_no_address_and_no_descriptor() {
31679        let mut f = Fixture::new();
31680        assert_eq!(client_field(&mut f, "addr"), "");
31681        assert_eq!(client_field(&mut f, "laddr"), "");
31682        assert_eq!(client_field(&mut f, "fd"), "-1");
31683        assert_eq!(client_field(&mut f, "id"), "7");
31684    }
31685
31686    #[test]
31687    fn client_setname_takes_a_name_back_and_refuses_one_with_a_space_in_it() {
31688        let mut f = Fixture::new();
31689        assert_eq!(f.run(&[b"CLIENT", b"GETNAME"]), "$-1\r\n");
31690        assert_eq!(f.run(&[b"CLIENT", b"SETNAME", b"worker"]), "+OK\r\n");
31691        assert_eq!(f.run(&[b"CLIENT", b"GETNAME"]), "$6\r\nworker\r\n");
31692        assert_eq!(client_field(&mut f, "name"), "worker");
31693        assert_eq!(
31694            f.run(&[b"CLIENT", b"SETNAME", b"two words"]),
31695            "-ERR Client names cannot contain spaces, newlines or special characters.\r\n"
31696        );
31697        // And the name it had is still the name it has.
31698        assert_eq!(f.run(&[b"CLIENT", b"GETNAME"]), "$6\r\nworker\r\n");
31699    }
31700
31701    /// `RESET` is `clearClientConnectionState`, and the surprising half of it is
31702    /// what it keeps: the library behind the socket is the same library it was.
31703    #[test]
31704    fn reset_clears_the_name_and_the_switches_and_keeps_the_library() {
31705        let mut f = Fixture::new();
31706        f.run(&[b"CLIENT", b"SETNAME", b"worker"]);
31707        f.run(&[b"CLIENT", b"SETINFO", b"LIB-NAME", b"yo-py"]);
31708        f.run(&[b"CLIENT", b"SETINFO", b"LIB-VER", b"1.2.3"]);
31709        f.run(&[b"CLIENT", b"NO-EVICT", b"on"]);
31710        f.run(&[b"CLIENT", b"NO-TOUCH", b"on"]);
31711        assert_eq!(client_field(&mut f, "flags"), "eT");
31712
31713        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
31714        assert_eq!(client_field(&mut f, "name"), "");
31715        assert_eq!(client_field(&mut f, "flags"), "N");
31716        assert_eq!(client_field(&mut f, "lib-name"), "yo-py");
31717        assert_eq!(client_field(&mut f, "lib-ver"), "1.2.3");
31718    }
31719
31720    #[test]
31721    fn client_setinfo_complains_the_way_a_real_server_does() {
31722        let mut f = Fixture::new();
31723        assert_eq!(
31724            f.run(&[b"CLIENT", b"SETINFO", b"LIB-NAME"]),
31725            "-ERR wrong number of arguments for 'client|setinfo' command\r\n"
31726        );
31727        assert_eq!(
31728            f.run(&[b"CLIENT", b"SETINFO", b"NOPE", b"x"]),
31729            "-ERR Unrecognized option 'NOPE'\r\n"
31730        );
31731        assert_eq!(
31732            f.run(&[b"CLIENT", b"SETINFO", b"lib-name", b"ok x"]),
31733            "-ERR lib-name cannot contain spaces, newlines or special characters.\r\n"
31734        );
31735        assert_eq!(
31736            f.run(&[b"CLIENT", b"SETINFO", b"LIB-VER", b"has space"]),
31737            "-ERR lib-ver cannot contain spaces, newlines or special characters.\r\n"
31738        );
31739    }
31740
31741    #[test]
31742    fn client_refuses_a_subcommand_it_does_not_have_and_arguments_it_did_not_ask_for() {
31743        let mut f = Fixture::new();
31744        assert_eq!(
31745            f.run(&[b"CLIENT", b"NOPE"]),
31746            "-ERR unknown subcommand 'NOPE'. Try CLIENT HELP.\r\n"
31747        );
31748        assert_eq!(
31749            f.run(&[b"CLIENT", b"GETNAME", b"extra"]),
31750            "-ERR wrong number of arguments for 'client|getname' command\r\n"
31751        );
31752        assert_eq!(
31753            f.run(&[b"CLIENT", b"NO-EVICT", b"maybe"]),
31754            "-ERR syntax error\r\n"
31755        );
31756        assert_eq!(
31757            f.run(&[b"CLIENT", b"REPLY", b"BAD"]),
31758            "-ERR syntax error\r\n"
31759        );
31760    }
31761
31762    /// The three subscribe namespaces are counted apart, which is not the same
31763    /// count a subscribe reply carries: that one puts channels and patterns
31764    /// together.
31765    #[test]
31766    fn client_info_counts_the_three_subscribe_namespaces_apart() {
31767        let mut f = Fixture::new();
31768        // On RESP3, because a subscribed RESP2 connection may only send nine
31769        // commands and `CLIENT` is not one of them.
31770        f.run(&[b"HELLO", b"3"]);
31771        f.run(&[b"SUBSCRIBE", b"a", b"b"]);
31772        f.run(&[b"PSUBSCRIBE", b"p*"]);
31773        f.run(&[b"SSUBSCRIBE", b"s"]);
31774        let info = client_info(&mut f);
31775        let at = |name: &str| {
31776            info.iter()
31777                .find(|(n, _)| n == name)
31778                .map(|(_, v)| v.clone())
31779                .unwrap()
31780        };
31781        assert_eq!(at("sub"), "2");
31782        assert_eq!(at("psub"), "1");
31783        assert_eq!(at("ssub"), "1");
31784        assert_eq!(at("flags"), "P");
31785        forget_session(&f.server, &mut f.session);
31786    }
31787
31788    /// The `cmd` field names the subcommand, which for this command is always
31789    /// `client|info` and is the one field that reports the command asking.
31790    #[test]
31791    fn client_info_reports_itself_as_the_command_running() {
31792        let mut f = Fixture::new();
31793        assert_eq!(client_field(&mut f, "cmd"), "client|info");
31794        f.run(&[b"GET", b"nothing"]);
31795        // Still `client|info`, because the field is about the command asking
31796        // and the command asking is this one.
31797        assert_eq!(client_field(&mut f, "cmd"), "client|info");
31798    }
31799
31800    /// A container called in mixed case is still the same command underneath.
31801    #[test]
31802    fn the_command_field_is_lower_case_however_the_client_spelled_it() {
31803        let mut f = Fixture::new();
31804        let reply = f.run(&[b"CLIENT", b"Info"]);
31805        assert!(reply.contains("cmd=client|info"), "{reply}");
31806    }
31807
31808    #[test]
31809    fn client_help_lists_the_subcommands_that_are_here() {
31810        let mut f = Fixture::new();
31811        let reply = f.run(&[b"CLIENT", b"HELP"]);
31812        for sub in ["ID", "GETNAME", "SETNAME", "SETINFO", "INFO", "REPLY"] {
31813            assert!(reply.contains(sub), "no {sub} in {reply}");
31814        }
31815        // And not the ones that are not, since a client reads this to find out
31816        // what it can send.
31817        assert!(!reply.contains("TRACKING"), "{reply}");
31818    }
31819
31820    // ------------------------------------------------- what crosses to a replica
31821
31822    /// The stream, split back into the commands it is made of.
31823    ///
31824    /// A replica reads this with the same parser it reads a client with, so a
31825    /// test can read it the same way, and a list of words is what the rewrite
31826    /// table in the spec is written in.
31827    fn commands(stream: &str) -> Vec<Vec<String>> {
31828        let mut out = Vec::new();
31829        let mut rest = stream;
31830        while let Some(tail) = rest.strip_prefix('*') {
31831            let (n, tail) = tail.split_once("\r\n").expect("a header ends");
31832            let mut one = Vec::new();
31833            let mut tail = tail;
31834            for _ in 0..n.parse::<usize>().expect("a count") {
31835                let body = tail.strip_prefix('$').expect("a bulk string");
31836                let (len, body) = body.split_once("\r\n").expect("a length ends");
31837                let len: usize = len.parse().expect("a length");
31838                one.push(body[..len].to_string());
31839                tail = &body[len + 2..];
31840            }
31841            out.push(one);
31842            rest = tail;
31843        }
31844        assert!(rest.is_empty(), "left over: {rest:?}");
31845        out
31846    }
31847
31848    /// The words of the one command a test expects to have crossed.
31849    fn only(stream: &str) -> Vec<String> {
31850        let mut each = commands(stream);
31851        assert_eq!(each.len(), 1, "expected one command: {stream:?}");
31852        each.pop().expect("one command")
31853    }
31854
31855    #[test]
31856    fn the_stream_opens_with_a_select_and_says_it_once() {
31857        let mut f = Fixture::replicated();
31858        assert_eq!(
31859            commands(&f.crossed(&[b"SET", b"k", b"v"])),
31860            vec![
31861                vec!["SELECT".to_string(), "0".to_string()],
31862                vec!["SET".to_string(), "k".to_string(), "v".to_string()],
31863            ]
31864        );
31865        // The second write is on the same database, so it goes on its own.
31866        assert_eq!(only(&f.crossed(&[b"SET", b"k2", b"v"])), ["SET", "k2", "v"]);
31867        // A different one says so first, and the SELECT is not the client's,
31868        // which crossed nothing on its own.
31869        f.run(&[b"SELECT", b"3"]);
31870        assert_eq!(
31871            commands(&f.crossed(&[b"SET", b"k3", b"v"])),
31872            vec![
31873                vec!["SELECT".to_string(), "3".to_string()],
31874                vec!["SET".to_string(), "k3".to_string(), "v".to_string()],
31875            ]
31876        );
31877    }
31878
31879    /// The deadline is read back off the key rather than worked out twice.
31880    ///
31881    /// So what crosses is the instant this server picked, and a replica that
31882    /// applies it an hour later still expires the key at the same moment.
31883    #[test]
31884    fn a_relative_deadline_crosses_as_the_instant_it_resolved_to() {
31885        let mut f = Fixture::replicated();
31886        f.crossed(&[b"SET", b"seed", b"1"]);
31887        for parts in [
31888            &[b"SET".as_slice(), b"k", b"v", b"EX", b"100"][..],
31889            &[b"SETEX".as_slice(), b"k", b"100", b"v"][..],
31890        ] {
31891            let words = only(&f.crossed(parts));
31892            assert_eq!(&words[..3], ["SET", "k", "v"], "{words:?}");
31893            assert_eq!(words[3], "PXAT", "{words:?}");
31894            let at: i64 = words[4].parse().expect("an instant");
31895            assert!(at > f.server.clock.now_ms() as i64, "{words:?}");
31896        }
31897        for parts in [
31898            &[b"EXPIRE".as_slice(), b"k", b"50"][..],
31899            &[b"PEXPIRE".as_slice(), b"k", b"50000"][..],
31900            &[b"EXPIREAT".as_slice(), b"k", b"99999999999"][..],
31901            &[b"GETEX".as_slice(), b"k", b"EX", b"100"][..],
31902        ] {
31903            let words = only(&f.crossed(parts));
31904            assert_eq!(&words[..2], ["PEXPIREAT", "k"], "{words:?}");
31905        }
31906        assert_eq!(
31907            only(&f.crossed(&[b"GETEX", b"k", b"PERSIST"])),
31908            ["PERSIST", "k"]
31909        );
31910    }
31911
31912    /// A read sends nothing, and neither does a write that was refused.
31913    #[test]
31914    fn nothing_crosses_for_a_read_or_for_a_failure() {
31915        let mut f = Fixture::replicated();
31916        f.crossed(&[b"SET", b"k", b"v"]);
31917        for parts in [
31918            &[b"GET".as_slice(), b"k"][..],
31919            &[b"TYPE".as_slice(), b"k"][..],
31920            &[b"STRLEN".as_slice(), b"k"][..],
31921            &[b"EXISTS".as_slice(), b"k"][..],
31922            &[b"PING".as_slice()][..],
31923            // Refused, and a refusal leaves the stream alone whatever the body
31924            // pushed before it found out.
31925            &[b"LPUSH".as_slice(), b"k", b"a"][..],
31926            &[b"INCR".as_slice(), b"k"][..],
31927        ] {
31928            assert_eq!(f.crossed(parts), "", "{parts:?}");
31929        }
31930    }
31931
31932    /// A write that changed nothing still crosses, which is D-140.
31933    ///
31934    /// Redis decides with a counter of real changes and sends nothing when it
31935    /// did not move. There is no such counter here yet, so what is sent is what
31936    /// can be said without one: an accepted write goes down the link. The ones
31937    /// whose verbatim form would be wrong rather than merely wasteful already
31938    /// say so for themselves, which is the second half of this.
31939    #[test]
31940    fn a_write_that_changed_nothing_still_crosses() {
31941        let mut f = Fixture::replicated();
31942        f.crossed(&[b"SET", b"k", b"v"]);
31943        assert_eq!(
31944            only(&f.crossed(&[b"DEL", b"nosuchkey"])),
31945            ["DEL", "nosuchkey"]
31946        );
31947        assert_eq!(only(&f.crossed(&[b"SET", b"k", b"v"])), ["SET", "k", "v"]);
31948        // And the ones that would be wrong say nothing, whatever the rule above.
31949        for parts in [
31950            &[b"SPOP".as_slice(), b"nosuchset"][..],
31951            &[b"EXPIRE".as_slice(), b"nosuchkey", b"100"][..],
31952            &[
31953                b"XADD".as_slice(),
31954                b"nosuchstream",
31955                b"NOMKSTREAM",
31956                b"*",
31957                b"f",
31958                b"v",
31959            ][..],
31960        ] {
31961            assert_eq!(f.crossed(parts), "", "{parts:?}");
31962        }
31963    }
31964
31965    /// A conditional write crosses as the plain one, since the condition was
31966    /// decided here and a replica has no business deciding it again.
31967    #[test]
31968    fn a_condition_that_held_crosses_without_it() {
31969        let mut f = Fixture::replicated();
31970        f.crossed(&[b"SET", b"seed", b"1"]);
31971        assert_eq!(only(&f.crossed(&[b"SETNX", b"k", b"v"])), ["SET", "k", "v"]);
31972        assert_eq!(
31973            only(&f.crossed(&[b"SET", b"k", b"w", b"XX"])),
31974            ["SET", "k", "w"]
31975        );
31976    }
31977
31978    /// A write whose result depends on where it ran crosses as the result.
31979    #[test]
31980    fn a_random_or_derived_write_crosses_as_what_it_did() {
31981        let mut f = Fixture::replicated();
31982        f.crossed(&[b"SET", b"seed", b"1"]);
31983        f.crossed(&[b"SADD", b"s", b"one", b"two"]);
31984        let words = only(&f.crossed(&[b"SPOP", b"s"]));
31985        assert_eq!(&words[..2], ["SREM", "s"], "{words:?}");
31986        assert!(words[2] == "one" || words[2] == "two", "{words:?}");
31987        // The one that took the last member crosses as the key going, since
31988        // that is what happened and a set with nothing in it does not exist.
31989        assert_eq!(only(&f.crossed(&[b"SPOP", b"s"])), ["DEL", "s"]);
31990        f.crossed(&[b"SET", b"n", b"1"]);
31991        assert_eq!(
31992            only(&f.crossed(&[b"INCRBYFLOAT", b"n", b"1.5"])),
31993            ["SET", "n", "2.5", "KEEPTTL"]
31994        );
31995        assert_eq!(only(&f.crossed(&[b"GETDEL", b"n"])), ["DEL", "n"]);
31996        let words = only(&f.crossed(&[b"XADD", b"st", b"*", b"f", b"v"]));
31997        assert_eq!(&words[..2], ["XADD", "st"], "{words:?}");
31998        assert_ne!(words[2], "*", "an auto id has to be resolved: {words:?}");
31999        assert_eq!(&words[3..], ["f", "v"], "{words:?}");
32000    }
32001
32002    /// A key that went on its own crosses as the deletion, ahead of whatever the
32003    /// command that noticed was doing.
32004    ///
32005    /// A replica never expires anything itself, so this is the only way it hears
32006    /// about it, and the order matters: the write that follows would be refused
32007    /// by a replica still holding the old key at the old type.
32008    #[test]
32009    fn an_expiry_a_read_noticed_crosses_as_a_deletion() {
32010        let mut f = Fixture::replicated();
32011        f.crossed(&[b"SET", b"k", b"v", b"PX", b"50"]);
32012        f.advance(100);
32013        assert_eq!(only(&f.crossed(&[b"GET", b"k"])), ["DEL", "k"]);
32014        // And the deletion goes first when the command had something of its own.
32015        f.run(&[b"SET", b"k2", b"v", b"PX", b"50"]);
32016        f.crossed(&[b"PING"]);
32017        f.advance(100);
32018        assert_eq!(
32019            commands(&f.crossed(&[b"LPUSH", b"k2", b"a"])),
32020            vec![
32021                vec!["DEL".to_string(), "k2".to_string()],
32022                vec!["LPUSH".to_string(), "k2".to_string(), "a".to_string()],
32023            ]
32024        );
32025    }
32026
32027    /// A command that parked has done nothing, so nothing crosses.
32028    ///
32029    /// What must never cross is the command as it arrived, since a replica told
32030    /// to `BLPOP` would stop and wait on the one connection that cannot stop.
32031    #[test]
32032    fn a_blocking_command_that_parked_crosses_nothing() {
32033        let mut f = Fixture::replicated();
32034        f.crossed(&[b"SET", b"seed", b"1"]);
32035        assert_eq!(f.flow(&[b"BLPOP", b"gone", b"0"]).0, Flow::Block);
32036        assert_eq!(f.server.stream_since(f.mark).0, "");
32037        f.run(&[b"XADD", b"st", b"1-1", b"f", b"v"]);
32038        f.run(&[b"XGROUP", b"CREATE", b"st", b"g", b"$"]);
32039        f.crossed(&[b"PING"]);
32040        // A group read that read nothing is in the same position, and this one
32041        // does not even park.
32042        f.run(&[
32043            b"XREADGROUP",
32044            b"GROUP",
32045            b"g",
32046            b"c",
32047            b"COUNT",
32048            b"1",
32049            b"STREAMS",
32050            b"st",
32051            b">",
32052        ]);
32053        assert_eq!(
32054            only(&f.crossed(&[b"PING"])),
32055            ["XGROUP", "CREATECONSUMER", "st", "g", "c"]
32056        );
32057    }
32058
32059    /// `XGROUP` carries its write flag on its subcommands, which are not in the
32060    /// table yet, so each arm says for itself what it did.
32061    #[test]
32062    fn every_xgroup_subcommand_that_changed_something_crosses() {
32063        let mut f = Fixture::replicated();
32064        f.crossed(&[b"XADD", b"st", b"1-1", b"f", b"v"]);
32065        // The dollar is resolved here, because by the time a replica reads it
32066        // the stream it means is a different length.
32067        assert_eq!(
32068            only(&f.crossed(&[b"XGROUP", b"CREATE", b"st", b"g", b"$"])),
32069            ["XGROUP", "CREATE", "st", "g", "1-1"]
32070        );
32071        assert_eq!(
32072            only(&f.crossed(&[b"XGROUP", b"CREATECONSUMER", b"st", b"g", b"c"])),
32073            ["XGROUP", "CREATECONSUMER", "st", "g", "c"]
32074        );
32075        assert_eq!(
32076            only(&f.crossed(&[b"XGROUP", b"SETID", b"st", b"g", b"0"])),
32077            ["XGROUP", "SETID", "st", "g", "0-0"]
32078        );
32079        assert_eq!(
32080            only(&f.crossed(&[b"XGROUP", b"DELCONSUMER", b"st", b"g", b"c"])),
32081            ["XGROUP", "DELCONSUMER", "st", "g", "c"]
32082        );
32083        assert_eq!(
32084            only(&f.crossed(&[b"XGROUP", b"DESTROY", b"st", b"g"])),
32085            ["XGROUP", "DESTROY", "st", "g"]
32086        );
32087        // And one that changed nothing crosses nothing.
32088        assert_eq!(f.crossed(&[b"XGROUP", b"DESTROY", b"st", b"g"]), "");
32089    }
32090
32091    /// A publish crosses even though it is not a write and touches no key.
32092    ///
32093    /// A client subscribed to a replica is subscribed to the whole server, so
32094    /// it has to hear what was published on the master.
32095    #[test]
32096    fn a_publish_crosses_with_nobody_listening() {
32097        let mut f = Fixture::replicated();
32098        f.crossed(&[b"SET", b"seed", b"1"]);
32099        assert_eq!(
32100            only(&f.crossed(&[b"PUBLISH", b"news", b"hello"])),
32101            ["PUBLISH", "news", "hello"]
32102        );
32103        assert_eq!(
32104            only(&f.crossed(&[b"SPUBLISH", b"news", b"hello"])),
32105            ["SPUBLISH", "news", "hello"]
32106        );
32107    }
32108
32109    /// A replica that lost the link for a moment is given the bytes it missed.
32110    ///
32111    /// The number it sends is the position of the first byte it wants counted
32112    /// from one, so a replica that has everything asks for one past the end.
32113    /// Reading that as a count of bytes written instead is an off by one that
32114    /// turns every reconnect into a full resync, which is exactly what a real
32115    /// replica did until this was fixed.
32116    #[test]
32117    fn a_replica_asking_to_carry_on_is_caught_up_from_the_backlog() {
32118        let mut f = Fixture::replicated();
32119        f.crossed(&[b"SET", b"k", b"v"]);
32120        let id = f.server.repl_id();
32121        let had = f.mark;
32122        f.crossed(&[b"SET", b"k2", b"later"]);
32123        let asked = (had + 1).to_string();
32124        let reply = f.run(&[b"PSYNC", &id, asked.as_bytes()]);
32125        assert!(reply.starts_with("+CONTINUE "), "{reply}");
32126        assert!(reply.contains("later"), "{reply}");
32127        // And what it already had is not sent twice.
32128        assert_eq!(reply.matches("k2").count(), 1, "{reply}");
32129    }
32130
32131    /// A replica with nothing to carry on from is sent the whole dataset.
32132    #[test]
32133    fn a_replica_with_no_history_is_sent_a_snapshot() {
32134        let mut f = Fixture::replicated();
32135        f.crossed(&[b"SET", b"k", b"v"]);
32136        let reply = f.run(&[b"PSYNC", b"?", b"-1"]);
32137        assert!(reply.starts_with("+FULLRESYNC "), "{reply}");
32138        // The header, then the image as a bulk string with no newline after it.
32139        let body = reply.split_once("\r\n").expect("a header ends").1;
32140        assert!(body.starts_with('$'), "{body:?}");
32141        assert!(!body.ends_with("\r\n"), "{body:?}");
32142    }
32143
32144    // ------------------------------------------------------ being a replica
32145
32146    /// The whole point of the read only refusal, and the read that goes through.
32147    #[test]
32148    fn a_read_only_replica_refuses_a_write_and_answers_a_read() {
32149        let mut f = Fixture::new();
32150        f.run(&[b"SET", b"k", b"v"]);
32151        f.server.pretend_following("127.0.0.1", 6379, true);
32152        assert_eq!(
32153            f.run(&[b"SET", b"k", b"other"]),
32154            "-READONLY You can't write against a read only replica.\r\n"
32155        );
32156        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
32157        // And a command that is not a write at all is not touched by any of it.
32158        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
32159    }
32160
32161    /// The refusal is off on a server that is nobody's replica, whatever the
32162    /// setting says, because the setting is about being a replica.
32163    #[test]
32164    fn a_master_takes_writes_however_the_read_only_setting_is_left() {
32165        let mut f = Fixture::new();
32166        f.server.set_replica_read_only(true);
32167        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
32168        f.server.pretend_following("127.0.0.1", 6379, true);
32169        assert!(f.run(&[b"SET", b"k", b"v"]).starts_with("-READONLY"));
32170        // And a replica that was told it is writable takes the write.
32171        f.server.set_replica_read_only(false);
32172        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
32173        f.server.set_replica_read_only(true);
32174        // Stopping being a replica is enough on its own, with the setting left
32175        // exactly where it was.
32176        f.server.pretend_master();
32177        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
32178    }
32179
32180    /// The master's own connection is what the refusal is not for.
32181    #[test]
32182    fn the_link_to_the_master_writes_through_the_read_only_refusal() {
32183        let mut f = Fixture::new();
32184        f.server.pretend_following("127.0.0.1", 6379, true);
32185        assert!(f.run(&[b"SET", b"k", b"v"]).starts_with("-READONLY"));
32186        f.session.serve_master(true);
32187        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
32188        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
32189    }
32190
32191    /// A refused `EXEC` fails the whole transaction rather than one command in
32192    /// it, which is the same rule every other gate in `resolved` follows.
32193    #[test]
32194    fn a_transaction_on_a_read_only_replica_is_refused_whole() {
32195        let mut f = Fixture::new();
32196        f.server.pretend_following("127.0.0.1", 6379, true);
32197        f.run(&[b"MULTI"]);
32198        assert!(f.run(&[b"SET", b"k", b"v"]).starts_with("-READONLY"));
32199        assert!(f.run(&[b"EXEC"]).starts_with("-EXECABORT"));
32200    }
32201
32202    /// What an operator reads to find out who this server is following.
32203    #[test]
32204    fn a_replica_says_who_it_follows_in_info_and_in_role() {
32205        let mut f = Fixture::new();
32206        assert!(f.run(&[b"INFO", b"replication"]).contains("role:master"));
32207        f.server.pretend_following("10.0.0.4", 7000, true);
32208        let info = f.run(&[b"INFO", b"replication"]);
32209        assert!(info.contains("role:slave"), "{info}");
32210        assert!(info.contains("master_host:10.0.0.4"), "{info}");
32211        assert!(info.contains("master_port:7000"), "{info}");
32212        assert!(info.contains("master_link_status:up"), "{info}");
32213        assert!(info.contains("slave_read_only:1"), "{info}");
32214        // The five element replica form, and the state word is the one that
32215        // tells an operator whether anything is arriving.
32216        let role = f.run(&[b"ROLE"]);
32217        assert!(role.starts_with("*5\r\n$5\r\nslave\r\n"), "{role}");
32218        assert!(role.contains("10.0.0.4"), "{role}");
32219        assert!(role.contains("connected"), "{role}");
32220        // A link that is down says so in both places rather than in one.
32221        f.server.pretend_following("10.0.0.4", 7000, false);
32222        assert!(
32223            f.run(&[b"INFO", b"replication"])
32224                .contains("master_link_status:down"),
32225            "a link that is not up is down"
32226        );
32227        assert!(f.run(&[b"ROLE"]).contains("connect"));
32228    }
32229
32230    /// A server nobody wrapped in a handle cannot start a link, and says so
32231    /// rather than answering `OK` and doing nothing.
32232    #[test]
32233    fn replicaof_on_an_embedded_server_says_it_is_not_available() {
32234        let mut f = Fixture::new();
32235        let said = f.run(&[b"REPLICAOF", b"127.0.0.1", b"6379"]);
32236        assert!(
32237            said.contains("not available on an embedded server"),
32238            "{said}"
32239        );
32240        // The arity and the port are checked first, so a caller that got the
32241        // command wrong hears about that and not about the handle.
32242        assert!(
32243            f.run(&[b"REPLICAOF", b"127.0.0.1"])
32244                .starts_with("-ERR wrong number")
32245        );
32246        assert_eq!(
32247            f.run(&[b"SLAVEOF", b"127.0.0.1", b"abc"]),
32248            "-ERR Invalid master port\r\n"
32249        );
32250        assert_eq!(
32251            f.run(&[b"REPLICAOF", b"127.0.0.1", b"99999"]),
32252            "-ERR Invalid master port\r\n"
32253        );
32254    }
32255
32256    /// Promotion keeps the history it was part of, which is what lets the
32257    /// replicas that shared it carry on rather than start again.
32258    #[test]
32259    fn a_promotion_keeps_the_old_history_as_the_second_id() {
32260        let f = Fixture::new();
32261        let was = f.server.repl_id();
32262        f.server.promote();
32263        assert_ne!(f.server.repl_id(), was);
32264        let info = {
32265            let mut f = f;
32266            f.run(&[b"INFO", b"replication"])
32267        };
32268        let was = String::from_utf8_lossy(&was).into_owned();
32269        assert!(info.contains(&format!("master_replid2:{was}")), "{info}");
32270    }
32271
32272    /// `DEBUG CHANGE-REPL-ID` is the opposite: a new history and no claim on the
32273    /// old one, so the next `PSYNC` between two servers that shared it is full.
32274    #[test]
32275    fn change_repl_id_takes_a_new_id_and_forgets_the_old_one() {
32276        let mut f = Fixture::new();
32277        let was = f.server.repl_id();
32278        f.server.promote();
32279        assert_eq!(f.run(&[b"DEBUG", b"CHANGE-REPL-ID"]), "+OK\r\n");
32280        assert_ne!(f.server.repl_id(), was);
32281        let info = f.run(&[b"INFO", b"replication"]);
32282        assert!(
32283            info.contains(&format!("master_replid2:{}", "0".repeat(40))),
32284            "{info}"
32285        );
32286    }
32287
32288    /// Every way of getting `FAILOVER` wrong, in the order a real server checks
32289    /// them, because the order is what a script sees when it gets two things
32290    /// wrong at once.
32291    #[test]
32292    fn failover_refuses_in_the_order_the_reference_refuses() {
32293        let mut f = Fixture::new();
32294        // Nothing going on, so ABORT has nothing to abort.
32295        assert_eq!(
32296            f.run(&[b"FAILOVER", b"ABORT"]),
32297            "-ERR No failover in progress.\r\n"
32298        );
32299        // The parsing comes before any of the state checks, and a timeout of
32300        // nought or less has a sentence of its own rather than being a syntax
32301        // error.
32302        assert_eq!(
32303            f.run(&[b"FAILOVER", b"TIMEOUT", b"0"]),
32304            "-ERR FAILOVER timeout must be greater than 0\r\n"
32305        );
32306        assert_eq!(
32307            f.run(&[b"FAILOVER", b"TIMEOUT", b"-1"]),
32308            "-ERR FAILOVER timeout must be greater than 0\r\n"
32309        );
32310        assert!(
32311            f.run(&[b"FAILOVER", b"TIMEOUT", b"abc"])
32312                .starts_with("-ERR value is not an integer")
32313        );
32314        // Each word is taken at most once, so a second one is a syntax error and
32315        // not an overwrite, and anything unrecognised is one too.
32316        assert_eq!(f.run(&[b"FAILOVER", b"bogus"]), "-ERR syntax error\r\n");
32317        assert_eq!(
32318            f.run(&[b"FAILOVER", b"TIMEOUT", b"1", b"TIMEOUT", b"2"]),
32319            "-ERR syntax error\r\n"
32320        );
32321        assert_eq!(
32322            f.run(&[b"FAILOVER", b"FORCE", b"FORCE"]),
32323            "-ERR syntax error\r\n"
32324        );
32325        // TO wants both of its words, so one word short of it is a syntax error
32326        // rather than a target with a missing port.
32327        assert_eq!(f.run(&[b"FAILOVER", b"TO", b"h"]), "-ERR syntax error\r\n");
32328        // ABORT is only ABORT when it is the whole command.
32329        assert_eq!(
32330            f.run(&[b"FAILOVER", b"ABORT", b"TIMEOUT", b"1"]),
32331            "-ERR syntax error\r\n"
32332        );
32333        // Then the state checks. Nobody is following this server, so there is
32334        // nobody to hand the job to, and that is asked before FORCE is.
32335        assert_eq!(
32336            f.run(&[b"FAILOVER"]),
32337            "-ERR FAILOVER requires connected replicas.\r\n"
32338        );
32339        assert_eq!(
32340            f.run(&[b"FAILOVER", b"FORCE"]),
32341            "-ERR FAILOVER requires connected replicas.\r\n"
32342        );
32343        // A replica has nothing of its own to give away.
32344        f.server.pretend_following("10.0.0.4", 7000, true);
32345        assert_eq!(
32346            f.run(&[b"FAILOVER"]),
32347            "-ERR FAILOVER is not valid when server is a replica.\r\n"
32348        );
32349    }
32350
32351    /// The state word `INFO` reports, which is what an operator watching a
32352    /// handover reads, and which is `no-failover` on a server that is not in one.
32353    #[test]
32354    fn a_server_that_is_not_failing_over_says_no_failover() {
32355        let mut f = Fixture::new();
32356        assert!(
32357            f.run(&[b"INFO", b"replication"])
32358                .contains("master_failover_state:no-failover"),
32359            "the field is there and says nothing is going on"
32360        );
32361    }
32362
32363    /// A transaction crosses as the commands it ran, which is D-141: a real
32364    /// server wraps them in `MULTI` and `EXEC`.
32365    #[test]
32366    fn a_transaction_crosses_as_its_commands() {
32367        let mut f = Fixture::replicated();
32368        f.crossed(&[b"SET", b"seed", b"1"]);
32369        f.run(&[b"MULTI"]);
32370        f.run(&[b"SET", b"a", b"1"]);
32371        f.run(&[b"INCR", b"a"]);
32372        assert_eq!(
32373            commands(&f.crossed(&[b"EXEC"])),
32374            vec![
32375                vec!["SET".to_string(), "a".to_string(), "1".to_string()],
32376                vec!["INCR".to_string(), "a".to_string()],
32377            ]
32378        );
32379    }
32380
32381    /// A cluster node owning every slot, with a second node in the table that
32382    /// nobody has met, which is the only way a redirection can fire before the
32383    /// bus is in.
32384    ///
32385    /// The slot `foo` lands in, read off a real server.
32386    const FOO: u16 = 12182;
32387
32388    /// The slot `bar` lands in, which is a different one and is the whole point.
32389    const BAR: u16 = 5061;
32390
32391    fn clustered() -> Fixture {
32392        let mut server = Server::new();
32393        server.enable_cluster("", 7000);
32394        server.cluster_own_everything();
32395        let other = server.cluster_pretend_node(
32396            "5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f",
32397            "10.0.0.9",
32398            7002,
32399        );
32400        assert_eq!(other, 1, "the made up node is the second one in the table");
32401        Fixture::on(server)
32402    }
32403
32404    /// The runs come out in slot order and not in node order, which is the
32405    /// order a real server walks and the order a client that caches the reply
32406    /// by position is counting on.
32407    #[test]
32408    fn cluster_slots_comes_out_in_slot_order() {
32409        let mut f = clustered();
32410        for slot in 0..100u16 {
32411            f.server.cluster_hand_over(slot, 1);
32412        }
32413        let reply = f.run(&[b"CLUSTER", b"SLOTS"]);
32414        assert!(
32415            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"),
32416            "the other node's run is first because it starts at slot 0: {reply}"
32417        );
32418        assert!(
32419            reply.contains("*3\r\n:100\r\n:16383\r\n"),
32420            "and this node's run is the rest of them: {reply}"
32421        );
32422    }
32423
32424    /// A key in a slot somebody else owns is a redirection and not an answer.
32425    #[test]
32426    fn a_key_on_another_node_is_moved_there() {
32427        let mut f = clustered();
32428        f.server.cluster_hand_over(BAR, 1);
32429        assert_eq!(
32430            f.run(&[b"GET", b"bar"]),
32431            format!("-MOVED {BAR} 10.0.0.9:7002\r\n")
32432        );
32433        // Every other slot is still this node's, so nothing about them moves.
32434        assert_eq!(f.run(&[b"GET", b"foo"]), "$-1\r\n");
32435    }
32436
32437    /// A command that names no key never redirects, whatever the table says,
32438    /// which is what lets a client talk to any node at all.
32439    #[test]
32440    fn a_command_with_no_keys_never_redirects() {
32441        let mut f = clustered();
32442        for slot in 0..16384u16 {
32443            f.server.cluster_hand_over(slot, 1);
32444        }
32445        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
32446        assert_eq!(f.run(&[b"ECHO", b"hi"]), "$2\r\nhi\r\n");
32447    }
32448
32449    /// Two keys in two slots cannot be served by anybody, so the client is told
32450    /// that rather than being sent somewhere that would only fail again.
32451    #[test]
32452    fn two_slots_in_one_command_is_a_cross_slot() {
32453        let mut f = clustered();
32454        assert_eq!(
32455            f.run(&[b"MGET", b"foo", b"bar"]),
32456            "-CROSSSLOT Keys in request don't hash to the same slot\r\n"
32457        );
32458        // The same two keys with a tag that puts them together are fine.
32459        assert_eq!(
32460            f.run(&[b"MGET", b"{t}foo", b"{t}bar"]),
32461            "*2\r\n$-1\r\n$-1\r\n"
32462        );
32463    }
32464
32465    /// A hole in the table beats everything, including the two slots, because a
32466    /// real server works out the first key's node before it looks at the rest.
32467    #[test]
32468    fn a_hole_is_reported_before_the_cross_slot() {
32469        let mut server = Server::new();
32470        server.enable_cluster("", 7000);
32471        let mut f = Fixture::on(server);
32472        assert_eq!(
32473            f.run(&[b"MGET", b"foo", b"bar"]),
32474            "-CLUSTERDOWN Hash slot not served\r\n"
32475        );
32476        // And with the slots back it is the two slots again.
32477        f.server.cluster_own_everything();
32478        assert_eq!(
32479            f.run(&[b"MGET", b"foo", b"bar"]),
32480            "-CROSSSLOT Keys in request don't hash to the same slot\r\n"
32481        );
32482    }
32483
32484    /// A slot on its way out sends a client on for the keys that have gone and
32485    /// answers for the ones that are still here, which is what makes a slot move
32486    /// without a window where a key is on neither node.
32487    #[test]
32488    fn a_migrating_slot_asks_for_the_keys_that_have_gone() {
32489        let mut f = clustered();
32490        f.run(&[b"SET", b"foo", b"1"]);
32491        f.server.cluster_moving(FOO, Some(1), None);
32492        // Still here, so this node answers.
32493        assert_eq!(f.run(&[b"GET", b"foo"]), "$1\r\n1\r\n");
32494        // Gone, so the client is sent on for this one command only.
32495        assert_eq!(
32496            f.run(&[b"GET", b"{foo}gone"]),
32497            format!("-ASK {FOO} 10.0.0.9:7002\r\n")
32498        );
32499    }
32500
32501    /// Some here and some gone is nobody's command to run, and the client is
32502    /// told to come back rather than being given half an answer.
32503    #[test]
32504    fn a_half_moved_slot_is_a_try_again() {
32505        let mut f = clustered();
32506        f.run(&[b"SET", b"{t}here", b"1"]);
32507        let slot = cluster::key_slot(b"{t}here");
32508        f.server.cluster_moving(slot, Some(1), None);
32509        assert_eq!(
32510            f.run(&[b"MGET", b"{t}here", b"{t}gone"]),
32511            "-TRYAGAIN Multiple keys request during rehashing of slot\r\n"
32512        );
32513    }
32514
32515    /// A slot coming in is refused until the connection says `ASKING`, and the
32516    /// permission lasts exactly one command.
32517    #[test]
32518    fn asking_lets_one_command_into_an_importing_slot() {
32519        let mut f = clustered();
32520        f.server.cluster_hand_over(FOO, 1);
32521        f.server.cluster_moving(FOO, None, Some(1));
32522        let moved = format!("-MOVED {FOO} 10.0.0.9:7002\r\n");
32523        assert_eq!(f.run(&[b"GET", b"foo"]), moved);
32524        assert_eq!(f.run(&[b"ASKING"]), "+OK\r\n");
32525        assert_eq!(f.run(&[b"SET", b"foo", b"1"]), "+OK\r\n");
32526        // And it is spent, so the next one is a redirection again.
32527        assert_eq!(f.run(&[b"GET", b"foo"]), moved);
32528    }
32529
32530    /// `RESTORE-ASKING` carries its own `ASKING`, which is the whole reason it
32531    /// exists: the node being sent a slot's keys does not own the slot yet, so a
32532    /// plain `RESTORE` would come back as a redirection to the node sending
32533    /// them and the migration would never get off the ground.
32534    #[test]
32535    fn restore_asking_gets_into_an_importing_slot_on_its_own() {
32536        let mut f = clustered();
32537        f.server.cluster_hand_over(FOO, 1);
32538        f.server.cluster_moving(FOO, None, Some(1));
32539        // The payload is whatever `DUMP` makes of a one byte string, taken from
32540        // this server so the footer is this server's.
32541        f.run(&[b"SET", b"scratch", b"1"]);
32542        let dumped = f.raw(&[b"DUMP", b"scratch"]);
32543        let payload =
32544            &dumped[dumped.iter().position(|b| *b == b'\n').unwrap() + 1..dumped.len() - 2];
32545        let payload = payload.to_vec();
32546        // The ordinary spelling is turned away.
32547        assert_eq!(
32548            f.run(&[b"RESTORE", b"foo", b"0", &payload]),
32549            format!("-MOVED {FOO} 10.0.0.9:7002\r\n")
32550        );
32551        // And the one migration uses is not.
32552        assert_eq!(
32553            f.run(&[b"RESTORE-ASKING", b"foo", b"0", &payload]),
32554            "+OK\r\n"
32555        );
32556        // It is not a connection wide permission either, so the next ordinary
32557        // command is redirected the same as before.
32558        assert_eq!(
32559            f.run(&[b"GET", b"foo"]),
32560            format!("-MOVED {FOO} 10.0.0.9:7002\r\n")
32561        );
32562    }
32563
32564    /// The end of a slot import takes an epoch above everybody else's, which is
32565    /// the only thing that makes the rest of the cluster stop pointing clients
32566    /// at the node the slot came from.
32567    #[test]
32568    fn closing_an_import_takes_a_higher_epoch() {
32569        let mut f = clustered();
32570        f.server.cluster_hand_over(FOO, 1);
32571        f.server.cluster_moving(FOO, None, Some(1));
32572        let me = f.run(&[b"CLUSTER", b"MYID"]);
32573        let me = me[me.find("\r\n").unwrap() + 2..me.len() - 2].to_owned();
32574        assert_eq!(
32575            f.run(&[
32576                b"CLUSTER",
32577                b"SETSLOT",
32578                FOO.to_string().as_bytes(),
32579                b"NODE",
32580                me.as_bytes()
32581            ]),
32582            "+OK\r\n"
32583        );
32584        // The epoch moved on its own, so a bump asked for now has nothing left
32585        // to outrank and says so.
32586        assert_eq!(f.run(&[b"CLUSTER", b"BUMPEPOCH"]), "+STILL 1\r\n");
32587        // And the slot is this node's with nothing left marked.
32588        assert_eq!(f.run(&[b"GET", b"foo"]), "$-1\r\n");
32589    }
32590
32591    /// And a slot handed over without an import behind it does not, because
32592    /// nothing has been taken off anybody and there is nothing to outrank.
32593    #[test]
32594    fn a_plain_hand_over_does_not_touch_the_epoch() {
32595        let mut f = clustered();
32596        let me = f.run(&[b"CLUSTER", b"MYID"]);
32597        let me = me[me.find("\r\n").unwrap() + 2..me.len() - 2].to_owned();
32598        assert_eq!(
32599            f.run(&[
32600                b"CLUSTER",
32601                b"SETSLOT",
32602                FOO.to_string().as_bytes(),
32603                b"NODE",
32604                me.as_bytes()
32605            ]),
32606            "+OK\r\n"
32607        );
32608        assert_eq!(
32609            f.run(&[b"CLUSTER", b"BUMPEPOCH"]),
32610            "+BUMPED 1\r\n",
32611            "the epoch was still zero, so this is the first thing to move it"
32612        );
32613    }
32614
32615    /// A slot is not handed to somebody else while this node still holds keys
32616    /// for it, because that would leave two nodes answering for the same data.
32617    #[test]
32618    fn a_slot_with_keys_in_it_is_not_handed_over() {
32619        let mut f = clustered();
32620        f.run(&[b"SET", b"foo", b"1"]);
32621        let them = b"5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f";
32622        assert_eq!(
32623            f.run(&[
32624                b"CLUSTER",
32625                b"SETSLOT",
32626                FOO.to_string().as_bytes(),
32627                b"NODE",
32628                them
32629            ]),
32630            format!(
32631                "-ERR Can't assign hashslot {FOO} to a different node while I still hold keys for this hash slot.\r\n"
32632            )
32633        );
32634        // With the key gone it goes through.
32635        f.run(&[b"DEL", b"foo"]);
32636        assert_eq!(
32637            f.run(&[
32638                b"CLUSTER",
32639                b"SETSLOT",
32640                FOO.to_string().as_bytes(),
32641                b"NODE",
32642                them
32643            ]),
32644            "+OK\r\n"
32645        );
32646        assert_eq!(
32647            f.run(&[b"GET", b"foo"]),
32648            format!("-MOVED {FOO} 10.0.0.9:7002\r\n")
32649        );
32650    }
32651
32652    /// The slot migration protocol is shut to anybody who is not a node, and the
32653    /// connection goes with the refusal.
32654    ///
32655    /// The hang up is the reference's and it is the part worth having. Nothing
32656    /// behind this command checks that it is being driven in order, because the
32657    /// only thing that ever drives it is another node following the same state
32658    /// machine, so the whole defence is getting in at all and making a guess cost
32659    /// a fresh connection is most of that defence.
32660    #[test]
32661    fn the_slot_migration_protocol_is_shut_to_a_client() {
32662        let mut f = clustered();
32663        // The arity is read first, so a client that sends the container on its own
32664        // is told that much and keeps its connection.
32665        let (flow, reply) = f.flow(&[b"CLUSTER", b"SYNCSLOTS"]);
32666        assert_eq!(
32667            reply,
32668            "-ERR wrong number of arguments for 'cluster|syncslots' command\r\n"
32669        );
32670        assert_eq!(flow, Flow::Continue);
32671        let (flow, reply) = f.flow(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]);
32672        assert_eq!(
32673            reply,
32674            "-ERR CLUSTER SYNCSLOTS subcommands are only allowed for internal clients\r\n"
32675        );
32676        assert_eq!(flow, Flow::Close, "and the socket goes with it");
32677    }
32678
32679    /// The one way in is the secret the whole cluster has agreed on, and there is
32680    /// no secret at all on a server that is not in a cluster.
32681    #[test]
32682    fn the_internal_login_wants_the_cluster_secret() {
32683        let mut f = Fixture::new();
32684        assert_eq!(
32685            f.run(&[b"AUTH", b"internal connection", b"x"]),
32686            "-ERR Cannot authenticate as an internal connection on non-cluster instances\r\n"
32687        );
32688        assert_eq!(
32689            f.run(&[b"DEBUG", b"INTERNAL_SECRET"]),
32690            "-ERR Internal secret is missing\r\n"
32691        );
32692        let mut f = clustered();
32693        assert_eq!(
32694            f.run(&[b"AUTH", b"internal connection", b"x"]),
32695            "-WRONGPASS invalid internal password\r\n"
32696        );
32697        // The name is matched exactly and not the way a keyword is, so this is a
32698        // failed login as a user of that name rather than a failed internal one.
32699        assert_eq!(
32700            f.run(&[b"AUTH", b"INTERNAL CONNECTION", b"x"]),
32701            "-WRONGPASS invalid username-password pair or user is disabled.\r\n"
32702        );
32703        let secret = f.server.cluster_secret();
32704        assert_eq!(secret.len(), 40, "forty characters, like a node id");
32705        assert_eq!(
32706            f.run(&[b"DEBUG", b"INTERNAL_SECRET"]),
32707            format!(":{}\r\n", yo_common::crc::crc16(secret.as_bytes())),
32708            "what comes back is a checksum, so a test can see two nodes agree \
32709             and nobody can log in with what they read"
32710        );
32711        assert_eq!(
32712            f.run(&[b"AUTH", b"internal connection", secret.as_bytes()]),
32713            "+OK\r\n"
32714        );
32715        assert_eq!(
32716            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]),
32717            "+OK\r\n"
32718        );
32719    }
32720
32721    /// `CONF` carries on past an option it did not understand and still says
32722    /// `OK`, so one command can answer with two replies.
32723    #[test]
32724    fn conf_says_ok_after_an_option_it_did_not_know() {
32725        let mut f = clustered();
32726        assert_eq!(f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]), "+OK\r\n");
32727        assert_eq!(
32728            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"zzz", b"1"]),
32729            "-ERR Unknown option zzz\r\n+OK\r\n"
32730        );
32731        // A capability nobody here has heard of is not an unknown option, which
32732        // is what lets a newer node say something to an older one.
32733        assert_eq!(
32734            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"quantum"]),
32735            "+OK\r\n"
32736        );
32737        // The node saying who it is has to be a node this one knows.
32738        assert_eq!(
32739            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"node-id", b"abc"]),
32740            "-ERR Invalid node id length 3\r\n"
32741        );
32742        let unknown = b"1111111111111111111111111111111111111111";
32743        assert_eq!(
32744            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"node-id", unknown]),
32745            "-ERR Node 1111111111111111111111111111111111111111 not found in cluster\r\n"
32746        );
32747        assert_eq!(
32748            f.run(&[
32749                b"CLUSTER",
32750                b"SYNCSLOTS",
32751                b"CONF",
32752                b"node-id",
32753                b"5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f"
32754            ]),
32755            "+OK\r\n"
32756        );
32757        // The size hint is three numbers and the first of them is a slot.
32758        assert_eq!(
32759            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"slot-info", b"5:10:2"]),
32760            "+OK\r\n"
32761        );
32762        for bad in [b"zz".as_slice(), b"16384:0:0", b"5:10:2:3", b"5:-1:0"] {
32763            assert_eq!(
32764                f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"slot-info", bad]),
32765                format!(
32766                    "-ERR Invalid slot info: {}\r\n",
32767                    String::from_utf8_lossy(bad)
32768                )
32769            );
32770        }
32771        // And a master has no business being told what its own migration looks
32772        // like, since it is the one running it.
32773        assert_eq!(
32774            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"asm-task", b"x"]),
32775            "-ERR CLUSTER SYNCSLOTS CONF ASM-TASK only allowed on replica\r\n"
32776        );
32777        assert_eq!(
32778            f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT", b"UNMARK"]),
32779            "+OK\r\n"
32780        );
32781        let (flow, _) = f.flow(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]);
32782        assert_eq!(flow, Flow::Close, "and the door shuts again");
32783    }
32784
32785    /// The slot ranges are checked in full before anything is asked to move, and
32786    /// the answers name what is wrong with them.
32787    #[test]
32788    fn the_slot_ranges_of_a_sync_are_checked_in_full() {
32789        let mut f = clustered();
32790        f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]);
32791        let id = b"5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f";
32792        fn sync<'a>(id: &'a [u8], slots: &[&'a [u8]]) -> Vec<&'a [u8]> {
32793            let mut parts: Vec<&[u8]> = vec![b"CLUSTER", b"SYNCSLOTS", b"SYNC", id];
32794            parts.extend_from_slice(slots);
32795            parts
32796        }
32797        let bar = BAR.to_string();
32798        let bar = bar.as_bytes();
32799        assert_eq!(
32800            f.run(&sync(id, &[b"5", b"4"])),
32801            "-ERR start slot number 5 is greater than end slot number 4\r\n"
32802        );
32803        assert_eq!(
32804            f.run(&sync(id, &[b"99999", b"2"])),
32805            "-ERR Invalid or out of range slot\r\n"
32806        );
32807        // Ranges that touch are joined up and ranges that overlap are not, so
32808        // this is one slot asked for twice and the one below is a run of four.
32809        assert_eq!(
32810            f.run(&sync(id, &[b"1", b"2", b"2", b"3"])),
32811            "-ERR Slot 2 specified multiple times\r\n"
32812        );
32813        assert_eq!(
32814            f.run(&sync(id, &[b"1", b"2", b"3", b"4"])),
32815            "-ERR CLUSTER SYNCSLOTS SYNC is not implemented yet, move the slot with SETSLOT and MIGRATE\r\n"
32816        );
32817        // A slot somebody else owns is not this node's to send.
32818        f.server.cluster_hand_over(BAR, 1);
32819        assert_eq!(
32820            f.run(&sync(id, &[bar])),
32821            "-ERR syntax error\r\n",
32822            "one slot number is not a range, and a shape it does not know is a \
32823             syntax error rather than a count it can complain about"
32824        );
32825        assert_eq!(
32826            f.run(&sync(id, &[bar, bar])),
32827            "-ERR This node is not the owner of the slots\r\n"
32828        );
32829        // And neither way of moving a slot runs while the other one is half done.
32830        f.server.cluster_moving(FOO, Some(1), None);
32831        assert_eq!(
32832            f.run(&sync(id, &[b"1", b"2"])),
32833            "-ERR all slot states must be STABLE to start a slot migration task.\r\n"
32834        );
32835    }
32836
32837    /// A replica takes one thing off its master and nothing at all off anybody
32838    /// else, because there is nothing it could be being asked to hand over.
32839    #[test]
32840    fn a_replica_only_hears_the_settings_and_only_from_its_master() {
32841        let mut server = Server::new();
32842        server.enable_cluster("", 7000);
32843        let of = server.cluster_pretend_node(
32844            "5b1e2ce29b1e0c86bd53ee1e5b0dd7b66c0e6e0f",
32845            "10.0.0.9",
32846            7002,
32847        );
32848        server.cluster_pretend_follower(of);
32849        let mut f = Fixture::on(server);
32850        f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]);
32851        let (flow, reply) = f.flow(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]);
32852        assert_eq!(
32853            reply,
32854            "-ERR CLUSTER SYNCSLOTS subcommands are only allowed for master\r\n"
32855        );
32856        assert_eq!(flow, Flow::Close);
32857        // Off the master's own stream the settings go through, and anything else
32858        // is dropped without a word rather than refused, because an error written
32859        // into the replication stream is an error nobody reads.
32860        f.session.serve_master(true);
32861        assert_eq!(
32862            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"capa", b"x"]),
32863            "+OK\r\n"
32864        );
32865        assert_eq!(f.run(&[b"CLUSTER", b"SYNCSLOTS", b"SNAPSHOT-EOF"]), "");
32866        assert_eq!(
32867            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"CONF", b"asm-task", b"x"]),
32868            "-ERR Failed to handle master task: x\r\n+OK\r\n",
32869            "there is no migration for a replica to follow along with yet, and \
32870             this option is one the reference keeps going past as well"
32871        );
32872    }
32873
32874    /// The arms that answer nothing at all, which is how the far side of a
32875    /// migration says something it does not expect a reply to.
32876    #[test]
32877    fn the_one_way_arms_of_the_protocol_say_nothing_back() {
32878        let mut f = clustered();
32879        f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]);
32880        assert_eq!(f.run(&[b"CLUSTER", b"SYNCSLOTS", b"ACK", b"x", b"1"]), "");
32881        assert_eq!(f.run(&[b"CLUSTER", b"SYNCSLOTS", b"FAIL", b"boom"]), "");
32882        // The two that say a transfer has ended do the same and drop the
32883        // connection, since there is no transfer here for them to be about.
32884        let (flow, reply) = f.flow(&[b"CLUSTER", b"SYNCSLOTS", b"STREAM-EOF"]);
32885        assert_eq!(reply, "");
32886        assert_eq!(flow, Flow::Close);
32887        // And the one arm that has a real answer on a node with nothing running.
32888        let mut f = clustered();
32889        f.run(&[b"DEBUG", b"MARK-INTERNAL-CLIENT"]);
32890        assert_eq!(
32891            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"RDBCHANNEL", b"abc"]),
32892            "-ERR Invalid task id\r\n"
32893        );
32894        assert_eq!(
32895            f.run(&[
32896                b"CLUSTER",
32897                b"SYNCSLOTS",
32898                b"RDBCHANNEL",
32899                b"0000000000000000000000000000000000000000"
32900            ]),
32901            "-ERR No slot migration task in progress\r\n"
32902        );
32903        assert_eq!(
32904            f.run(&[b"CLUSTER", b"SYNCSLOTS", b"NONSENSE"]),
32905            "-ERR syntax error\r\n"
32906        );
32907    }
32908
32909    /// A transaction is refused at queue time rather than at `EXEC`, so a client
32910    /// finds out about the redirection while it can still do something about it.
32911    #[test]
32912    fn a_transaction_is_refused_when_it_is_queued() {
32913        let mut f = clustered();
32914        f.server.cluster_hand_over(BAR, 1);
32915        assert_eq!(f.run(&[b"MULTI"]), "+OK\r\n");
32916        assert_eq!(
32917            f.run(&[b"GET", b"bar"]),
32918            format!("-MOVED {BAR} 10.0.0.9:7002\r\n")
32919        );
32920        assert_eq!(
32921            f.run(&[b"EXEC"]),
32922            "-EXECABORT Transaction discarded because of previous errors.\r\n"
32923        );
32924    }
32925
32926    /// The two commands a cluster refuses outright, because there is only one
32927    /// database in a cluster and nothing to swap it with.
32928    #[test]
32929    fn select_and_swapdb_are_not_cluster_commands() {
32930        let mut f = clustered();
32931        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
32932        assert_eq!(
32933            f.run(&[b"SELECT", b"1"]),
32934            "-ERR SELECT is not allowed in cluster mode\r\n"
32935        );
32936        assert_eq!(
32937            f.run(&[b"SWAPDB", b"0", b"1"]),
32938            "-ERR SWAPDB is not allowed in cluster mode\r\n"
32939        );
32940    }
32941
32942    /// Everything in the container is refused on a server that was not started
32943    /// as a cluster node, and so are the three connection commands.
32944    #[test]
32945    fn a_plain_server_has_no_cluster_in_it() {
32946        let mut f = Fixture::new();
32947        for argv in [
32948            &[b"CLUSTER".as_slice(), b"INFO".as_slice()][..],
32949            &[b"CLUSTER", b"MYID"],
32950            &[b"CLUSTER", b"SLOTS"],
32951            &[b"CLUSTER", b"HELP"],
32952            &[b"ASKING"],
32953            &[b"READONLY"],
32954            &[b"READWRITE"],
32955        ] {
32956            assert_eq!(
32957                f.run(argv),
32958                "-ERR This instance has cluster support disabled\r\n",
32959                "{argv:?}"
32960            );
32961        }
32962        // The arity is still checked in front of the refusal.
32963        assert_eq!(
32964            f.run(&[b"CLUSTER", b"KEYSLOT"]),
32965            "-ERR wrong number of arguments for 'cluster|keyslot' command\r\n"
32966        );
32967    }
32968}